diff --git a/README.md b/README.md index ac5530a..09108af 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Changemaker Lite is a streamlined documentation and development platform featuri - **PostgreSQL**: Reliable database backend - **n8n**: Workflow automation and service integration - **NocoDB**: No-code database platform and smart spreadsheet interface -- **NocoDB Map Viewer**: Interactive map visualization for geographic data with real-time geolocation, walk sheet generation, and QR code integration +- **Map**: Interactive map visualization for geographic data with real-time geolocation, walk sheet generation, and QR code integration ## Quick Start diff --git a/configs/mkdocs-site/default.conf b/configs/mkdocs-site/default.conf index 7fd9f83..a118e71 100644 --- a/configs/mkdocs-site/default.conf +++ b/configs/mkdocs-site/default.conf @@ -5,6 +5,14 @@ server { root /config/www; index index.html; + # CRITICAL: Include MIME types + include /etc/nginx/mime.types; + + # Handle trailing slashes for directories + location ~ ^([^.]*[^/])$ { + try_files $uri $uri/ $uri.html =404; + } + # CORS configuration for search index location /search/search_index.json { add_header 'Access-Control-Allow-Origin' '*' always; @@ -16,9 +24,26 @@ server { } } - # General CORS for all requests (optional) + # Main location block location / { add_header 'Access-Control-Allow-Origin' '*' always; - try_files $uri $uri/ =404; + try_files $uri $uri/ $uri/index.html $uri.html /index.html =404; } + + # Handle 404 with MkDocs 404 page + error_page 404 /404.html; + location = /404.html { + internal; + } + + # Static assets + location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { + add_header 'Access-Control-Allow-Origin' '*' always; + expires 30d; + add_header Cache-Control "public, immutable"; + } + + # Enable gzip + gzip on; + gzip_types text/plain text/css text/javascript application/javascript application/json; } \ No newline at end of file diff --git a/map/README.md b/map/README.md index 2f01973..3ea19f5 100644 --- a/map/README.md +++ b/map/README.md @@ -55,16 +55,32 @@ A containerized web application that visualizes geographic data from NocoDB on a NOCODB_SHIFTS_SHEET= NOCODB_SHIFT_SIGNUPS_SHEET= + # Domain Configuration + DOMAIN=cmlite.org + + # MkDocs Integration + MKDOCS_URL=https://cmlite.org + MKDOCS_SEARCH_URL=https://cmlite.org + MKDOCS_SITE_SERVER_PORT=4002 + # Server Configuration PORT=3000 NODE_ENV=production + + # Session Secret (Generate with: openssl rand -hex 32) SESSION_SECRET=your-secure-random-string - # Map Defaults (Edmonton, AB) + # Map Defaults (Edmonton, Alberta, Canada) DEFAULT_LAT=53.5461 DEFAULT_LNG=-113.4938 DEFAULT_ZOOM=11 + # Optional: Map Boundaries (prevents users from adding points outside area) + # BOUND_NORTH=53.7 + # BOUND_SOUTH=53.4 + # BOUND_EAST=-113.3 + # BOUND_WEST=-113.7 + # Cloudflare Settings TRUST_PROXY=true COOKIE_DOMAIN=.cmlite.org @@ -131,53 +147,73 @@ A containerized web application that visualizes geographic data from NocoDB on a The build script automatically creates the following table structure: ### Main Locations Table -- `Geo-Location` (Geo-Data): Format "latitude;longitude" -- `latitude` (Decimal): Precision 10, Scale 8 -- `longitude` (Decimal): Precision 11, Scale 8 +- `ID` (ID): Auto-incrementing primary key +- `Geo-Location` (GeoData): Geographic coordinate data +- `latitude` (Decimal): Precision 8, Scale 8 +- `longitude` (Decimal): Precision 8, Scale 8 - `First Name` (Single Line Text): Person's first name - `Last Name` (Single Line Text): Person's last name - `Email` (Email): Email address -- `Phone` (Single Line Text): Phone number +- `Phone` (PhoneNumber): Phone number with validation - `Unit Number` (Single Line Text): Unit or apartment number -- `Support Level` (Single Select): Options: "1", "2", "3", "4" (1=Strong Support/Green, 2=Moderate Support/Yellow, 3=Low Support/Orange, 4=No Support/Red) +- `Support Level` (Single Select): Options: "1" (Green), "2" (Yellow), "3" (Orange), "4" (Red) - `Address` (Single Line Text): Street address - `Sign` (Checkbox): Has campaign sign -- `Sign Size` (Single Select): Options: "Regular", "Large", "Unsure" +- `Sign Size` (Single Select): Options: "Regular" (Blue), "Large" (Green), "Unsure" (Orange) - `Notes` (Long Text): Additional details and comments -- `title` (Text): Location name (legacy field) -- `category` (Single Select): Classification (legacy field) +- `created_by_user` (Single Line Text): Creator email +- `last_updated_by_user` (Single Line Text): Last updater email ### Login Table -- `Email` (Email): User email address +- `ID` (ID): Auto-incrementing primary key +- `Email` (Email): User email address (required) +- `Password` (Single Line Text): User password (required) - `Name` (Single Line Text): User display name - `Admin` (Checkbox): Admin privileges +- `Created At` (DateTime): Account creation timestamp +- `Last Login` (DateTime): Last login timestamp ### Settings Table -- `key` (Single Line Text): Setting identifier -- `title` (Single Line Text): Display name -- `value` (Long Text): Setting value -- `Geo-Location` (Text): Format "latitude;longitude" -- `latitude` (Decimal): Precision 10, Scale 8 -- `longitude` (Decimal): Precision 11, Scale 8 +- `ID` (ID): Auto-incrementing primary key +- `created_at` (DateTime): Record creation timestamp +- `created_by` (Single Line Text): Creator identifier +- `Geo-Location` (Single Line Text): Format "latitude;longitude" +- `latitude` (Decimal): Precision 8, Scale 8 +- `longitude` (Decimal): Precision 8, Scale 8 - `zoom` (Number): Map zoom level -- `category` (Single Select): Setting category -- `updated_by` (Single Line Text): Last updater email -- `updated_at` (DateTime): Last update time -- `qr_code_1_image` (Attachment): QR code 1 image -- `qr_code_2_image` (Attachment): QR code 2 image -- `qr_code_3_image` (Attachment): QR code 3 image +- `Walk Sheet Title` (Single Line Text): Title for walk sheets +- `Walk Sheet Subtitle` (Single Line Text): Subtitle for walk sheets +- `Walk Sheet Footer` (Long Text): Footer text for walk sheets +- `QR Code 1 URL` (URL): First QR code link +- `QR Code 1 Label` (Single Line Text): First QR code label +- `QR Code 2 URL` (URL): Second QR code link +- `QR Code 2 Label` (Single Line Text): Second QR code label +- `QR Code 3 URL` (URL): Third QR code link +- `QR Code 3 Label` (Single Line Text): Third QR code label ### Shifts Table -- Standard NocoDB fields for shift scheduling and management -- Contains shift dates, times, locations, capacity limits -- Status tracking (Active, Cancelled, Full) -- Created automatically by build script with basic structure +- `ID` (ID): Auto-incrementing primary key +- `Title` (Single Line Text): Shift title (required) +- `Description` (Long Text): Detailed shift description +- `Date` (Date): Shift date (required) +- `Start Time` (Time): Shift start time (required) +- `End Time` (Time): Shift end time (required) +- `Location` (Single Line Text): Shift location +- `Max Volunteers` (Number): Maximum volunteer capacity (required) +- `Current Volunteers` (Number): Current volunteer count +- `Status` (Single Select): Options: "Open" (Green), "Full" (Orange), "Cancelled" (Red) +- `Created By` (Single Line Text): Creator identifier +- `Created At` (DateTime): Creation timestamp +- `Updated At` (DateTime): Last update timestamp ### Shift Signups Table -- Links users to shifts they've signed up for -- Tracks signup timestamps and user information -- Handles cancellations and waitlist management -- Created automatically by build script with basic structure +- `ID` (ID): Auto-incrementing primary key +- `Shift ID` (Number): Reference to shifts table (required) +- `Shift Title` (Single Line Text): Copy of shift title for reference +- `User Email` (Email): User's email address (required) +- `User Name` (Single Line Text): User's display name +- `Signup Date` (DateTime): When user signed up +- `Status` (Single Select): Options: "Confirmed" (Green), "Cancelled" (Red) ## API Endpoints @@ -316,15 +352,23 @@ All configuration is done via environment variables: | `NOCODB_SETTINGS_SHEET` | Settings table URL for admin config | Required | | `NOCODB_SHIFTS_SHEET` | Shifts table URL for shift management | Required | | `NOCODB_SHIFT_SIGNUPS_SHEET` | Shift signups table URL for user registrations | Required | +| `DOMAIN` | Primary domain for the application | Required | +| `MKDOCS_URL` | MkDocs documentation site URL | Optional | +| `MKDOCS_SEARCH_URL` | MkDocs search endpoint URL | Optional | +| `MKDOCS_SITE_SERVER_PORT` | Port for MkDocs integration | 4002 | | `PORT` | Server port | 3000 | | `NODE_ENV` | Environment mode | production | -| `SESSION_SECRET` | Session encryption secret | Required | +| `SESSION_SECRET` | Session encryption secret (generate with openssl rand -hex 32) | Required | | `DEFAULT_LAT` | Default map latitude | 53.5461 | | `DEFAULT_LNG` | Default map longitude | -113.4938 | | `DEFAULT_ZOOM` | Default map zoom level | 11 | +| `BOUND_NORTH` | Northern boundary for map points (optional) | None | +| `BOUND_SOUTH` | Southern boundary for map points (optional) | None | +| `BOUND_EAST` | Eastern boundary for map points (optional) | None | +| `BOUND_WEST` | Western boundary for map points (optional) | None | | `TRUST_PROXY` | Trust proxy headers (for Cloudflare) | true | | `COOKIE_DOMAIN` | Cookie domain setting | .cmlite.org | -| `ALLOWED_ORIGINS` | CORS allowed origins | Multiple URLs | +| `ALLOWED_ORIGINS` | CORS allowed origins (comma-separated) | Multiple URLs | ## Maintenance Commands @@ -375,7 +419,7 @@ To run in development mode: ### Locations not showing -- Verify table has `geodata`, `latitude`, and `longitude` columns +- Verify table has `Geo-Location`, `latitude`, and `longitude` columns - Check that coordinates are valid numbers - Ensure API token has read permissions diff --git a/map/app/public/css/shifts.css b/map/app/public/css/shifts.css index 8260fbd..498a362 100644 --- a/map/app/public/css/shifts.css +++ b/map/app/public/css/shifts.css @@ -209,6 +209,7 @@ .calendar-dropdown { position: relative; display: inline-block; + z-index: 100; /* Lower than popup but above other content */ } .calendar-toggle { @@ -227,6 +228,7 @@ box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; margin-top: 4px; + display: none; /* Hidden by default */ } .calendar-option { @@ -428,13 +430,13 @@ /* Calendar shift popup/tooltip */ .shift-popup { - position: absolute; + position: fixed; /* Changed from absolute to fixed */ background: white; border: 1px solid #e0e0e0; border-radius: var(--border-radius); padding: 15px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); - z-index: 1000; + z-index: 10000; /* High z-index to appear above header */ min-width: 250px; max-width: 300px; } @@ -455,6 +457,153 @@ gap: 10px; } +/* Custom Confirmation Modal */ +.confirm-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 20000; /* Even higher than shift popup */ + display: flex; + align-items: center; + justify-content: center; +} + +.confirm-modal-backdrop { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + animation: fadeInBackdrop 0.2s ease-out; +} + +.confirm-modal-content { + background: white; + border-radius: var(--border-radius); + box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); + max-width: 400px; + width: 100%; + padding: 30px; + text-align: center; + animation: slideInModal 0.3s ease-out; + position: relative; +} + +.confirm-modal-content h3 { + margin: 0 0 15px 0; + color: var(--dark-color); + font-size: 1.2em; +} + +.confirm-modal-content p { + margin: 0 0 25px 0; + color: var(--secondary-color); + line-height: 1.5; +} + +.confirm-modal-actions { + display: flex; + gap: 15px; + justify-content: center; +} + +.confirm-modal-actions .btn { + padding: 10px 20px; + font-weight: 500; + min-width: 100px; +} + +/* Animations */ +@keyframes fadeInBackdrop { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes slideInModal { + from { + opacity: 0; + transform: translate(-50%, -60%) scale(0.9); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +/* Position the modal content in the center */ +.confirm-modal-content { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +/* Mobile adjustments for confirm modal */ +@media (max-width: 768px) { + .confirm-modal-backdrop { + padding: 15px; + } + + .confirm-modal-content { + padding: 25px 20px; + max-width: 90%; + } + + .confirm-modal-content h3 { + font-size: 1.1em; + } + + .confirm-modal-actions { + flex-direction: column; + gap: 10px; + } + + .confirm-modal-actions .btn { + width: 100%; + min-width: auto; + } +} + +@media (max-width: 480px) { + .confirm-modal-content { + padding: 20px 15px; + } + + .confirm-modal-content h3 { + font-size: 1em; + margin-bottom: 12px; + } + + .confirm-modal-content p { + font-size: 0.9em; + margin-bottom: 20px; + } +} + +/* Fix for mobile popup positioning */ +@media (max-width: 768px) { + .shift-popup { + max-width: 90%; + left: 5% !important; + right: 5% !important; + width: auto; + position: fixed; + top: 50% !important; + transform: translateY(-50%); + } +} + /* Mobile adjustments */ @media (max-width: 768px) { /* Ensure proper scrolling on mobile */ diff --git a/map/app/public/js/shifts.js b/map/app/public/js/shifts.js index ffaeb2e..93cd0e0 100644 --- a/map/app/public/js/shifts.js +++ b/map/app/public/js/shifts.js @@ -101,17 +101,17 @@ function displayShifts(shifts) { const grid = document.getElementById('shifts-grid'); if (shifts.length === 0) { - grid.innerHTML = '

No shifts available at this time.

'; + grid.innerHTML = '
No shifts available for the selected criteria.
'; return; } grid.innerHTML = shifts.map(shift => { - const shiftDate = new Date(shift.Date); - const isSignedUp = mySignups.some(s => s.shift_id === shift.ID); + const isSignedUp = mySignups.some(signup => signup.shift_id === shift.ID); const isFull = shift['Current Volunteers'] >= shift['Max Volunteers']; + const shiftDate = new Date(shift.Date); return ` -
+

${escapeHtml(shift.Title)}

📅 ${shiftDate.toLocaleDateString()}

@@ -119,7 +119,7 @@ function displayShifts(shifts) {

📍 ${escapeHtml(shift.Location || 'TBD')}

👥 ${shift['Current Volunteers']}/${shift['Max Volunteers']} volunteers

- ${shift.Description ? `
${escapeHtml(shift.Description)}
` : ''} + ${shift.Description ? `

${escapeHtml(shift.Description)}

` : ''}
${isSignedUp ? ` @@ -133,7 +133,7 @@ function displayShifts(shifts) { `; }).join(''); - // Add event listeners after rendering + // Set up event listeners using delegation setupShiftCardListeners(); // Update calendar view if it's currently active @@ -146,19 +146,14 @@ function displayMySignups() { const list = document.getElementById('my-signups-list'); if (mySignups.length === 0) { - list.innerHTML = '

You haven\'t signed up for any shifts yet.

'; + list.innerHTML = '

You haven\'t signed up for any shifts yet.

'; return; } // Need to match signups with shift details for date/time info const signupsWithDetails = mySignups.map(signup => { const shift = allShifts.find(s => s.ID === signup.shift_id); - return { - ...signup, - shift, - // Use title from signup record if available, otherwise from shift - displayTitle: signup.shift_title || (shift ? shift.Title : 'Unknown Shift') - }; + return { ...signup, shift }; }).filter(s => s.shift); // Only show signups where we can find the shift details list.innerHTML = signupsWithDetails.map(signup => { @@ -166,18 +161,19 @@ function displayMySignups() { return ` `; }).join(''); - // Add event listeners after rendering + // Set up event listeners using delegation setupMySignupsListeners(); // Update calendar view if it's currently active @@ -191,45 +187,55 @@ function setupShiftCardListeners() { const grid = document.getElementById('shifts-grid'); if (!grid) return; - // Remove any existing listeners by cloning - const newGrid = grid.cloneNode(true); - grid.parentNode.replaceChild(newGrid, grid); + // Use event delegation on the grid itself, not cloning + grid.removeEventListener('click', handleShiftCardClick); // Remove if exists + grid.addEventListener('click', handleShiftCardClick); +} + +// Create a separate handler function +function handleShiftCardClick(e) { + const target = e.target; - // Add click listener for all buttons - newGrid.addEventListener('click', async (e) => { - // Handle signup buttons - if (e.target.classList.contains('signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await signupForShift(shiftId); - } - // Handle cancel buttons - else if (e.target.classList.contains('cancel-signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await cancelSignup(shiftId); - } - // Handle calendar toggle buttons - else if (e.target.classList.contains('calendar-toggle')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - const isOpen = options.style.display !== 'none'; - - // Close all other dropdowns - document.querySelectorAll('.calendar-options').forEach(opt => { - opt.style.display = 'none'; - }); - - // Toggle this dropdown - options.style.display = isOpen ? 'none' : 'block'; - } - // Handle calendar option clicks - else if (e.target.closest('.calendar-option')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - options.style.display = 'none'; - } - }); + // Handle signup button + if (target.classList.contains('signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) signupForShift(shiftId); + return; + } + + // Handle cancel button + if (target.classList.contains('cancel-signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) cancelSignup(shiftId); + return; + } + + // Handle calendar toggle + if (target.classList.contains('calendar-toggle')) { + e.preventDefault(); + e.stopPropagation(); + const dropdown = target.nextElementSibling; + + // Close all other dropdowns + document.querySelectorAll('.calendar-options').forEach(opt => { + if (opt !== dropdown) opt.style.display = 'none'; + }); + + // Toggle this dropdown + dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; + return; + } + + // Handle calendar option clicks + if (target.classList.contains('calendar-option')) { + e.stopPropagation(); + // Let the link work naturally + return; + } } // New function to setup listeners for my signups @@ -237,146 +243,42 @@ function setupMySignupsListeners() { const list = document.getElementById('my-signups-list'); if (!list) return; - // Remove any existing listeners by cloning - const newList = list.cloneNode(true); - list.parentNode.replaceChild(newList, list); + // Use event delegation + list.removeEventListener('click', handleMySignupsClick); // Remove if exists + list.addEventListener('click', handleMySignupsClick); +} + +// Create a separate handler for my signups +function handleMySignupsClick(e) { + const target = e.target; - // Add click listener for all interactions - newList.addEventListener('click', async (e) => { - // Handle cancel buttons - if (e.target.classList.contains('cancel-signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await cancelSignup(shiftId); - } - // Handle calendar toggle buttons - else if (e.target.classList.contains('calendar-toggle')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - const isOpen = options.style.display !== 'none'; - - // Close all other dropdowns - document.querySelectorAll('.calendar-options').forEach(opt => { - opt.style.display = 'none'; - }); - - // Toggle this dropdown - options.style.display = isOpen ? 'none' : 'block'; - } - // Handle calendar option clicks - else if (e.target.closest('.calendar-option')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - options.style.display = 'none'; - } - }); -} - -async function signupForShift(shiftId) { - try { - const response = await fetch(`/api/shifts/${shiftId}/signup`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } - }); - - const data = await response.json(); - - if (data.success) { - showStatus('Successfully signed up for shift!', 'success'); - await loadShifts(); - await loadMySignups(); - } else { - showStatus(data.error || 'Failed to sign up', 'error'); - } - } catch (error) { - console.error('Error signing up:', error); - showStatus('Failed to sign up for shift', 'error'); - } -} - -async function cancelSignup(shiftId) { - if (!confirm('Are you sure you want to cancel your signup for this shift?')) { + // Handle cancel button + if (target.classList.contains('cancel-signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) cancelSignup(shiftId); return; } - try { - const response = await fetch(`/api/shifts/${shiftId}/cancel`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - } + // Handle calendar toggle + if (target.classList.contains('calendar-toggle')) { + e.preventDefault(); + e.stopPropagation(); + const dropdown = target.nextElementSibling; + + // Close all other dropdowns + document.querySelectorAll('.calendar-options').forEach(opt => { + if (opt !== dropdown) opt.style.display = 'none'; }); - const data = await response.json(); - - if (data.success) { - showStatus('Signup cancelled', 'success'); - await loadShifts(); - await loadMySignups(); - } else { - showStatus(data.error || 'Failed to cancel signup', 'error'); - } - } catch (error) { - console.error('Error cancelling signup:', error); - showStatus('Failed to cancel signup', 'error'); - } -} - -function setupEventListeners() { - const dateFilter = document.getElementById('date-filter'); - if (dateFilter) { - dateFilter.addEventListener('change', filterShifts); - } -} - -function filterShifts() { - const dateFilter = document.getElementById('date-filter').value; - - if (!dateFilter) { - displayShifts(allShifts); + // Toggle this dropdown + dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; return; } - - const filtered = allShifts.filter(shift => { - return shift.Date === dateFilter; // Changed from shift.date to shift.Date - }); - - displayShifts(filtered); } -function clearFilters() { - document.getElementById('date-filter').value = ''; - loadShifts(); // Reload shifts without filters -} - -function showStatus(message, type = 'info') { - const container = document.getElementById('status-container'); - if (!container) return; - - const messageDiv = document.createElement('div'); - messageDiv.className = `status-message ${type}`; - messageDiv.textContent = message; - - container.appendChild(messageDiv); - - setTimeout(() => { - messageDiv.remove(); - }, 5000); -} - -function escapeHtml(text) { - if (text === null || text === undefined) { - return ''; - } - const div = document.createElement('div'); - div.textContent = String(text); - return div.innerHTML; -} - -// Add these calendar URL generation functions after the existing functions +// New function to generate calendar URLs function generateCalendarUrls(shift) { const shiftDate = new Date(shift.Date); @@ -492,86 +394,466 @@ function setupShiftCardListeners() { const grid = document.getElementById('shifts-grid'); if (!grid) return; - // Remove any existing listeners by cloning - const newGrid = grid.cloneNode(true); - grid.parentNode.replaceChild(newGrid, grid); - - // Add click listener for all buttons - newGrid.addEventListener('click', async (e) => { - // Handle signup buttons - if (e.target.classList.contains('signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await signupForShift(shiftId); - } - // Handle cancel buttons - else if (e.target.classList.contains('cancel-signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await cancelSignup(shiftId); - } - // Handle calendar toggle buttons - else if (e.target.classList.contains('calendar-toggle')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - const isOpen = options.style.display !== 'none'; - - // Close all other dropdowns - document.querySelectorAll('.calendar-options').forEach(opt => { - opt.style.display = 'none'; - }); - - // Toggle this dropdown - options.style.display = isOpen ? 'none' : 'block'; - } - // Handle calendar option clicks - else if (e.target.closest('.calendar-option')) { - e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - options.style.display = 'none'; - } - }); + // Use event delegation on the grid itself, not cloning + grid.removeEventListener('click', handleShiftCardClick); // Remove if exists + grid.addEventListener('click', handleShiftCardClick); } -// Update setupMySignupsListeners similarly +// Create a separate handler function +function handleShiftCardClick(e) { + const target = e.target; + + // Handle signup button + if (target.classList.contains('signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) signupForShift(shiftId); + return; + } + + // Handle cancel button + if (target.classList.contains('cancel-signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) cancelSignup(shiftId); + return; + } + + // Handle calendar toggle + if (target.classList.contains('calendar-toggle')) { + e.preventDefault(); + e.stopPropagation(); + const dropdown = target.nextElementSibling; + + // Close all other dropdowns + document.querySelectorAll('.calendar-options').forEach(opt => { + if (opt !== dropdown) opt.style.display = 'none'; + }); + + // Toggle this dropdown + dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; + return; + } + + // Handle calendar option clicks + if (target.classList.contains('calendar-option')) { + e.stopPropagation(); + // Let the link work naturally + return; + } +} + +// Fix the setupMySignupsListeners function similarly function setupMySignupsListeners() { const list = document.getElementById('my-signups-list'); if (!list) return; - // Remove any existing listeners by cloning - const newList = list.cloneNode(true); - list.parentNode.replaceChild(newList, list); + // Use event delegation + list.removeEventListener('click', handleMySignupsClick); // Remove if exists + list.addEventListener('click', handleMySignupsClick); +} + +// Create a separate handler for my signups +function handleMySignupsClick(e) { + const target = e.target; - // Add click listener for all interactions - newList.addEventListener('click', async (e) => { - // Handle cancel buttons - if (e.target.classList.contains('cancel-signup-btn')) { - const shiftId = e.target.getAttribute('data-shift-id'); - await cancelSignup(shiftId); - } - // Handle calendar toggle buttons - else if (e.target.classList.contains('calendar-toggle')) { + // Handle cancel button + if (target.classList.contains('cancel-signup-btn')) { + e.preventDefault(); + e.stopPropagation(); + const shiftId = target.getAttribute('data-shift-id'); + if (shiftId) cancelSignup(shiftId); + return; + } + + // Handle calendar toggle + if (target.classList.contains('calendar-toggle')) { + e.preventDefault(); + e.stopPropagation(); + const dropdown = target.nextElementSibling; + + // Close all other dropdowns + document.querySelectorAll('.calendar-options').forEach(opt => { + if (opt !== dropdown) opt.style.display = 'none'; + }); + + // Toggle this dropdown + dropdown.style.display = dropdown.style.display === 'none' ? 'block' : 'none'; + return; + } +} + +// Update the displayShifts function to properly show calendar dropdowns +function displayShifts(shifts) { + const grid = document.getElementById('shifts-grid'); + + if (shifts.length === 0) { + grid.innerHTML = '
No shifts available for the selected criteria.
'; + return; + } + + grid.innerHTML = shifts.map(shift => { + const isSignedUp = mySignups.some(signup => signup.shift_id === shift.ID); + const isFull = shift['Current Volunteers'] >= shift['Max Volunteers']; + const shiftDate = new Date(shift.Date); + + return ` +
+

${escapeHtml(shift.Title)}

+
+

📅 ${shiftDate.toLocaleDateString()}

+

⏰ ${shift['Start Time']} - ${shift['End Time']}

+

📍 ${escapeHtml(shift.Location || 'TBD')}

+

👥 ${shift['Current Volunteers']}/${shift['Max Volunteers']} volunteers

+
+ ${shift.Description ? `

${escapeHtml(shift.Description)}

` : ''} +
+ ${isSignedUp + ? ` + ${generateCalendarDropdown(shift)}` + : isFull + ? '' + : `` + } +
+
+ `; + }).join(''); + + // Set up event listeners using delegation + setupShiftCardListeners(); + + // Update calendar view if it's currently active + if (currentView === 'calendar') { + renderCalendar(); + } +} + +// Update the displayMySignups function to always show calendar dropdowns +function displayMySignups() { + const list = document.getElementById('my-signups-list'); + + if (mySignups.length === 0) { + list.innerHTML = '

You haven\'t signed up for any shifts yet.

'; + return; + } + + // Need to match signups with shift details for date/time info + const signupsWithDetails = mySignups.map(signup => { + const shift = allShifts.find(s => s.ID === signup.shift_id); + return { ...signup, shift }; + }).filter(s => s.shift); // Only show signups where we can find the shift details + + list.innerHTML = signupsWithDetails.map(signup => { + const shiftDate = new Date(signup.shift.Date); + return ` + + `; + }).join(''); + + // Set up event listeners using delegation + setupMySignupsListeners(); + + // Update calendar view if it's currently active + if (currentView === 'calendar') { + renderCalendar(); + } +} + +// Add a global variable to track popup cleanup +let currentPopup = null; + +// Update the showShiftPopup function to handle z-index and cleanup +function showShiftPopup(shift, targetElement) { + // Remove any existing popup + if (currentPopup) { + currentPopup.remove(); + currentPopup = null; + } + + const existingPopup = document.querySelector('.shift-popup'); + if (existingPopup) { + existingPopup.remove(); + } + + const popup = document.createElement('div'); + popup.className = 'shift-popup'; + + const isSignedUp = mySignups.some(signup => signup.shift_id === shift.ID); + const isFull = shift['Current Volunteers'] >= shift['Max Volunteers']; + const shiftDate = new Date(shift.Date); + + popup.innerHTML = ` +

${escapeHtml(shift.Title)}

+

📅 ${shiftDate.toLocaleDateString()}

+

⏰ ${shift['Start Time']} - ${shift['End Time']}

+

📍 ${escapeHtml(shift.Location || 'TBD')}

+

👥 ${shift['Current Volunteers']}/${shift['Max Volunteers']} volunteers

+ ${shift.Description ? `

${escapeHtml(shift.Description)}

` : ''} +
+ ${isSignedUp + ? `` + : isFull + ? '' + : `` + } +
+ `; + + // Position popup + document.body.appendChild(popup); + currentPopup = popup; // Track the current popup + + const rect = targetElement.getBoundingClientRect(); + const popupRect = popup.getBoundingClientRect(); + + let left = rect.left + (rect.width / 2) - (popupRect.width / 2); + let top = rect.bottom + 10; + + // Adjust if popup goes off screen + if (left < 10) left = 10; + if (left + popupRect.width > window.innerWidth - 10) { + left = window.innerWidth - popupRect.width - 10; + } + if (top + popupRect.height > window.innerHeight - 10) { + top = rect.top - popupRect.height - 10; + } + + popup.style.left = `${left}px`; + popup.style.top = `${top}px`; + + // Add event listeners for buttons in popup + const signupBtn = popup.querySelector('.signup-btn'); + const cancelBtn = popup.querySelector('.cancel-signup-btn'); + + if (signupBtn) { + signupBtn.addEventListener('click', async (e) => { e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - const isOpen = options.style.display !== 'none'; - - // Close all other dropdowns - document.querySelectorAll('.calendar-options').forEach(opt => { - opt.style.display = 'none'; - }); - - // Toggle this dropdown - options.style.display = isOpen ? 'none' : 'block'; - } - // Handle calendar option clicks - else if (e.target.closest('.calendar-option')) { + await signupForShift(shift.ID); + popup.remove(); + currentPopup = null; + }); + } + + if (cancelBtn) { + cancelBtn.addEventListener('click', async (e) => { e.stopPropagation(); - const dropdown = e.target.closest('.calendar-dropdown'); - const options = dropdown.querySelector('.calendar-options'); - options.style.display = 'none'; + await cancelSignup(shift.ID); + popup.remove(); + currentPopup = null; + }); + } + + // Close popup when clicking outside + const closePopup = (e) => { + if (!popup.contains(e.target) && e.target !== targetElement) { + popup.remove(); + currentPopup = null; + document.removeEventListener('click', closePopup); + } + }; + + setTimeout(() => { + document.addEventListener('click', closePopup); + }, 100); +} + +// Close calendar dropdowns when clicking outside +document.addEventListener('click', function(e) { + // Don't close if clicking on a toggle or option + if (!e.target.classList.contains('calendar-toggle') && + !e.target.classList.contains('calendar-option') && + !e.target.closest('.calendar-dropdown')) { + document.querySelectorAll('.calendar-options').forEach(opt => { + opt.style.display = 'none'; + }); + } +}); + +async function signupForShift(shiftId) { + try { + const response = await fetch(`/api/shifts/${shiftId}/signup`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }); + + const data = await response.json(); + + if (data.success) { + showStatus('Successfully signed up for shift!', 'success'); + await loadShifts(); + await loadMySignups(); + } else { + showStatus(data.error || 'Failed to sign up', 'error'); + } + } catch (error) { + console.error('Error signing up:', error); + showStatus('Failed to sign up for shift', 'error'); + } +} + +// Add a custom confirmation modal function +function showConfirmModal(message, onConfirm, onCancel = null) { + // Remove any existing modal + const existingModal = document.querySelector('.confirm-modal'); + if (existingModal) { + existingModal.remove(); + } + + // Create modal + const modal = document.createElement('div'); + modal.className = 'confirm-modal'; + modal.innerHTML = ` +
+
+

Confirm Action

+

${message}

+
+ + +
+
+
+ `; + + document.body.appendChild(modal); + + // Add event listeners + const cancelBtn = modal.querySelector('.confirm-cancel'); + const confirmBtn = modal.querySelector('.confirm-ok'); + const backdrop = modal.querySelector('.confirm-modal-backdrop'); + + const cleanup = () => { + modal.remove(); + }; + + cancelBtn.addEventListener('click', () => { + cleanup(); + if (onCancel) onCancel(); + }); + + confirmBtn.addEventListener('click', () => { + cleanup(); + onConfirm(); + }); + + // Close on backdrop click + backdrop.addEventListener('click', (e) => { + if (e.target === backdrop) { + cleanup(); + if (onCancel) onCancel(); } }); + + // Close on escape key + const handleEscape = (e) => { + if (e.key === 'Escape') { + cleanup(); + document.removeEventListener('keydown', handleEscape); + if (onCancel) onCancel(); + } + }; + document.addEventListener('keydown', handleEscape); + + // Focus the confirm button for keyboard navigation + setTimeout(() => { + confirmBtn.focus(); + }, 100); +} + +// Update the cancelSignup function to use the custom modal +async function cancelSignup(shiftId) { + showConfirmModal( + 'Are you sure you want to cancel your signup for this shift?', + async () => { + try { + const response = await fetch(`/api/shifts/${shiftId}/cancel`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }); + + const data = await response.json(); + + if (data.success) { + showStatus('Signup cancelled', 'success'); + await loadShifts(); + await loadMySignups(); + } else { + showStatus(data.error || 'Failed to cancel signup', 'error'); + } + } catch (error) { + console.error('Error cancelling signup:', error); + showStatus('Failed to cancel signup', 'error'); + } + } + ); +} + +function setupEventListeners() { + const dateFilter = document.getElementById('date-filter'); + if (dateFilter) { + dateFilter.addEventListener('change', filterShifts); + } +} + +function filterShifts() { + const dateFilter = document.getElementById('date-filter').value; + + if (!dateFilter) { + displayShifts(allShifts); + return; + } + + const filtered = allShifts.filter(shift => { + return shift.Date === dateFilter; // Changed from shift.date to shift.Date + }); + + displayShifts(filtered); +} + +function clearFilters() { + document.getElementById('date-filter').value = ''; + loadShifts(); // Reload shifts without filters +} + +function showStatus(message, type = 'info') { + const container = document.getElementById('status-container'); + if (!container) return; + + const messageDiv = document.createElement('div'); + messageDiv.className = `status-message ${type}`; + messageDiv.textContent = message; + + container.appendChild(messageDiv); + + setTimeout(() => { + messageDiv.remove(); + }, 5000); +} + +function escapeHtml(text) { + if (text === null || text === undefined) { + return ''; + } + const div = document.createElement('div'); + div.textContent = String(text); + return div.innerHTML; } // Calendar View Functions @@ -755,6 +1037,11 @@ function createCalendarShift(shift) { function showShiftPopup(shift, targetElement) { // Remove any existing popup + if (currentPopup) { + currentPopup.remove(); + currentPopup = null; + } + const existingPopup = document.querySelector('.shift-popup'); if (existingPopup) { existingPopup.remove(); @@ -786,6 +1073,7 @@ function showShiftPopup(shift, targetElement) { // Position popup document.body.appendChild(popup); + currentPopup = popup; // Track the current popup const rect = targetElement.getBoundingClientRect(); const popupRect = popup.getBoundingClientRect(); @@ -810,23 +1098,28 @@ function showShiftPopup(shift, targetElement) { const cancelBtn = popup.querySelector('.cancel-signup-btn'); if (signupBtn) { - signupBtn.addEventListener('click', async () => { + signupBtn.addEventListener('click', async (e) => { + e.stopPropagation(); await signupForShift(shift.ID); popup.remove(); + currentPopup = null; }); } if (cancelBtn) { - cancelBtn.addEventListener('click', async () => { + cancelBtn.addEventListener('click', async (e) => { + e.stopPropagation(); await cancelSignup(shift.ID); popup.remove(); + currentPopup = null; }); } // Close popup when clicking outside const closePopup = (e) => { - if (!popup.contains(e.target)) { + if (!popup.contains(e.target) && e.target !== targetElement) { popup.remove(); + currentPopup = null; document.removeEventListener('click', closePopup); } }; diff --git a/mkdocs/.cache/plugin/social/08c3137589ee1e07c6b82c5f3e9e47bf.png b/mkdocs/.cache/plugin/social/08c3137589ee1e07c6b82c5f3e9e47bf.png deleted file mode 100644 index 5215c38..0000000 Binary files a/mkdocs/.cache/plugin/social/08c3137589ee1e07c6b82c5f3e9e47bf.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/09bbbc93a961c0990fa7e3217673978f.png b/mkdocs/.cache/plugin/social/09bbbc93a961c0990fa7e3217673978f.png index 3bec336..d19dffb 100644 Binary files a/mkdocs/.cache/plugin/social/09bbbc93a961c0990fa7e3217673978f.png and b/mkdocs/.cache/plugin/social/09bbbc93a961c0990fa7e3217673978f.png differ diff --git a/mkdocs/.cache/plugin/social/0bb26570b1971d8fad2883fbe816588f.png b/mkdocs/.cache/plugin/social/0bb26570b1971d8fad2883fbe816588f.png deleted file mode 100644 index 9678544..0000000 Binary files a/mkdocs/.cache/plugin/social/0bb26570b1971d8fad2883fbe816588f.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/0ee943db87dfa39f1d8b8882384425da.png b/mkdocs/.cache/plugin/social/0ee943db87dfa39f1d8b8882384425da.png deleted file mode 100644 index 5263f14..0000000 Binary files a/mkdocs/.cache/plugin/social/0ee943db87dfa39f1d8b8882384425da.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/0efb03f86bd9388aaf748992627aca2b.png b/mkdocs/.cache/plugin/social/0efb03f86bd9388aaf748992627aca2b.png deleted file mode 100644 index 2a087b8..0000000 Binary files a/mkdocs/.cache/plugin/social/0efb03f86bd9388aaf748992627aca2b.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/10a5546a448a8a0b16de3eb978f8a68f.png b/mkdocs/.cache/plugin/social/10a5546a448a8a0b16de3eb978f8a68f.png index bfb5e64..3f11c63 100644 Binary files a/mkdocs/.cache/plugin/social/10a5546a448a8a0b16de3eb978f8a68f.png and b/mkdocs/.cache/plugin/social/10a5546a448a8a0b16de3eb978f8a68f.png differ diff --git a/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png b/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png deleted file mode 100644 index ea7810f..0000000 Binary files a/mkdocs/.cache/plugin/social/112206e21dc1efc3b2fdcaf783b198ac.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/13b7450908684a452205d94a249e3e4d.png b/mkdocs/.cache/plugin/social/13b7450908684a452205d94a249e3e4d.png deleted file mode 100644 index 237bb71..0000000 Binary files a/mkdocs/.cache/plugin/social/13b7450908684a452205d94a249e3e4d.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/1ba54b15aedf0426dba781e37512dce1.png b/mkdocs/.cache/plugin/social/1ba54b15aedf0426dba781e37512dce1.png deleted file mode 100644 index 420d8e2..0000000 Binary files a/mkdocs/.cache/plugin/social/1ba54b15aedf0426dba781e37512dce1.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/1e5bfd318a5e9797b8831dbc973c0e57.png b/mkdocs/.cache/plugin/social/1e5bfd318a5e9797b8831dbc973c0e57.png deleted file mode 100644 index 26daf57..0000000 Binary files a/mkdocs/.cache/plugin/social/1e5bfd318a5e9797b8831dbc973c0e57.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/2030a47afa1104093ebf519a6f22a7d1.png b/mkdocs/.cache/plugin/social/2030a47afa1104093ebf519a6f22a7d1.png index b00e9d9..8a6b5ac 100644 Binary files a/mkdocs/.cache/plugin/social/2030a47afa1104093ebf519a6f22a7d1.png and b/mkdocs/.cache/plugin/social/2030a47afa1104093ebf519a6f22a7d1.png differ diff --git a/mkdocs/.cache/plugin/social/29555bf533763ae8eb2a49b8d7625632.png b/mkdocs/.cache/plugin/social/29555bf533763ae8eb2a49b8d7625632.png deleted file mode 100644 index 332440b..0000000 Binary files a/mkdocs/.cache/plugin/social/29555bf533763ae8eb2a49b8d7625632.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/2c4ddee96734cdcd028d11e87d09ac54.png b/mkdocs/.cache/plugin/social/2c4ddee96734cdcd028d11e87d09ac54.png deleted file mode 100644 index 4d94562..0000000 Binary files a/mkdocs/.cache/plugin/social/2c4ddee96734cdcd028d11e87d09ac54.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/2ebef08d3656ba930098da817110716f.png b/mkdocs/.cache/plugin/social/2ebef08d3656ba930098da817110716f.png deleted file mode 100644 index 2a8927b..0000000 Binary files a/mkdocs/.cache/plugin/social/2ebef08d3656ba930098da817110716f.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/33611a3592836152c371702e7667700b.png b/mkdocs/.cache/plugin/social/33611a3592836152c371702e7667700b.png deleted file mode 100644 index 4af557d..0000000 Binary files a/mkdocs/.cache/plugin/social/33611a3592836152c371702e7667700b.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/361e599f2484bc4c4e5e1bf247f2da41.png b/mkdocs/.cache/plugin/social/361e599f2484bc4c4e5e1bf247f2da41.png deleted file mode 100644 index 0e645bf..0000000 Binary files a/mkdocs/.cache/plugin/social/361e599f2484bc4c4e5e1bf247f2da41.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/3a2fd3f826d2a906ed92c3970b25cb7d.png b/mkdocs/.cache/plugin/social/3a2fd3f826d2a906ed92c3970b25cb7d.png deleted file mode 100644 index 4971e61..0000000 Binary files a/mkdocs/.cache/plugin/social/3a2fd3f826d2a906ed92c3970b25cb7d.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/3df345a41836bfa1f24aa074839aff71.png b/mkdocs/.cache/plugin/social/3df345a41836bfa1f24aa074839aff71.png index f62f0a0..37e5f35 100644 Binary files a/mkdocs/.cache/plugin/social/3df345a41836bfa1f24aa074839aff71.png and b/mkdocs/.cache/plugin/social/3df345a41836bfa1f24aa074839aff71.png differ diff --git a/mkdocs/.cache/plugin/social/3f61be45dd5d8e9a1d4e6862e64b7214.png b/mkdocs/.cache/plugin/social/3f61be45dd5d8e9a1d4e6862e64b7214.png deleted file mode 100644 index 481d9fa..0000000 Binary files a/mkdocs/.cache/plugin/social/3f61be45dd5d8e9a1d4e6862e64b7214.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/42eea5e63f7af7504d3273126c270804.png b/mkdocs/.cache/plugin/social/42eea5e63f7af7504d3273126c270804.png deleted file mode 100644 index 31c3c68..0000000 Binary files a/mkdocs/.cache/plugin/social/42eea5e63f7af7504d3273126c270804.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/431ab4f4662987971222ad7fc4583df8.png b/mkdocs/.cache/plugin/social/431ab4f4662987971222ad7fc4583df8.png deleted file mode 100644 index 1b798d8..0000000 Binary files a/mkdocs/.cache/plugin/social/431ab4f4662987971222ad7fc4583df8.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/440935fb98e7e27d2e2cfdc0bbbdc6ae.png b/mkdocs/.cache/plugin/social/440935fb98e7e27d2e2cfdc0bbbdc6ae.png deleted file mode 100644 index cce4739..0000000 Binary files a/mkdocs/.cache/plugin/social/440935fb98e7e27d2e2cfdc0bbbdc6ae.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/4491c6079bc3398b69a84ade4d737edc.png b/mkdocs/.cache/plugin/social/4491c6079bc3398b69a84ade4d737edc.png deleted file mode 100644 index afbbbe1..0000000 Binary files a/mkdocs/.cache/plugin/social/4491c6079bc3398b69a84ade4d737edc.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/461dbf70704556ebdba00d9b93fdd71a.png b/mkdocs/.cache/plugin/social/461dbf70704556ebdba00d9b93fdd71a.png index 8c9d2bc..77e4acc 100644 Binary files a/mkdocs/.cache/plugin/social/461dbf70704556ebdba00d9b93fdd71a.png and b/mkdocs/.cache/plugin/social/461dbf70704556ebdba00d9b93fdd71a.png differ diff --git a/mkdocs/.cache/plugin/social/4808f3bb43f566509e08cde812609b16.png b/mkdocs/.cache/plugin/social/4808f3bb43f566509e08cde812609b16.png deleted file mode 100644 index 4c174de..0000000 Binary files a/mkdocs/.cache/plugin/social/4808f3bb43f566509e08cde812609b16.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/499785a5782a92d89dee51c0bf8b6995.png b/mkdocs/.cache/plugin/social/499785a5782a92d89dee51c0bf8b6995.png index 60d2241..1d4020a 100644 Binary files a/mkdocs/.cache/plugin/social/499785a5782a92d89dee51c0bf8b6995.png and b/mkdocs/.cache/plugin/social/499785a5782a92d89dee51c0bf8b6995.png differ diff --git a/mkdocs/.cache/plugin/social/49f28fa8303f79b46bfb7904c8e551a1.png b/mkdocs/.cache/plugin/social/49f28fa8303f79b46bfb7904c8e551a1.png index cc6851b..b73e175 100644 Binary files a/mkdocs/.cache/plugin/social/49f28fa8303f79b46bfb7904c8e551a1.png and b/mkdocs/.cache/plugin/social/49f28fa8303f79b46bfb7904c8e551a1.png differ diff --git a/mkdocs/.cache/plugin/social/513e74590c0aaa12f169c3f283993a05.png b/mkdocs/.cache/plugin/social/513e74590c0aaa12f169c3f283993a05.png index d605523..da7950b 100644 Binary files a/mkdocs/.cache/plugin/social/513e74590c0aaa12f169c3f283993a05.png and b/mkdocs/.cache/plugin/social/513e74590c0aaa12f169c3f283993a05.png differ diff --git a/mkdocs/.cache/plugin/social/5642de01e7f59764299bd2e8ed35a5d0.png b/mkdocs/.cache/plugin/social/5642de01e7f59764299bd2e8ed35a5d0.png deleted file mode 100644 index 6305b67..0000000 Binary files a/mkdocs/.cache/plugin/social/5642de01e7f59764299bd2e8ed35a5d0.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/59775a558e0236f880c9401534507ab3.png b/mkdocs/.cache/plugin/social/59775a558e0236f880c9401534507ab3.png deleted file mode 100644 index 7121640..0000000 Binary files a/mkdocs/.cache/plugin/social/59775a558e0236f880c9401534507ab3.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/59a246ca1baf0f21e33a0f0a13e1fbd5.png b/mkdocs/.cache/plugin/social/59a246ca1baf0f21e33a0f0a13e1fbd5.png deleted file mode 100644 index 5fd1cda..0000000 Binary files a/mkdocs/.cache/plugin/social/59a246ca1baf0f21e33a0f0a13e1fbd5.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/5a026625721699a22ed4902c86e27264.png b/mkdocs/.cache/plugin/social/5a026625721699a22ed4902c86e27264.png index 6009e1f..4f37bd1 100644 Binary files a/mkdocs/.cache/plugin/social/5a026625721699a22ed4902c86e27264.png and b/mkdocs/.cache/plugin/social/5a026625721699a22ed4902c86e27264.png differ diff --git a/mkdocs/.cache/plugin/social/5c8323641288ce96dac5e5d0c03d1d88.png b/mkdocs/.cache/plugin/social/5c8323641288ce96dac5e5d0c03d1d88.png index d3f2f01..58583ca 100644 Binary files a/mkdocs/.cache/plugin/social/5c8323641288ce96dac5e5d0c03d1d88.png and b/mkdocs/.cache/plugin/social/5c8323641288ce96dac5e5d0c03d1d88.png differ diff --git a/mkdocs/.cache/plugin/social/5dc82dd4549191e7484d163c2916414e.png b/mkdocs/.cache/plugin/social/5dc82dd4549191e7484d163c2916414e.png deleted file mode 100644 index 340b120..0000000 Binary files a/mkdocs/.cache/plugin/social/5dc82dd4549191e7484d163c2916414e.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/5de16fced5aba77a2bd09132eb5fda0d.png b/mkdocs/.cache/plugin/social/5de16fced5aba77a2bd09132eb5fda0d.png index 1dfe0d5..8ca597c 100644 Binary files a/mkdocs/.cache/plugin/social/5de16fced5aba77a2bd09132eb5fda0d.png and b/mkdocs/.cache/plugin/social/5de16fced5aba77a2bd09132eb5fda0d.png differ diff --git a/mkdocs/.cache/plugin/social/607c40b09175ea34f533d65c20cf04a3.png b/mkdocs/.cache/plugin/social/607c40b09175ea34f533d65c20cf04a3.png deleted file mode 100644 index 9c80823..0000000 Binary files a/mkdocs/.cache/plugin/social/607c40b09175ea34f533d65c20cf04a3.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/630ed53169b0d638a0ecbc5a43b36dd5.png b/mkdocs/.cache/plugin/social/630ed53169b0d638a0ecbc5a43b36dd5.png index d3b1a8a..3876e7d 100644 Binary files a/mkdocs/.cache/plugin/social/630ed53169b0d638a0ecbc5a43b36dd5.png and b/mkdocs/.cache/plugin/social/630ed53169b0d638a0ecbc5a43b36dd5.png differ diff --git a/mkdocs/.cache/plugin/social/63fe0d7764ab46b6b1a896c92f5f08ad.png b/mkdocs/.cache/plugin/social/63fe0d7764ab46b6b1a896c92f5f08ad.png index de606a1..3bf3c14 100644 Binary files a/mkdocs/.cache/plugin/social/63fe0d7764ab46b6b1a896c92f5f08ad.png and b/mkdocs/.cache/plugin/social/63fe0d7764ab46b6b1a896c92f5f08ad.png differ diff --git a/mkdocs/.cache/plugin/social/668c246629c618cf956e7cea78d4037d.png b/mkdocs/.cache/plugin/social/668c246629c618cf956e7cea78d4037d.png deleted file mode 100644 index 42681cc..0000000 Binary files a/mkdocs/.cache/plugin/social/668c246629c618cf956e7cea78d4037d.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/6721c84d438859307b9b7082ea394f26.png b/mkdocs/.cache/plugin/social/6721c84d438859307b9b7082ea394f26.png deleted file mode 100644 index 9216ea5..0000000 Binary files a/mkdocs/.cache/plugin/social/6721c84d438859307b9b7082ea394f26.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/6e0a466e141c6410aa3b931db727ad5a.png b/mkdocs/.cache/plugin/social/6e0a466e141c6410aa3b931db727ad5a.png index 98db777..39fda7c 100644 Binary files a/mkdocs/.cache/plugin/social/6e0a466e141c6410aa3b931db727ad5a.png and b/mkdocs/.cache/plugin/social/6e0a466e141c6410aa3b931db727ad5a.png differ diff --git a/mkdocs/.cache/plugin/social/721bd151e1ec0d1e48006634b8ff2e38.png b/mkdocs/.cache/plugin/social/721bd151e1ec0d1e48006634b8ff2e38.png deleted file mode 100644 index 321a1b9..0000000 Binary files a/mkdocs/.cache/plugin/social/721bd151e1ec0d1e48006634b8ff2e38.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/7339eda49b6ad5cd292f8762fe09c799.png b/mkdocs/.cache/plugin/social/7339eda49b6ad5cd292f8762fe09c799.png deleted file mode 100644 index 3a4518e..0000000 Binary files a/mkdocs/.cache/plugin/social/7339eda49b6ad5cd292f8762fe09c799.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/740e1015f23d88c791e7cd6c726e1081.png b/mkdocs/.cache/plugin/social/740e1015f23d88c791e7cd6c726e1081.png deleted file mode 100644 index d10dcd9..0000000 Binary files a/mkdocs/.cache/plugin/social/740e1015f23d88c791e7cd6c726e1081.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/74ba9b753bb76229ba95a19f832f65f7.png b/mkdocs/.cache/plugin/social/74ba9b753bb76229ba95a19f832f65f7.png deleted file mode 100644 index 285cbbd..0000000 Binary files a/mkdocs/.cache/plugin/social/74ba9b753bb76229ba95a19f832f65f7.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/75846f8ef2f495a7834e15cf35a558af.png b/mkdocs/.cache/plugin/social/75846f8ef2f495a7834e15cf35a558af.png deleted file mode 100644 index d2cfff2..0000000 Binary files a/mkdocs/.cache/plugin/social/75846f8ef2f495a7834e15cf35a558af.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/7b06061b4b9b4a82384b4b9cf809471d.png b/mkdocs/.cache/plugin/social/7b06061b4b9b4a82384b4b9cf809471d.png index 1a165eb..493dfe4 100644 Binary files a/mkdocs/.cache/plugin/social/7b06061b4b9b4a82384b4b9cf809471d.png and b/mkdocs/.cache/plugin/social/7b06061b4b9b4a82384b4b9cf809471d.png differ diff --git a/mkdocs/.cache/plugin/social/7cc7e1ec8732cd69b83aa549bfb13cc3.png b/mkdocs/.cache/plugin/social/7cc7e1ec8732cd69b83aa549bfb13cc3.png index 6c00bdf..bf1ff94 100644 Binary files a/mkdocs/.cache/plugin/social/7cc7e1ec8732cd69b83aa549bfb13cc3.png and b/mkdocs/.cache/plugin/social/7cc7e1ec8732cd69b83aa549bfb13cc3.png differ diff --git a/mkdocs/.cache/plugin/social/7cf40f902bff1e35d8f0d03034794e4a.png b/mkdocs/.cache/plugin/social/7cf40f902bff1e35d8f0d03034794e4a.png deleted file mode 100644 index 4345aff..0000000 Binary files a/mkdocs/.cache/plugin/social/7cf40f902bff1e35d8f0d03034794e4a.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/7e31098fbf39b20bb38d3f1e683c5e05.png b/mkdocs/.cache/plugin/social/7e31098fbf39b20bb38d3f1e683c5e05.png deleted file mode 100644 index 44beaa9..0000000 Binary files a/mkdocs/.cache/plugin/social/7e31098fbf39b20bb38d3f1e683c5e05.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/7e8c377901c50a398db9288363ecd8be.png b/mkdocs/.cache/plugin/social/7e8c377901c50a398db9288363ecd8be.png deleted file mode 100644 index 439136b..0000000 Binary files a/mkdocs/.cache/plugin/social/7e8c377901c50a398db9288363ecd8be.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/83db91be2c8770ec6e0cad47805127ef.png b/mkdocs/.cache/plugin/social/83db91be2c8770ec6e0cad47805127ef.png deleted file mode 100644 index 6120273..0000000 Binary files a/mkdocs/.cache/plugin/social/83db91be2c8770ec6e0cad47805127ef.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/89cb9170565057569d85b76ef729d173.png b/mkdocs/.cache/plugin/social/89cb9170565057569d85b76ef729d173.png index 2762632..91abd87 100644 Binary files a/mkdocs/.cache/plugin/social/89cb9170565057569d85b76ef729d173.png and b/mkdocs/.cache/plugin/social/89cb9170565057569d85b76ef729d173.png differ diff --git a/mkdocs/.cache/plugin/social/8b4d2b2992e85f6cc7dcfc9a7eb7c502.png b/mkdocs/.cache/plugin/social/8b4d2b2992e85f6cc7dcfc9a7eb7c502.png index 11040cd..2b6baf5 100644 Binary files a/mkdocs/.cache/plugin/social/8b4d2b2992e85f6cc7dcfc9a7eb7c502.png and b/mkdocs/.cache/plugin/social/8b4d2b2992e85f6cc7dcfc9a7eb7c502.png differ diff --git a/mkdocs/.cache/plugin/social/8e08f754f4d8c04a82391ae575aafaaa.png b/mkdocs/.cache/plugin/social/8e08f754f4d8c04a82391ae575aafaaa.png index f84a58e..4ddeb63 100644 Binary files a/mkdocs/.cache/plugin/social/8e08f754f4d8c04a82391ae575aafaaa.png and b/mkdocs/.cache/plugin/social/8e08f754f4d8c04a82391ae575aafaaa.png differ diff --git a/mkdocs/.cache/plugin/social/96a422da08dcb28205d2e584a7c620ca.png b/mkdocs/.cache/plugin/social/96a422da08dcb28205d2e584a7c620ca.png deleted file mode 100644 index c6dd85d..0000000 Binary files a/mkdocs/.cache/plugin/social/96a422da08dcb28205d2e584a7c620ca.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/9ce7dbc001bbf6d2aac4483d3c682a9b.png b/mkdocs/.cache/plugin/social/9ce7dbc001bbf6d2aac4483d3c682a9b.png index f14d6ca..4a3b3ae 100644 Binary files a/mkdocs/.cache/plugin/social/9ce7dbc001bbf6d2aac4483d3c682a9b.png and b/mkdocs/.cache/plugin/social/9ce7dbc001bbf6d2aac4483d3c682a9b.png differ diff --git a/mkdocs/.cache/plugin/social/9d5df4651afceab422984c33bfbdc9ed.png b/mkdocs/.cache/plugin/social/9d5df4651afceab422984c33bfbdc9ed.png deleted file mode 100644 index 1c05739..0000000 Binary files a/mkdocs/.cache/plugin/social/9d5df4651afceab422984c33bfbdc9ed.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/9eea194aa16428b4509c011322cd1920.png b/mkdocs/.cache/plugin/social/9eea194aa16428b4509c011322cd1920.png deleted file mode 100644 index 4fe2fb6..0000000 Binary files a/mkdocs/.cache/plugin/social/9eea194aa16428b4509c011322cd1920.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/9fdd03d6602a201cef87a201f27fc715.png b/mkdocs/.cache/plugin/social/9fdd03d6602a201cef87a201f27fc715.png deleted file mode 100644 index ea5947b..0000000 Binary files a/mkdocs/.cache/plugin/social/9fdd03d6602a201cef87a201f27fc715.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/a0553b3e88ffc3c868350353b63036cb.png b/mkdocs/.cache/plugin/social/a0553b3e88ffc3c868350353b63036cb.png deleted file mode 100644 index 441516d..0000000 Binary files a/mkdocs/.cache/plugin/social/a0553b3e88ffc3c868350353b63036cb.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/a2ad821d050eaeab17c1c5cc13a3277c.png b/mkdocs/.cache/plugin/social/a2ad821d050eaeab17c1c5cc13a3277c.png deleted file mode 100644 index 63c7038..0000000 Binary files a/mkdocs/.cache/plugin/social/a2ad821d050eaeab17c1c5cc13a3277c.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/a33fe3375450956515c8b6a627d12fd1.png b/mkdocs/.cache/plugin/social/a33fe3375450956515c8b6a627d12fd1.png deleted file mode 100644 index c6336a5..0000000 Binary files a/mkdocs/.cache/plugin/social/a33fe3375450956515c8b6a627d12fd1.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/a5775bbacaf158405defcb68416ea8bd.png b/mkdocs/.cache/plugin/social/a5775bbacaf158405defcb68416ea8bd.png deleted file mode 100644 index 9615bd6..0000000 Binary files a/mkdocs/.cache/plugin/social/a5775bbacaf158405defcb68416ea8bd.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/aa0707be07fb7b02ab3372711d954d83.png b/mkdocs/.cache/plugin/social/aa0707be07fb7b02ab3372711d954d83.png deleted file mode 100644 index 1a02082..0000000 Binary files a/mkdocs/.cache/plugin/social/aa0707be07fb7b02ab3372711d954d83.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/af754735b566f6d264e3f21b80a2d139.png b/mkdocs/.cache/plugin/social/af754735b566f6d264e3f21b80a2d139.png deleted file mode 100644 index 8bc5d08..0000000 Binary files a/mkdocs/.cache/plugin/social/af754735b566f6d264e3f21b80a2d139.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/b3b12e8c71c3f991609d278875d5ca18.png b/mkdocs/.cache/plugin/social/b3b12e8c71c3f991609d278875d5ca18.png deleted file mode 100644 index e5a8891..0000000 Binary files a/mkdocs/.cache/plugin/social/b3b12e8c71c3f991609d278875d5ca18.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/bbe91e9dff56613b08f6e23ca8196d35.png b/mkdocs/.cache/plugin/social/bbe91e9dff56613b08f6e23ca8196d35.png deleted file mode 100644 index a2d7102..0000000 Binary files a/mkdocs/.cache/plugin/social/bbe91e9dff56613b08f6e23ca8196d35.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/c502f7e3a2048dd87b10fa6ff5291aab.png b/mkdocs/.cache/plugin/social/c502f7e3a2048dd87b10fa6ff5291aab.png deleted file mode 100644 index 0596860..0000000 Binary files a/mkdocs/.cache/plugin/social/c502f7e3a2048dd87b10fa6ff5291aab.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/c72738f5b04c0e7332b338cbcc9bd0ff.png b/mkdocs/.cache/plugin/social/c72738f5b04c0e7332b338cbcc9bd0ff.png deleted file mode 100644 index 5a89605..0000000 Binary files a/mkdocs/.cache/plugin/social/c72738f5b04c0e7332b338cbcc9bd0ff.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/c7a42e4b7c6d01803867d237fe2d8617.png b/mkdocs/.cache/plugin/social/c7a42e4b7c6d01803867d237fe2d8617.png new file mode 100644 index 0000000..8f73b79 Binary files /dev/null and b/mkdocs/.cache/plugin/social/c7a42e4b7c6d01803867d237fe2d8617.png differ diff --git a/mkdocs/.cache/plugin/social/ca221d210444f7caca141f1462c1634d.png b/mkdocs/.cache/plugin/social/ca221d210444f7caca141f1462c1634d.png index ea626c4..ab1f8f4 100644 Binary files a/mkdocs/.cache/plugin/social/ca221d210444f7caca141f1462c1634d.png and b/mkdocs/.cache/plugin/social/ca221d210444f7caca141f1462c1634d.png differ diff --git a/mkdocs/.cache/plugin/social/ca7a35746e5b24c6195659a5ce3f5a0d.png b/mkdocs/.cache/plugin/social/ca7a35746e5b24c6195659a5ce3f5a0d.png deleted file mode 100644 index f46cc75..0000000 Binary files a/mkdocs/.cache/plugin/social/ca7a35746e5b24c6195659a5ce3f5a0d.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/cb389f13acf697cb6aa15c98fec4a25a.png b/mkdocs/.cache/plugin/social/cb389f13acf697cb6aa15c98fec4a25a.png deleted file mode 100644 index ffcc330..0000000 Binary files a/mkdocs/.cache/plugin/social/cb389f13acf697cb6aa15c98fec4a25a.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/cffa905c80eff784b883e05e89cc8e46.png b/mkdocs/.cache/plugin/social/cffa905c80eff784b883e05e89cc8e46.png deleted file mode 100644 index eebe859..0000000 Binary files a/mkdocs/.cache/plugin/social/cffa905c80eff784b883e05e89cc8e46.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/d988b15f28c148e32971e4f12f707389.png b/mkdocs/.cache/plugin/social/d988b15f28c148e32971e4f12f707389.png deleted file mode 100644 index 91be978..0000000 Binary files a/mkdocs/.cache/plugin/social/d988b15f28c148e32971e4f12f707389.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/e2de330782e38985d85dffc7a4d6b94c.png b/mkdocs/.cache/plugin/social/e2de330782e38985d85dffc7a4d6b94c.png deleted file mode 100644 index 5f99b74..0000000 Binary files a/mkdocs/.cache/plugin/social/e2de330782e38985d85dffc7a4d6b94c.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/e323ce43d557f92ed3a43f83a2c99c85.png b/mkdocs/.cache/plugin/social/e323ce43d557f92ed3a43f83a2c99c85.png deleted file mode 100644 index 7a0819e..0000000 Binary files a/mkdocs/.cache/plugin/social/e323ce43d557f92ed3a43f83a2c99c85.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/e66117191f6f452004c243553237c2ee.png b/mkdocs/.cache/plugin/social/e66117191f6f452004c243553237c2ee.png deleted file mode 100644 index fb5de1a..0000000 Binary files a/mkdocs/.cache/plugin/social/e66117191f6f452004c243553237c2ee.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/f380f2c14dd2d5339cbb8e757cc24fc9.png b/mkdocs/.cache/plugin/social/f380f2c14dd2d5339cbb8e757cc24fc9.png deleted file mode 100644 index bf058b1..0000000 Binary files a/mkdocs/.cache/plugin/social/f380f2c14dd2d5339cbb8e757cc24fc9.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/f3cbac41242a5f4062687e6ebf8b69a9.png b/mkdocs/.cache/plugin/social/f3cbac41242a5f4062687e6ebf8b69a9.png index a5d2f7c..8c4aaa6 100644 Binary files a/mkdocs/.cache/plugin/social/f3cbac41242a5f4062687e6ebf8b69a9.png and b/mkdocs/.cache/plugin/social/f3cbac41242a5f4062687e6ebf8b69a9.png differ diff --git a/mkdocs/.cache/plugin/social/fb1ef6eb92689bdb34466fc79a8aebdf.png b/mkdocs/.cache/plugin/social/fb1ef6eb92689bdb34466fc79a8aebdf.png index dc579a3..118ab81 100644 Binary files a/mkdocs/.cache/plugin/social/fb1ef6eb92689bdb34466fc79a8aebdf.png and b/mkdocs/.cache/plugin/social/fb1ef6eb92689bdb34466fc79a8aebdf.png differ diff --git a/mkdocs/.cache/plugin/social/fb97f0bfe74bea4659ea90c0a851c2ec.png b/mkdocs/.cache/plugin/social/fb97f0bfe74bea4659ea90c0a851c2ec.png deleted file mode 100644 index 9be948c..0000000 Binary files a/mkdocs/.cache/plugin/social/fb97f0bfe74bea4659ea90c0a851c2ec.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fbde04cce7c404b3322bd153c6e57a38.png b/mkdocs/.cache/plugin/social/fbde04cce7c404b3322bd153c6e57a38.png deleted file mode 100644 index 914f854..0000000 Binary files a/mkdocs/.cache/plugin/social/fbde04cce7c404b3322bd153c6e57a38.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fbe77cdbcef871b9d78306f1c9bdce51.png b/mkdocs/.cache/plugin/social/fbe77cdbcef871b9d78306f1c9bdce51.png deleted file mode 100644 index 1dbabe1..0000000 Binary files a/mkdocs/.cache/plugin/social/fbe77cdbcef871b9d78306f1c9bdce51.png and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fd3474c8ee7ae0ad5529def83d0c8857.png b/mkdocs/.cache/plugin/social/fd3474c8ee7ae0ad5529def83d0c8857.png index 11839b0..5a6e8d0 100644 Binary files a/mkdocs/.cache/plugin/social/fd3474c8ee7ae0ad5529def83d0c8857.png and b/mkdocs/.cache/plugin/social/fd3474c8ee7ae0ad5529def83d0c8857.png differ diff --git a/mkdocs/.cache/plugin/social/fd4de0e14e62b2216135775537405340.png b/mkdocs/.cache/plugin/social/fd4de0e14e62b2216135775537405340.png index 8697881..54e8821 100644 Binary files a/mkdocs/.cache/plugin/social/fd4de0e14e62b2216135775537405340.png and b/mkdocs/.cache/plugin/social/fd4de0e14e62b2216135775537405340.png differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Black Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Black Italic.ttf deleted file mode 100644 index c71c549..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Black Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Black.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Black.ttf deleted file mode 100644 index d51221a..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Black.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Bold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Bold Italic.ttf deleted file mode 100644 index f73d681..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Bold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Bold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Bold.ttf deleted file mode 100644 index 9d7cf22..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Bold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black Italic.ttf deleted file mode 100644 index 0c31e9f..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black.ttf deleted file mode 100644 index 7529d1b..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Black.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold Italic.ttf deleted file mode 100644 index d269187..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold.ttf deleted file mode 100644 index c3ccd49..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Bold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold Italic.ttf deleted file mode 100644 index aeff7c2..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold.ttf deleted file mode 100644 index 782442a..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight Italic.ttf deleted file mode 100644 index 0f6fe70..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight.ttf deleted file mode 100644 index 16a1560..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed ExtraLight.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Italic.ttf deleted file mode 100644 index 3b387eb..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light Italic.ttf deleted file mode 100644 index 9f623e0..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light.ttf deleted file mode 100644 index e70c357..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Light.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium Italic.ttf deleted file mode 100644 index 80ff64e..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium.ttf deleted file mode 100644 index dd2842b..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Medium.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Regular.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Regular.ttf deleted file mode 100644 index 5af42d4..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Regular.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold Italic.ttf deleted file mode 100644 index 6cb4656..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold.ttf deleted file mode 100644 index 4297f17..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed SemiBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin Italic.ttf deleted file mode 100644 index e58e966..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin.ttf deleted file mode 100644 index 1ccebcc..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Condensed Thin.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold Italic.ttf deleted file mode 100644 index a5536f5..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold.ttf deleted file mode 100644 index 7092a88..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight Italic.ttf deleted file mode 100644 index 23dbbef..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight.ttf deleted file mode 100644 index 75608c6..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/ExtraLight.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Italic.ttf deleted file mode 100644 index 978e53a..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Light Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Light Italic.ttf deleted file mode 100644 index a6e5047..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Light Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Light.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Light.ttf deleted file mode 100644 index 6fcd5f9..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Light.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Medium Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Medium Italic.ttf deleted file mode 100644 index ef9ed1b..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Medium Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Medium.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Medium.ttf deleted file mode 100644 index d629e98..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Medium.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Regular.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Regular.ttf deleted file mode 100644 index bba55f6..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Regular.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold Italic.ttf deleted file mode 100644 index 132cca1..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold.ttf deleted file mode 100644 index 3f34834..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black Italic.ttf deleted file mode 100644 index 19a5096..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black.ttf deleted file mode 100644 index 8eedb64..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Black.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold Italic.ttf deleted file mode 100644 index 8604aee..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold.ttf deleted file mode 100644 index 98d7b0d..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Bold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold Italic.ttf deleted file mode 100644 index b40ce77..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold.ttf deleted file mode 100644 index 36423c3..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight Italic.ttf deleted file mode 100644 index 929a093..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight.ttf deleted file mode 100644 index e1c25a0..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed ExtraLight.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Italic.ttf deleted file mode 100644 index 23454ff..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light Italic.ttf deleted file mode 100644 index c096473..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light.ttf deleted file mode 100644 index b9aedcd..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Light.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium Italic.ttf deleted file mode 100644 index ab34b70..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium.ttf deleted file mode 100644 index e9c34d6..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Medium.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Regular.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Regular.ttf deleted file mode 100644 index 36109ba..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Regular.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold Italic.ttf deleted file mode 100644 index e88bc4a..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold.ttf deleted file mode 100644 index 6d10b33..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed SemiBold.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin Italic.ttf deleted file mode 100644 index 81afeea..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin.ttf deleted file mode 100644 index 8ed8d79..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/SemiCondensed Thin.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Thin Italic.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Thin Italic.ttf deleted file mode 100644 index 0381198..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Thin Italic.ttf and /dev/null differ diff --git a/mkdocs/.cache/plugin/social/fonts/Roboto/Thin.ttf b/mkdocs/.cache/plugin/social/fonts/Roboto/Thin.ttf deleted file mode 100644 index 6ee97b8..0000000 Binary files a/mkdocs/.cache/plugin/social/fonts/Roboto/Thin.ttf and /dev/null differ diff --git a/mkdocs/core b/mkdocs/core deleted file mode 100644 index ade3959..0000000 Binary files a/mkdocs/core and /dev/null differ diff --git a/mkdocs/docs/assets/repo-data/admin-changemaker.lite.json b/mkdocs/docs/assets/repo-data/admin-changemaker.lite.json index d21b0ca..8c6f8ea 100644 --- a/mkdocs/docs/assets/repo-data/admin-changemaker.lite.json +++ b/mkdocs/docs/assets/repo-data/admin-changemaker.lite.json @@ -6,11 +6,11 @@ "language": "HTML", "stars_count": 0, "forks_count": 0, - "open_issues_count": 10, - "updated_at": "2025-07-20T10:26:01-06:00", + "open_issues_count": 8, + "updated_at": "2025-07-24T17:09:48-06:00", "created_at": "2025-05-28T14:54:59-06:00", "clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git", "ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git", "default_branch": "main", - "last_build_update": "2025-07-20T10:26:01-06:00" + "last_build_update": "2025-07-24T17:09:48-06:00" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/anthropics-claude-code.json b/mkdocs/docs/assets/repo-data/anthropics-claude-code.json index 426077d..9e2f2c4 100644 --- a/mkdocs/docs/assets/repo-data/anthropics-claude-code.json +++ b/mkdocs/docs/assets/repo-data/anthropics-claude-code.json @@ -4,13 +4,13 @@ "description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.", "html_url": "https://github.com/anthropics/claude-code", "language": "PowerShell", - "stars_count": 24785, - "forks_count": 1360, - "open_issues_count": 2190, - "updated_at": "2025-07-20T17:36:39Z", + "stars_count": 26166, + "forks_count": 1437, + "open_issues_count": 2513, + "updated_at": "2025-07-27T00:10:38Z", "created_at": "2025-02-22T17:41:21Z", "clone_url": "https://github.com/anthropics/claude-code.git", "ssh_url": "git@github.com:anthropics/claude-code.git", "default_branch": "main", - "last_build_update": "2025-07-19T00:06:09Z" + "last_build_update": "2025-07-25T21:06:46Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/coder-code-server.json b/mkdocs/docs/assets/repo-data/coder-code-server.json index 85a4000..5487e24 100644 --- a/mkdocs/docs/assets/repo-data/coder-code-server.json +++ b/mkdocs/docs/assets/repo-data/coder-code-server.json @@ -4,13 +4,13 @@ "description": "VS Code in the browser", "html_url": "https://github.com/coder/code-server", "language": "TypeScript", - "stars_count": 72988, - "forks_count": 6112, - "open_issues_count": 139, - "updated_at": "2025-07-20T17:46:07Z", + "stars_count": 73102, + "forks_count": 6130, + "open_issues_count": 143, + "updated_at": "2025-07-26T22:12:45Z", "created_at": "2019-02-27T16:50:41Z", "clone_url": "https://github.com/coder/code-server.git", "ssh_url": "git@github.com:coder/code-server.git", "default_branch": "main", - "last_build_update": "2025-07-17T21:36:37Z" + "last_build_update": "2025-07-24T23:16:29Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/gethomepage-homepage.json b/mkdocs/docs/assets/repo-data/gethomepage-homepage.json index 9de0522..900413d 100644 --- a/mkdocs/docs/assets/repo-data/gethomepage-homepage.json +++ b/mkdocs/docs/assets/repo-data/gethomepage-homepage.json @@ -4,13 +4,13 @@ "description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.", "html_url": "https://github.com/gethomepage/homepage", "language": "JavaScript", - "stars_count": 24903, - "forks_count": 1544, - "open_issues_count": 2, - "updated_at": "2025-07-20T17:37:56Z", + "stars_count": 25016, + "forks_count": 1560, + "open_issues_count": 1, + "updated_at": "2025-07-26T22:55:16Z", "created_at": "2022-08-24T07:29:42Z", "clone_url": "https://github.com/gethomepage/homepage.git", "ssh_url": "git@github.com:gethomepage/homepage.git", "default_branch": "dev", - "last_build_update": "2025-07-20T12:13:09Z" + "last_build_update": "2025-07-27T00:42:35Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/go-gitea-gitea.json b/mkdocs/docs/assets/repo-data/go-gitea-gitea.json index 9825e6f..123b2de 100644 --- a/mkdocs/docs/assets/repo-data/go-gitea-gitea.json +++ b/mkdocs/docs/assets/repo-data/go-gitea-gitea.json @@ -4,13 +4,13 @@ "description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD", "html_url": "https://github.com/go-gitea/gitea", "language": "Go", - "stars_count": 49648, - "forks_count": 5914, - "open_issues_count": 2728, - "updated_at": "2025-07-20T16:44:42Z", + "stars_count": 49740, + "forks_count": 5920, + "open_issues_count": 2736, + "updated_at": "2025-07-27T00:44:09Z", "created_at": "2016-11-01T02:13:26Z", "clone_url": "https://github.com/go-gitea/gitea.git", "ssh_url": "git@github.com:go-gitea/gitea.git", "default_branch": "main", - "last_build_update": "2025-07-20T01:49:36Z" + "last_build_update": "2025-07-27T00:44:04Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/knadh-listmonk.json b/mkdocs/docs/assets/repo-data/knadh-listmonk.json index f2adfe7..3203c36 100644 --- a/mkdocs/docs/assets/repo-data/knadh-listmonk.json +++ b/mkdocs/docs/assets/repo-data/knadh-listmonk.json @@ -4,13 +4,13 @@ "description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.", "html_url": "https://github.com/knadh/listmonk", "language": "Go", - "stars_count": 17355, - "forks_count": 1673, - "open_issues_count": 98, - "updated_at": "2025-07-20T13:46:34Z", + "stars_count": 17468, + "forks_count": 1686, + "open_issues_count": 100, + "updated_at": "2025-07-26T16:53:36Z", "created_at": "2019-06-26T05:08:39Z", "clone_url": "https://github.com/knadh/listmonk.git", "ssh_url": "git@github.com:knadh/listmonk.git", "default_branch": "master", - "last_build_update": "2025-07-20T12:18:04Z" + "last_build_update": "2025-07-22T12:07:13Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/lyqht-mini-qr.json b/mkdocs/docs/assets/repo-data/lyqht-mini-qr.json index 088b388..22a9592 100644 --- a/mkdocs/docs/assets/repo-data/lyqht-mini-qr.json +++ b/mkdocs/docs/assets/repo-data/lyqht-mini-qr.json @@ -4,10 +4,10 @@ "description": "Create & scan cute qr codes easily \ud83d\udc7e", "html_url": "https://github.com/lyqht/mini-qr", "language": "Vue", - "stars_count": 1291, - "forks_count": 172, + "stars_count": 1338, + "forks_count": 177, "open_issues_count": 13, - "updated_at": "2025-07-20T11:49:09Z", + "updated_at": "2025-07-26T18:13:59Z", "created_at": "2023-04-21T14:20:14Z", "clone_url": "https://github.com/lyqht/mini-qr.git", "ssh_url": "git@github.com:lyqht/mini-qr.git", diff --git a/mkdocs/docs/assets/repo-data/n8n-io-n8n.json b/mkdocs/docs/assets/repo-data/n8n-io-n8n.json index acedbfc..04da70c 100644 --- a/mkdocs/docs/assets/repo-data/n8n-io-n8n.json +++ b/mkdocs/docs/assets/repo-data/n8n-io-n8n.json @@ -4,13 +4,13 @@ "description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.", "html_url": "https://github.com/n8n-io/n8n", "language": "TypeScript", - "stars_count": 121441, - "forks_count": 36499, - "open_issues_count": 987, - "updated_at": "2025-07-20T17:49:04Z", + "stars_count": 123887, + "forks_count": 37509, + "open_issues_count": 971, + "updated_at": "2025-07-27T00:44:16Z", "created_at": "2019-06-22T09:24:21Z", "clone_url": "https://github.com/n8n-io/n8n.git", "ssh_url": "git@github.com:n8n-io/n8n.git", "default_branch": "master", - "last_build_update": "2025-07-20T14:58:33Z" + "last_build_update": "2025-07-26T15:16:15Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/nocodb-nocodb.json b/mkdocs/docs/assets/repo-data/nocodb-nocodb.json index f0662db..1d7e15b 100644 --- a/mkdocs/docs/assets/repo-data/nocodb-nocodb.json +++ b/mkdocs/docs/assets/repo-data/nocodb-nocodb.json @@ -4,13 +4,13 @@ "description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 Open Source Airtable Alternative", "html_url": "https://github.com/nocodb/nocodb", "language": "TypeScript", - "stars_count": 55879, - "forks_count": 4026, - "open_issues_count": 677, - "updated_at": "2025-07-20T17:42:16Z", + "stars_count": 56041, + "forks_count": 4048, + "open_issues_count": 686, + "updated_at": "2025-07-26T23:44:10Z", "created_at": "2017-10-29T18:51:48Z", "clone_url": "https://github.com/nocodb/nocodb.git", "ssh_url": "git@github.com:nocodb/nocodb.git", "default_branch": "develop", - "last_build_update": "2025-07-19T20:55:35Z" + "last_build_update": "2025-07-26T18:53:06Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/ollama-ollama.json b/mkdocs/docs/assets/repo-data/ollama-ollama.json index 7bb683a..2068b25 100644 --- a/mkdocs/docs/assets/repo-data/ollama-ollama.json +++ b/mkdocs/docs/assets/repo-data/ollama-ollama.json @@ -4,13 +4,13 @@ "description": "Get up and running with Llama 3.3, DeepSeek-R1, Phi-4, Gemma 3, Mistral Small 3.1 and other large language models.", "html_url": "https://github.com/ollama/ollama", "language": "Go", - "stars_count": 147025, - "forks_count": 12459, - "open_issues_count": 1920, - "updated_at": "2025-07-20T17:32:08Z", + "stars_count": 147622, + "forks_count": 12527, + "open_issues_count": 1947, + "updated_at": "2025-07-27T00:34:59Z", "created_at": "2023-06-26T19:39:32Z", "clone_url": "https://github.com/ollama/ollama.git", "ssh_url": "git@github.com:ollama/ollama.git", "default_branch": "main", - "last_build_update": "2025-07-19T20:29:38Z" + "last_build_update": "2025-07-25T23:58:11Z" } \ No newline at end of file diff --git a/mkdocs/docs/assets/repo-data/squidfunk-mkdocs-material.json b/mkdocs/docs/assets/repo-data/squidfunk-mkdocs-material.json index 0829284..afabfb7 100644 --- a/mkdocs/docs/assets/repo-data/squidfunk-mkdocs-material.json +++ b/mkdocs/docs/assets/repo-data/squidfunk-mkdocs-material.json @@ -4,13 +4,13 @@ "description": "Documentation that simply works", "html_url": "https://github.com/squidfunk/mkdocs-material", "language": "Python", - "stars_count": 23952, - "forks_count": 3817, - "open_issues_count": 7, - "updated_at": "2025-07-20T17:36:49Z", + "stars_count": 24011, + "forks_count": 3825, + "open_issues_count": 6, + "updated_at": "2025-07-27T00:11:59Z", "created_at": "2016-01-28T22:09:23Z", "clone_url": "https://github.com/squidfunk/mkdocs-material.git", "ssh_url": "git@github.com:squidfunk/mkdocs-material.git", "default_branch": "master", - "last_build_update": "2025-07-17T21:29:23Z" + "last_build_update": "2025-07-26T15:53:16Z" } \ No newline at end of file diff --git a/mkdocs/docs/manual/map.md b/mkdocs/docs/manual/map.md index df924a1..167ee31 100644 --- a/mkdocs/docs/manual/map.md +++ b/mkdocs/docs/manual/map.md @@ -1,19 +1,138 @@ -# Map Manual -Quick Tips: +# Map Viewer Manual -- **Data:** Map works best when you clear your cookies, cache, and other data before use! This is because it is a web-app that pushes information to your phone. By clearing that data, you will always load the most recent version of the app to your browser. -- **Browser:** Map will work on nearly any browser however the developers test on Firefox, Brave, & Chromium. Firefox is what the bnkops team uses to access Map and is generally the most stable. +This manual provides step-by-step instructions for using the NocoDB Map Viewer web application. Each section covers a major feature with direct instructions. *(Insert screenshot - feature overview)* -## How to add new location - Video +--- -
- -
+## 1. Logging In + +1. Go to the map site URL (e.g., http://localhost:3000). +2. Enter your email and password on the login page. +3. Click **Login**. + - If you forget your password, contact an admin. *(Insert screenshot - login page)* + +--- + +## 2. Viewing the Map + +1. After login, you will see the interactive map. +2. Use your mouse or touch to pan and zoom. +3. Your current location may be shown as a blue dot. *(Insert screenshot - main map view)* + +--- + +## 3. Adding a New Location + +1. Click the **Add Location** button (usually a plus icon on the map). +2. Click on the map where you want to add the new location. +3. Fill out the form: + - First Name, Last Name, Email, Phone, Unit Number, Support Level, Address, Sign, Sign Size, Notes. +4. Click **Save**. +5. The new location will appear as a marker on the map. *(Insert screenshot - add location form)* + +--- + +## 4. Editing or Deleting a Location + +1. Click on a location marker. +2. In the popup, click **Edit** to update details, or **Delete** to remove the location. +3. Confirm your changes. *(Insert screenshot - location popup with edit/delete)* + +--- + +## 5. Auto-Refresh + +The map automatically refreshes every 30 seconds to show the latest data. *(Insert screenshot - refresh indicator)* + +--- + +## 6. Map Start Location & Boundaries + +1. The map opens to the default start location (Edmonton, Canada, unless changed by admin). +2. Admins can set boundaries to restrict where points can be added. *(Insert screenshot - map boundaries)* + +--- + +## 7. Walk Sheet Generator + +1. Go to the **Walk Sheet** section (usually in the admin panel). +2. Enter the title, subtitle, footer, and QR code info. +3. Click **Generate** to create a printable walk sheet. +4. Download or print the sheet. *(Insert screenshot - walk sheet generator)* + +--- + +## 8. QR Code Integration + +1. QR codes can be added to walk sheets for quick access to digital resources. +2. Enter the URL and label for each QR code in the settings. +3. QR codes will appear on the generated walk sheet. *(Insert screenshot - QR code on walk sheet)* + +--- + +## 9. Volunteer Shift Management + +### For All Users +1. Go to **Shifts** (http://localhost:3000/shifts.html). +2. View shifts in **Grid** or **Calendar** view. +3. Click a shift to see details. +4. Click **Sign Up** to join a shift. +5. Your signed-up shifts are shown at the top. *(Insert screenshot - shifts grid and calendar)* + +### Cancel a Signup +1. Click **Cancel** next to a shift you signed up for. +2. Confirm cancellation. *(Insert screenshot - cancel signup)* + +### Calendar Color Codes +- **Green**: Shifts you signed up for +- **Blue**: Available shifts +- **Gray**: Full shifts + +--- + +## 10. Admin Features + +### Shift Management +1. Go to **Admin Panel** (http://localhost:3000/admin.html). +2. Create, edit, or cancel shifts. +3. View all signups and manage volunteers. *(Insert screenshot - admin shift management)* + +### User Management +1. In the admin panel, go to **User Management**. +2. Add new users with email, password, and role (admin/user). +3. Delete users as needed. *(Insert screenshot - user management panel)* + +### Map Start Location +1. In the admin panel, go to **Start Location**. +2. Select coordinates and zoom level. +3. Save changes to update the map default. *(Insert screenshot - start location config)* + +### Walk Sheet Config +1. In the admin panel, go to **Walk Sheet Config**. +2. Edit walk sheet fields and QR codes. +3. Save to persist changes. *(Insert screenshot - walk sheet config panel)* + +--- + +## 11. Troubleshooting + +- **Locations not showing:** Check that location data includes latitude/longitude and your API token has read permissions. +- **Cannot add locations:** Ensure your API token has write permissions and coordinates are valid. +- **Connection errors:** Verify NocoDB is accessible and API URL is correct. +- **Build script issues:** Make sure your NocoDB database is clean and API token has admin permissions. + +--- + +## 12. Security & Privacy + +- All API tokens are kept server-side only. +- CORS and rate limiting are enabled. +- Input validation and security headers are enforced. + +--- + +## 13. Support + +For help, check the troubleshooting section, review NocoDB docs, or contact your admin. *(Insert screenshot - help section)* diff --git a/mkdocs/docs/overrides/lander.html b/mkdocs/docs/overrides/lander.html index bbf391b..aecc973 100644 --- a/mkdocs/docs/overrides/lander.html +++ b/mkdocs/docs/overrides/lander.html @@ -5,1647 +5,2110 @@ Changemaker Lite - Campaign Power Tools + } + - -
-
+ + - -
-
-
🚀 Campaign Documentation Reimagined
-

Power Tools for Modern Campaign Documentation

-

- Give your canvassers instant answers at the door. Turn your campaign website & knowledge into a searchable, - mobile-first documentation system that actually works in the field. Your data, your servers, your control. -

-
- - Get Started - - - Explore Tools + +
+
+
🚀 Hardware Up This Site Served by Changemaker - Lite
+

Power Tools for Modern Campaign Documentation

+

+ Give your supporters instant answers at the door, on the phone, or in person. Turn your campaign website & knowledge into a searchable, + mobile-first documentation system that actually works in the field or at the party. No corporate middlemen; your data, your servers, your platform. +

+ + + +
- - -
-
-
-

Your Canvassers Are Struggling

-

Traditional campaign tools weren't built for the reality of door-to-door work

+ +
+
+
100%
+
Local Data Ownership
-
-
-
📱
-

Can't Find Answers Fast

-

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
$0
+
Free to Self-Host
+
+
+
Canadian
+
Built by Locals
+
+
+
Open-Source
+
Built for Campaigns
+
+
+
+
+
+ + +
+
+
+

Your Canvassers Are Struggling

+

Traditional campaign tools weren't built for the reality of door-to-door work

+
+
+
+
📱
+

Can't Find Answers Fast

+

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
+
🗺️
+

Disconnected Data

+

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+
+
+
💸
+

Death by Subscription

+

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+
+
🔒
+

No Data Control

+

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+
+
+
📵
+

Not Mobile-Ready

+

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+
+
🏢
+

Foreign Dependencies

+

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+
+
+
+
+ + +
+
+
+

Documentation That Works at the Door

+

Everything your team needs, instantly searchable, always accessible

+
+ +
+
+

Mobile-First Everything

+

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

+
    +
  • Touch-optimized interfaces
  • +
  • Offline-capable after first load
  • +
  • Simple, easy to read, and clear buttons
  • +
  • One-thumb navigation
  • +
  • Instant load times
  • +
+
+
+ Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results +
+
+ +
+
+

Your Data, Your Servers, Your Rules

+

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

+
    +
  • 100% self-hosted infrastructure
  • +
  • Canadian data residency
  • +
  • Complete export capabilities
  • +
  • No vendor lock-in ever
  • +
  • Privacy-first architecture
  • +
+
+
+ Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings +
+
+ +
+
+

Instant Search Everything

+

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

+
    +
  • Full-text search across all documentation
  • +
  • Smart categorization and tagging
  • +
  • Works offline after first load
  • +
  • Mobile-optimized interface
  • +
+
+
+ Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons +
+
+ +
+
+

Interactive Canvassing Maps

+

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

+
    +
  • Real-time location tracking
  • +
  • Color-coded support levels
  • +
  • Add notes directly from the field
  • +
  • Track door knocks & interactions
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+

Living Documentation

+

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

+
    +
  • Real-time collaborative editing
  • +
  • Version control and history
  • +
  • Automatic mobile optimization
  • +
  • Beautiful, branded output
  • +
  • Thousands of plugins available
  • +
  • Local AI
  • +
+
+
+ Split view showing markdown editor on left, live preview on right with campaign branding +
+
+ +
+
+

Beautiul Websites

+

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

+
    +
  • Social media cards
  • +
  • Fully customizable
  • +
  • Custom pages and landers
  • +
  • Integrated blogging
  • +
  • Supports 60+ languages
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+ + +
+
+
+

Your Complete Campaign Power Tool Suite

+

Everything works together. No integrations needed. No monthly fees.

+
+ +
+
+
+
📝
+
+

Smart Documentation Hub

+
MkDocs + Code Server
+ mkdocs/docs/blog
-
-
🗺️
-

Disconnected Data

-

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+

Create beautiful, searchable documentation that your team will actually use.

+
    +
  • Instant search across everything
  • +
  • Mobile-first responsive design
  • +
  • Automatic table of contents
  • +
  • Deep linking to any section
  • +
+ -
-
💸
-

Death by Subscription

-

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+ +
+
+
🗺️
+
+

BNKops Map

+
Interactive Canvassing System
+
-
-
🔒
-

No Data Control

-

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+

Turn voter data into visual intelligence your canvassers can use.

+
    +
  • Real-time GPS tracking
  • +
  • Support level heat maps
  • +
  • Instant data entry
  • +
  • Offline-capable
  • +
+ -
-
📵
-

Not Mobile-Ready

-

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+ +
+
+
📊
+
+

Voter Database

+
NocoDB Spreadsheet Interface
+
-
-
🏢
-

Foreign Dependencies

-

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+

Manage voter data like a spreadsheet, access it like a database.

+
    +
  • Familiar Excel-like interface
  • +
  • Custom forms for data entry
  • +
  • Advanced filtering & search
  • +
  • API access for automation
  • +
+ +
+ +
+
+
📧
+
+

Email Command Center

+
Listmonk Email Platform
+
+
+

Professional email campaigns without the professional price tag.

+
    +
  • Unlimited subscribers
  • +
  • Beautiful templates
  • +
  • Open & click tracking
  • +
  • Automated sequences
  • +
+ +
+ +
+
+
🤖
+
+

Campaign Automation

+
n8n Workflow Engine
+
+
+

Automate repetitive tasks so your team can focus on voters.

+
    +
  • Visual workflow builder
  • +
  • Connect any service
  • +
  • Trigger-based automation
  • +
  • No coding required
  • +
+ +
+ +
+
+
🗃️
+
+

Version Control

+
Gitea Repository
+
+
+

Track changes, collaborate safely, and never lose work again.

+
    +
  • Full version history
  • +
  • Collaborative editing
  • +
  • Backup everything
  • +
  • Roll back changes
  • +
+
-
- - -
-
-
-

Documentation That Works at the Door

-

Everything your team needs, instantly searchable, always accessible

-
- -
-
-

Mobile-First Everything

-

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

-
    -
  • Touch-optimized interfaces
  • -
  • Offline-capable after first load
  • -
  • Simple, easy to read, and clear buttons
  • -
  • One-thumb navigation
  • -
  • Instant load times
  • -
-
-
- Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results -
-
- -
-
-

Your Data, Your Servers, Your Rules

-

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

-
    -
  • 100% self-hosted infrastructure
  • -
  • Canadian data residency
  • -
  • Complete export capabilities
  • -
  • No vendor lock-in ever
  • -
  • Privacy-first architecture
  • -
-
-
- Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings -
-
- -
-
-

Instant Search Everything

-

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

-
    -
  • Full-text search across all documentation
  • -
  • Smart categorization and tagging
  • -
  • Works offline after first load
  • -
  • Mobile-optimized interface
  • -
-
-
- Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons -
-
- -
-
-

Interactive Canvassing Maps

-

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

-
    -
  • Real-time location tracking
  • -
  • Color-coded support levels
  • -
  • Add notes directly from the field
  • -
  • Track door knocks & interactions
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
- -
-
-

Living Documentation

-

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

-
    -
  • Real-time collaborative editing
  • -
  • Version control and history
  • -
  • Automatic mobile optimization
  • -
  • Beautiful, branded output
  • -
  • Thousands of plugins available
  • -
  • Local AI
  • -
-
-
- Split view showing markdown editor on left, live preview on right with campaign branding -
-
- -
-
-

Beautiul Websites

-

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

-
    -
  • Social media cards
  • -
  • Fully customizable
  • -
  • Custom pages and landers
  • -
  • Integrated blogging
  • -
  • Supports 60+ languages
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
+
+
+ +
+
+
+

Canadian Tech for Canadian Campaigns

+

Why trust your movement's future to foreign corporations?

-
- - -
-
-
-

Your Complete Campaign Power Tool Suite

-

Everything works together. No integrations needed. No monthly fees.

+ +
+
+
🇨🇦
+

100% Canadian

+

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

- -
-
-
-
📝
-
-

Smart Documentation Hub

-
MkDocs + Code Server
- mkdocs/docs/blog
-
-

Create beautiful, searchable documentation that your team will actually use.

-
    -
  • Instant search across everything
  • -
  • Mobile-first responsive design
  • -
  • Automatic table of contents
  • -
  • Deep linking to any section
  • +
    +
    🔐
    +

    True Ownership

    +

    Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

    +
    +
    +
    🛡️
    +

    Privacy First

    +

    Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

    +
    +
+ +
+

The Sovereignty Difference

+
+
+

US Corporate Platforms

+
    +
  • ❌ Subject to Patriot Act
  • +
  • ❌ NSA surveillance
  • +
  • ❌ Corporate data mining
  • +
  • ❌ Foreign jurisdiction
-
- -
-
-
🗺️
-
-

BNKops Map

-
Interactive Canvassing System
-
-
-

Turn voter data into visual intelligence your canvassers can use.

-
    -
  • Real-time GPS tracking
  • -
  • Support level heat maps
  • -
  • Instant data entry
  • -
  • Offline-capable
  • +
    +

    Changemaker Lite

    +
      +
    • ✅ Canadian sovereignty
    • +
    • ✅ Zero surveillance
    • +
    • ✅ Complete privacy
    • +
    • ✅ Your servers, your rules
    - -
    - -
    -
    -
    📊
    -
    -

    Voter Database

    -
    NocoDB Spreadsheet Interface
    -
    -
    -

    Manage voter data like a spreadsheet, access it like a database.

    -
      -
    • Familiar Excel-like interface
    • -
    • Custom forms for data entry
    • -
    • Advanced filtering & search
    • -
    • API access for automation
    • -
    - -
    - -
    -
    -
    📧
    -
    -

    Email Command Center

    -
    Listmonk Email Platform
    -
    -
    -

    Professional email campaigns without the professional price tag.

    -
      -
    • Unlimited subscribers
    • -
    • Beautiful templates
    • -
    • Open & click tracking
    • -
    • Automated sequences
    • -
    - -
    - -
    -
    -
    🤖
    -
    -

    Campaign Automation

    -
    n8n Workflow Engine
    -
    -
    -

    Automate repetitive tasks so your team can focus on voters.

    -
      -
    • Visual workflow builder
    • -
    • Connect any service
    • -
    • Trigger-based automation
    • -
    • No coding required
    • -
    - -
    - -
    -
    -
    🗃️
    -
    -

    Version Control

    -
    Gitea Repository
    -
    -
    -

    Track changes, collaborate safely, and never lose work again.

    -
      -
    • Full version history
    • -
    • Collaborative editing
    • -
    • Backup everything
    • -
    • Roll back changes
    • -
    -
-
+
+
- -
-
-
-

Canadian Tech for Canadian Campaigns

-

Why trust your movement's future to foreign corporations?

+ +
+
+
+

Simple, Transparent Pricing

+

No hidden fees. No usage limits. No surprises.

+
+ +
+
+
+

Self-Hosted

+
$0
+
forever
+
+
    +
  • ✓ All 11 campaign tools
  • +
  • ✓ Unlimited users
  • +
  • ✓ Unlimited data
  • +
  • ✓ Complete documentation
  • +
  • ✓ Community support
  • +
  • ✓ Your infrastructure
  • +
  • ✓ 100% open source
  • +
+ Installation Guide +

Perfect for tech-savvy campaigns

-
-
-
🇨🇦
-

100% Canadian

-

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

-
-
-
🔐
-

True Ownership

-

Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

-
-
-
🛡️
-

Privacy First

-

Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

+ -
-

The Sovereignty Difference

-
-
-

US Corporate Platforms

-
    -
  • ❌ Subject to Patriot Act
  • -
  • ❌ NSA surveillance
  • -
  • ❌ Corporate data mining
  • -
  • ❌ Foreign jurisdiction
  • -
-
-
-

Changemaker Lite

-
    -
  • ✅ Canadian sovereignty
  • -
  • ✅ Zero surveillance
  • -
  • ✅ Complete privacy
  • -
  • ✅ Your servers, your rules
  • -
-
+
+
+

Managed Hosting

+
Custom
+
monthly
+
    +
  • ✓ We handle everything
  • +
  • ✓ Canadian data centers
  • +
  • ✓ Daily backups
  • +
  • ✓ 24/7 monitoring
  • +
  • ✓ Security updates
  • +
  • ✓ Priority support
  • +
  • ✓ Still your data
  • +
+ Contact Sales +

For larger campaigns

-
- - -
-
-
-

Simple, Transparent Pricing

-

No hidden fees. No usage limits. No surprises.

-
- -
-
-
-

Self-Hosted

-
$0
-
forever
-
-
    -
  • ✓ All 11 campaign tools
  • -
  • ✓ Unlimited users
  • -
  • ✓ Unlimited data
  • -
  • ✓ Complete documentation
  • -
  • ✓ Community support
  • -
  • ✓ Your infrastructure
  • -
  • ✓ 100% open source
  • -
- Installation Guide -

Perfect for tech-savvy campaigns

-
- - - -
-
-

Managed Hosting

-
Custom
-
monthly
-
-
    -
  • ✓ We handle everything
  • -
  • ✓ Canadian data centers
  • -
  • ✓ Daily backups
  • -
  • ✓ 24/7 monitoring
  • -
  • ✓ Security updates
  • -
  • ✓ Priority support
  • -
  • ✓ Still your data
  • -
- Contact Sales -

For larger campaigns

-
-
- -
-

Compare Your Savings

-

Average campaign using corporate tools: $1,200-$4,000/month

-

Same capabilities with Changemaker Lite: $0 (self-hosted)

- See detailed cost breakdown → -
+ +
+

Compare Your Savings

+

Average campaign using corporate tools: $1,200-$4,000/month

+

Same capabilities with Changemaker Lite: $0 (self-hosted)

+ See detailed cost breakdown →
-
+
+
- -
-
-
-

Everything Works Together

-

One login. One system. Infinite possibilities.

-
- -
-
-
- All systems communicate and build on one another -
-
-

- 🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees -

-
+ +
+
+
+

Everything Works Together

+

One login. One system. Infinite possibilities.

-
- - -
-
- - -
-
-

Ready to Power Up Your Campaign?

-

Join hundreds of campaigns using open-source tools to win elections and save money.

-

🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees

-
+
+
- + + + + // Observe all cards and sections + document.querySelectorAll('.problem-card, .tool-card, .solution-showcase, .testimonial-card').forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(20px)'; + el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + observer.observe(el); + }); + \ No newline at end of file diff --git a/mkdocs/mkdocs.yml b/mkdocs/mkdocs.yml index d54b726..1fdc9c7 100644 --- a/mkdocs/mkdocs.yml +++ b/mkdocs/mkdocs.yml @@ -4,6 +4,7 @@ site_url: https://cmlite.org site_author: Bunker Operations docs_dir: docs site_dir: site +use_directory_urls: true # Repository repo_url: https://gitea.bnkops.com/admin/changemaker.lite @@ -42,9 +43,9 @@ theme: - navigation.expand - navigation.footer - navigation.indexes - - navigation.instant - - navigation.instant.prefetch - - navigation.instant.progress + # - navigation.instant + # - navigation.instant.prefetch + # - navigation.instant.progress - navigation.path - navigation.prune - navigation.sections diff --git a/mkdocs/site/404.html b/mkdocs/site/404.html index 5b353b1..2c16f27 100644 --- a/mkdocs/site/404.html +++ b/mkdocs/site/404.html @@ -670,12 +670,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/adv/ansible/index.html b/mkdocs/site/adv/ansible/index.html index 8034b4e..8e63347 100644 --- a/mkdocs/site/adv/ansible/index.html +++ b/mkdocs/site/adv/ansible/index.html @@ -2945,12 +2945,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/adv/index.html b/mkdocs/site/adv/index.html index ce67dbe..d1216ee 100644 --- a/mkdocs/site/adv/index.html +++ b/mkdocs/site/adv/index.html @@ -1598,12 +1598,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/adv/vscode-ssh/index.html b/mkdocs/site/adv/vscode-ssh/index.html index eb12da2..2776ff9 100644 --- a/mkdocs/site/adv/vscode-ssh/index.html +++ b/mkdocs/site/adv/vscode-ssh/index.html @@ -3250,12 +3250,10 @@ Add to SSH config:
-
- - + diff --git a/mkdocs/site/assets/images/social/adv/ansible.png b/mkdocs/site/assets/images/social/adv/ansible.png index 6009e1f..4f37bd1 100644 Binary files a/mkdocs/site/assets/images/social/adv/ansible.png and b/mkdocs/site/assets/images/social/adv/ansible.png differ diff --git a/mkdocs/site/assets/images/social/adv/index.png b/mkdocs/site/assets/images/social/adv/index.png index ea626c4..ab1f8f4 100644 Binary files a/mkdocs/site/assets/images/social/adv/index.png and b/mkdocs/site/assets/images/social/adv/index.png differ diff --git a/mkdocs/site/assets/images/social/adv/vscode-ssh.png b/mkdocs/site/assets/images/social/adv/vscode-ssh.png index d3b1a8a..3876e7d 100644 Binary files a/mkdocs/site/assets/images/social/adv/vscode-ssh.png and b/mkdocs/site/assets/images/social/adv/vscode-ssh.png differ diff --git a/mkdocs/site/assets/images/social/blog/index.png b/mkdocs/site/assets/images/social/blog/index.png index f14d6ca..4a3b3ae 100644 Binary files a/mkdocs/site/assets/images/social/blog/index.png and b/mkdocs/site/assets/images/social/blog/index.png differ diff --git a/mkdocs/site/assets/images/social/build/index.png b/mkdocs/site/assets/images/social/build/index.png index d3f2f01..58583ca 100644 Binary files a/mkdocs/site/assets/images/social/build/index.png and b/mkdocs/site/assets/images/social/build/index.png differ diff --git a/mkdocs/site/assets/images/social/build/map.png b/mkdocs/site/assets/images/social/build/map.png index dc579a3..118ab81 100644 Binary files a/mkdocs/site/assets/images/social/build/map.png and b/mkdocs/site/assets/images/social/build/map.png differ diff --git a/mkdocs/site/assets/images/social/build/server.png b/mkdocs/site/assets/images/social/build/server.png index bfb5e64..3f11c63 100644 Binary files a/mkdocs/site/assets/images/social/build/server.png and b/mkdocs/site/assets/images/social/build/server.png differ diff --git a/mkdocs/site/assets/images/social/build/site.png b/mkdocs/site/assets/images/social/build/site.png index cc6851b..b73e175 100644 Binary files a/mkdocs/site/assets/images/social/build/site.png and b/mkdocs/site/assets/images/social/build/site.png differ diff --git a/mkdocs/site/assets/images/social/config/cloudflare-config.png b/mkdocs/site/assets/images/social/config/cloudflare-config.png index a5d2f7c..8c4aaa6 100644 Binary files a/mkdocs/site/assets/images/social/config/cloudflare-config.png and b/mkdocs/site/assets/images/social/config/cloudflare-config.png differ diff --git a/mkdocs/site/assets/images/social/config/coder.png b/mkdocs/site/assets/images/social/config/coder.png index 8c9d2bc..77e4acc 100644 Binary files a/mkdocs/site/assets/images/social/config/coder.png and b/mkdocs/site/assets/images/social/config/coder.png differ diff --git a/mkdocs/site/assets/images/social/config/index.png b/mkdocs/site/assets/images/social/config/index.png index 11839b0..5a6e8d0 100644 Binary files a/mkdocs/site/assets/images/social/config/index.png and b/mkdocs/site/assets/images/social/config/index.png differ diff --git a/mkdocs/site/assets/images/social/config/map.png b/mkdocs/site/assets/images/social/config/map.png index 60d2241..1d4020a 100644 Binary files a/mkdocs/site/assets/images/social/config/map.png and b/mkdocs/site/assets/images/social/config/map.png differ diff --git a/mkdocs/site/assets/images/social/index.png b/mkdocs/site/assets/images/social/index.png index 8697881..54e8821 100644 Binary files a/mkdocs/site/assets/images/social/index.png and b/mkdocs/site/assets/images/social/index.png differ diff --git a/mkdocs/site/assets/images/social/manual/map.png b/mkdocs/site/assets/images/social/manual/map.png index 60d2241..1d4020a 100644 Binary files a/mkdocs/site/assets/images/social/manual/map.png and b/mkdocs/site/assets/images/social/manual/map.png differ diff --git a/mkdocs/site/assets/images/social/phil/cost-comparison.png b/mkdocs/site/assets/images/social/phil/cost-comparison.png index 3bec336..d19dffb 100644 Binary files a/mkdocs/site/assets/images/social/phil/cost-comparison.png and b/mkdocs/site/assets/images/social/phil/cost-comparison.png differ diff --git a/mkdocs/site/assets/images/social/phil/index.png b/mkdocs/site/assets/images/social/phil/index.png index 1dfe0d5..8ca597c 100644 Binary files a/mkdocs/site/assets/images/social/phil/index.png and b/mkdocs/site/assets/images/social/phil/index.png differ diff --git a/mkdocs/site/assets/images/social/services/code-server.png b/mkdocs/site/assets/images/social/services/code-server.png index 8c9d2bc..77e4acc 100644 Binary files a/mkdocs/site/assets/images/social/services/code-server.png and b/mkdocs/site/assets/images/social/services/code-server.png differ diff --git a/mkdocs/site/assets/images/social/services/gitea.png b/mkdocs/site/assets/images/social/services/gitea.png index b00e9d9..8a6b5ac 100644 Binary files a/mkdocs/site/assets/images/social/services/gitea.png and b/mkdocs/site/assets/images/social/services/gitea.png differ diff --git a/mkdocs/site/assets/images/social/services/homepage.png b/mkdocs/site/assets/images/social/services/homepage.png index 2762632..91abd87 100644 Binary files a/mkdocs/site/assets/images/social/services/homepage.png and b/mkdocs/site/assets/images/social/services/homepage.png differ diff --git a/mkdocs/site/assets/images/social/services/index.png b/mkdocs/site/assets/images/social/services/index.png index f84a58e..4ddeb63 100644 Binary files a/mkdocs/site/assets/images/social/services/index.png and b/mkdocs/site/assets/images/social/services/index.png differ diff --git a/mkdocs/site/assets/images/social/services/listmonk.png b/mkdocs/site/assets/images/social/services/listmonk.png index f62f0a0..37e5f35 100644 Binary files a/mkdocs/site/assets/images/social/services/listmonk.png and b/mkdocs/site/assets/images/social/services/listmonk.png differ diff --git a/mkdocs/site/assets/images/social/services/map.png b/mkdocs/site/assets/images/social/services/map.png index 60d2241..1d4020a 100644 Binary files a/mkdocs/site/assets/images/social/services/map.png and b/mkdocs/site/assets/images/social/services/map.png differ diff --git a/mkdocs/site/assets/images/social/services/mini-qr.png b/mkdocs/site/assets/images/social/services/mini-qr.png index de606a1..3bf3c14 100644 Binary files a/mkdocs/site/assets/images/social/services/mini-qr.png and b/mkdocs/site/assets/images/social/services/mini-qr.png differ diff --git a/mkdocs/site/assets/images/social/services/n8n.png b/mkdocs/site/assets/images/social/services/n8n.png index 98db777..39fda7c 100644 Binary files a/mkdocs/site/assets/images/social/services/n8n.png and b/mkdocs/site/assets/images/social/services/n8n.png differ diff --git a/mkdocs/site/assets/images/social/services/nocodb.png b/mkdocs/site/assets/images/social/services/nocodb.png index 6c00bdf..bf1ff94 100644 Binary files a/mkdocs/site/assets/images/social/services/nocodb.png and b/mkdocs/site/assets/images/social/services/nocodb.png differ diff --git a/mkdocs/site/assets/images/social/services/postgresql.png b/mkdocs/site/assets/images/social/services/postgresql.png index 1a165eb..493dfe4 100644 Binary files a/mkdocs/site/assets/images/social/services/postgresql.png and b/mkdocs/site/assets/images/social/services/postgresql.png differ diff --git a/mkdocs/site/assets/images/social/services/static-server.png b/mkdocs/site/assets/images/social/services/static-server.png index 11040cd..2b6baf5 100644 Binary files a/mkdocs/site/assets/images/social/services/static-server.png and b/mkdocs/site/assets/images/social/services/static-server.png differ diff --git a/mkdocs/site/assets/images/social/test.png b/mkdocs/site/assets/images/social/test.png index d605523..da7950b 100644 Binary files a/mkdocs/site/assets/images/social/test.png and b/mkdocs/site/assets/images/social/test.png differ diff --git a/mkdocs/site/assets/repo-data/admin-changemaker.lite.json b/mkdocs/site/assets/repo-data/admin-changemaker.lite.json index d21b0ca..8c6f8ea 100644 --- a/mkdocs/site/assets/repo-data/admin-changemaker.lite.json +++ b/mkdocs/site/assets/repo-data/admin-changemaker.lite.json @@ -6,11 +6,11 @@ "language": "HTML", "stars_count": 0, "forks_count": 0, - "open_issues_count": 10, - "updated_at": "2025-07-20T10:26:01-06:00", + "open_issues_count": 8, + "updated_at": "2025-07-24T17:09:48-06:00", "created_at": "2025-05-28T14:54:59-06:00", "clone_url": "https://gitea.bnkops.com/admin/changemaker.lite.git", "ssh_url": "git@gitea.bnkops.com:admin/changemaker.lite.git", "default_branch": "main", - "last_build_update": "2025-07-20T10:26:01-06:00" + "last_build_update": "2025-07-24T17:09:48-06:00" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/anthropics-claude-code.json b/mkdocs/site/assets/repo-data/anthropics-claude-code.json index 426077d..9e2f2c4 100644 --- a/mkdocs/site/assets/repo-data/anthropics-claude-code.json +++ b/mkdocs/site/assets/repo-data/anthropics-claude-code.json @@ -4,13 +4,13 @@ "description": "Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.", "html_url": "https://github.com/anthropics/claude-code", "language": "PowerShell", - "stars_count": 24785, - "forks_count": 1360, - "open_issues_count": 2190, - "updated_at": "2025-07-20T17:36:39Z", + "stars_count": 26166, + "forks_count": 1437, + "open_issues_count": 2513, + "updated_at": "2025-07-27T00:10:38Z", "created_at": "2025-02-22T17:41:21Z", "clone_url": "https://github.com/anthropics/claude-code.git", "ssh_url": "git@github.com:anthropics/claude-code.git", "default_branch": "main", - "last_build_update": "2025-07-19T00:06:09Z" + "last_build_update": "2025-07-25T21:06:46Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/coder-code-server.json b/mkdocs/site/assets/repo-data/coder-code-server.json index 85a4000..5487e24 100644 --- a/mkdocs/site/assets/repo-data/coder-code-server.json +++ b/mkdocs/site/assets/repo-data/coder-code-server.json @@ -4,13 +4,13 @@ "description": "VS Code in the browser", "html_url": "https://github.com/coder/code-server", "language": "TypeScript", - "stars_count": 72988, - "forks_count": 6112, - "open_issues_count": 139, - "updated_at": "2025-07-20T17:46:07Z", + "stars_count": 73102, + "forks_count": 6130, + "open_issues_count": 143, + "updated_at": "2025-07-26T22:12:45Z", "created_at": "2019-02-27T16:50:41Z", "clone_url": "https://github.com/coder/code-server.git", "ssh_url": "git@github.com:coder/code-server.git", "default_branch": "main", - "last_build_update": "2025-07-17T21:36:37Z" + "last_build_update": "2025-07-24T23:16:29Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/gethomepage-homepage.json b/mkdocs/site/assets/repo-data/gethomepage-homepage.json index 9de0522..900413d 100644 --- a/mkdocs/site/assets/repo-data/gethomepage-homepage.json +++ b/mkdocs/site/assets/repo-data/gethomepage-homepage.json @@ -4,13 +4,13 @@ "description": "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.", "html_url": "https://github.com/gethomepage/homepage", "language": "JavaScript", - "stars_count": 24903, - "forks_count": 1544, - "open_issues_count": 2, - "updated_at": "2025-07-20T17:37:56Z", + "stars_count": 25016, + "forks_count": 1560, + "open_issues_count": 1, + "updated_at": "2025-07-26T22:55:16Z", "created_at": "2022-08-24T07:29:42Z", "clone_url": "https://github.com/gethomepage/homepage.git", "ssh_url": "git@github.com:gethomepage/homepage.git", "default_branch": "dev", - "last_build_update": "2025-07-20T12:13:09Z" + "last_build_update": "2025-07-27T00:42:35Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/go-gitea-gitea.json b/mkdocs/site/assets/repo-data/go-gitea-gitea.json index 9825e6f..123b2de 100644 --- a/mkdocs/site/assets/repo-data/go-gitea-gitea.json +++ b/mkdocs/site/assets/repo-data/go-gitea-gitea.json @@ -4,13 +4,13 @@ "description": "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD", "html_url": "https://github.com/go-gitea/gitea", "language": "Go", - "stars_count": 49648, - "forks_count": 5914, - "open_issues_count": 2728, - "updated_at": "2025-07-20T16:44:42Z", + "stars_count": 49740, + "forks_count": 5920, + "open_issues_count": 2736, + "updated_at": "2025-07-27T00:44:09Z", "created_at": "2016-11-01T02:13:26Z", "clone_url": "https://github.com/go-gitea/gitea.git", "ssh_url": "git@github.com:go-gitea/gitea.git", "default_branch": "main", - "last_build_update": "2025-07-20T01:49:36Z" + "last_build_update": "2025-07-27T00:44:04Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/knadh-listmonk.json b/mkdocs/site/assets/repo-data/knadh-listmonk.json index f2adfe7..3203c36 100644 --- a/mkdocs/site/assets/repo-data/knadh-listmonk.json +++ b/mkdocs/site/assets/repo-data/knadh-listmonk.json @@ -4,13 +4,13 @@ "description": "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.", "html_url": "https://github.com/knadh/listmonk", "language": "Go", - "stars_count": 17355, - "forks_count": 1673, - "open_issues_count": 98, - "updated_at": "2025-07-20T13:46:34Z", + "stars_count": 17468, + "forks_count": 1686, + "open_issues_count": 100, + "updated_at": "2025-07-26T16:53:36Z", "created_at": "2019-06-26T05:08:39Z", "clone_url": "https://github.com/knadh/listmonk.git", "ssh_url": "git@github.com:knadh/listmonk.git", "default_branch": "master", - "last_build_update": "2025-07-20T12:18:04Z" + "last_build_update": "2025-07-22T12:07:13Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/lyqht-mini-qr.json b/mkdocs/site/assets/repo-data/lyqht-mini-qr.json index 088b388..22a9592 100644 --- a/mkdocs/site/assets/repo-data/lyqht-mini-qr.json +++ b/mkdocs/site/assets/repo-data/lyqht-mini-qr.json @@ -4,10 +4,10 @@ "description": "Create & scan cute qr codes easily \ud83d\udc7e", "html_url": "https://github.com/lyqht/mini-qr", "language": "Vue", - "stars_count": 1291, - "forks_count": 172, + "stars_count": 1338, + "forks_count": 177, "open_issues_count": 13, - "updated_at": "2025-07-20T11:49:09Z", + "updated_at": "2025-07-26T18:13:59Z", "created_at": "2023-04-21T14:20:14Z", "clone_url": "https://github.com/lyqht/mini-qr.git", "ssh_url": "git@github.com:lyqht/mini-qr.git", diff --git a/mkdocs/site/assets/repo-data/n8n-io-n8n.json b/mkdocs/site/assets/repo-data/n8n-io-n8n.json index acedbfc..04da70c 100644 --- a/mkdocs/site/assets/repo-data/n8n-io-n8n.json +++ b/mkdocs/site/assets/repo-data/n8n-io-n8n.json @@ -4,13 +4,13 @@ "description": "Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.", "html_url": "https://github.com/n8n-io/n8n", "language": "TypeScript", - "stars_count": 121441, - "forks_count": 36499, - "open_issues_count": 987, - "updated_at": "2025-07-20T17:49:04Z", + "stars_count": 123887, + "forks_count": 37509, + "open_issues_count": 971, + "updated_at": "2025-07-27T00:44:16Z", "created_at": "2019-06-22T09:24:21Z", "clone_url": "https://github.com/n8n-io/n8n.git", "ssh_url": "git@github.com:n8n-io/n8n.git", "default_branch": "master", - "last_build_update": "2025-07-20T14:58:33Z" + "last_build_update": "2025-07-26T15:16:15Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/nocodb-nocodb.json b/mkdocs/site/assets/repo-data/nocodb-nocodb.json index f0662db..1d7e15b 100644 --- a/mkdocs/site/assets/repo-data/nocodb-nocodb.json +++ b/mkdocs/site/assets/repo-data/nocodb-nocodb.json @@ -4,13 +4,13 @@ "description": "\ud83d\udd25 \ud83d\udd25 \ud83d\udd25 Open Source Airtable Alternative", "html_url": "https://github.com/nocodb/nocodb", "language": "TypeScript", - "stars_count": 55879, - "forks_count": 4026, - "open_issues_count": 677, - "updated_at": "2025-07-20T17:42:16Z", + "stars_count": 56041, + "forks_count": 4048, + "open_issues_count": 686, + "updated_at": "2025-07-26T23:44:10Z", "created_at": "2017-10-29T18:51:48Z", "clone_url": "https://github.com/nocodb/nocodb.git", "ssh_url": "git@github.com:nocodb/nocodb.git", "default_branch": "develop", - "last_build_update": "2025-07-19T20:55:35Z" + "last_build_update": "2025-07-26T18:53:06Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/ollama-ollama.json b/mkdocs/site/assets/repo-data/ollama-ollama.json index 7bb683a..2068b25 100644 --- a/mkdocs/site/assets/repo-data/ollama-ollama.json +++ b/mkdocs/site/assets/repo-data/ollama-ollama.json @@ -4,13 +4,13 @@ "description": "Get up and running with Llama 3.3, DeepSeek-R1, Phi-4, Gemma 3, Mistral Small 3.1 and other large language models.", "html_url": "https://github.com/ollama/ollama", "language": "Go", - "stars_count": 147025, - "forks_count": 12459, - "open_issues_count": 1920, - "updated_at": "2025-07-20T17:32:08Z", + "stars_count": 147622, + "forks_count": 12527, + "open_issues_count": 1947, + "updated_at": "2025-07-27T00:34:59Z", "created_at": "2023-06-26T19:39:32Z", "clone_url": "https://github.com/ollama/ollama.git", "ssh_url": "git@github.com:ollama/ollama.git", "default_branch": "main", - "last_build_update": "2025-07-19T20:29:38Z" + "last_build_update": "2025-07-25T23:58:11Z" } \ No newline at end of file diff --git a/mkdocs/site/assets/repo-data/squidfunk-mkdocs-material.json b/mkdocs/site/assets/repo-data/squidfunk-mkdocs-material.json index 0829284..afabfb7 100644 --- a/mkdocs/site/assets/repo-data/squidfunk-mkdocs-material.json +++ b/mkdocs/site/assets/repo-data/squidfunk-mkdocs-material.json @@ -4,13 +4,13 @@ "description": "Documentation that simply works", "html_url": "https://github.com/squidfunk/mkdocs-material", "language": "Python", - "stars_count": 23952, - "forks_count": 3817, - "open_issues_count": 7, - "updated_at": "2025-07-20T17:36:49Z", + "stars_count": 24011, + "forks_count": 3825, + "open_issues_count": 6, + "updated_at": "2025-07-27T00:11:59Z", "created_at": "2016-01-28T22:09:23Z", "clone_url": "https://github.com/squidfunk/mkdocs-material.git", "ssh_url": "git@github.com:squidfunk/mkdocs-material.git", "default_branch": "master", - "last_build_update": "2025-07-17T21:29:23Z" + "last_build_update": "2025-07-26T15:53:16Z" } \ No newline at end of file diff --git a/mkdocs/site/blog/2025/07/03/blog-1/index.html b/mkdocs/site/blog/2025/07/03/blog-1/index.html index f020384..f4afbd4 100644 --- a/mkdocs/site/blog/2025/07/03/blog-1/index.html +++ b/mkdocs/site/blog/2025/07/03/blog-1/index.html @@ -922,12 +922,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/blog/2025/07/10/2/index.html b/mkdocs/site/blog/2025/07/10/2/index.html index e84660a..a2e23e8 100644 --- a/mkdocs/site/blog/2025/07/10/2/index.html +++ b/mkdocs/site/blog/2025/07/10/2/index.html @@ -923,12 +923,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/blog/archive/2025/index.html b/mkdocs/site/blog/archive/2025/index.html index 1fa5f74..0ecb763 100644 --- a/mkdocs/site/blog/archive/2025/index.html +++ b/mkdocs/site/blog/archive/2025/index.html @@ -923,12 +923,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/blog/index.html b/mkdocs/site/blog/index.html index 921db66..4a5218f 100644 --- a/mkdocs/site/blog/index.html +++ b/mkdocs/site/blog/index.html @@ -922,12 +922,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/build/index.html b/mkdocs/site/build/index.html index dc93bc4..2cd97e3 100644 --- a/mkdocs/site/build/index.html +++ b/mkdocs/site/build/index.html @@ -2440,12 +2440,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/build/map/index.html b/mkdocs/site/build/map/index.html index a174643..89241b7 100644 --- a/mkdocs/site/build/map/index.html +++ b/mkdocs/site/build/map/index.html @@ -2065,12 +2065,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/build/server/index.html b/mkdocs/site/build/server/index.html index 0084528..2d736a3 100644 --- a/mkdocs/site/build/server/index.html +++ b/mkdocs/site/build/server/index.html @@ -2245,12 +2245,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/build/site/index.html b/mkdocs/site/build/site/index.html index 5335297..8893f7a 100644 --- a/mkdocs/site/build/site/index.html +++ b/mkdocs/site/build/site/index.html @@ -1852,12 +1852,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/config/cloudflare-config/index.html b/mkdocs/site/config/cloudflare-config/index.html index 222c71d..297b39e 100644 --- a/mkdocs/site/config/cloudflare-config/index.html +++ b/mkdocs/site/config/cloudflare-config/index.html @@ -1851,12 +1851,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/config/coder/index.html b/mkdocs/site/config/coder/index.html index 2a2ecc3..154fd8e 100644 --- a/mkdocs/site/config/coder/index.html +++ b/mkdocs/site/config/coder/index.html @@ -2323,12 +2323,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/config/index.html b/mkdocs/site/config/index.html index 511eef4..8498aba 100644 --- a/mkdocs/site/config/index.html +++ b/mkdocs/site/config/index.html @@ -1599,12 +1599,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/config/map/index.html b/mkdocs/site/config/map/index.html index ddb613c..29c9159 100644 --- a/mkdocs/site/config/map/index.html +++ b/mkdocs/site/config/map/index.html @@ -2984,12 +2984,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/config/mkdocs/index.html b/mkdocs/site/config/mkdocs/index.html index d7b622c..789197a 100644 --- a/mkdocs/site/config/mkdocs/index.html +++ b/mkdocs/site/config/mkdocs/index.html @@ -1950,12 +1950,10 @@ All repository stats are fetched at build time for performance and reliability.<
-
- - + diff --git a/mkdocs/site/how to/canvass/index.html b/mkdocs/site/how to/canvass/index.html index d2e2c69..ee89674 100644 --- a/mkdocs/site/how to/canvass/index.html +++ b/mkdocs/site/how to/canvass/index.html @@ -725,12 +725,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/index.html b/mkdocs/site/index.html index bbf391b..aecc973 100644 --- a/mkdocs/site/index.html +++ b/mkdocs/site/index.html @@ -5,1647 +5,2110 @@ Changemaker Lite - Campaign Power Tools + } + - -
- +
- -
-
-
🚀 Campaign Documentation Reimagined
-

Power Tools for Modern Campaign Documentation

-

- Give your canvassers instant answers at the door. Turn your campaign website & knowledge into a searchable, - mobile-first documentation system that actually works in the field. Your data, your servers, your control. -

-
- - Get Started - - - Explore Tools + +
+
+
🚀 Hardware Up This Site Served by Changemaker - Lite
+

Power Tools for Modern Campaign Documentation

+

+ Give your supporters instant answers at the door, on the phone, or in person. Turn your campaign website & knowledge into a searchable, + mobile-first documentation system that actually works in the field or at the party. No corporate middlemen; your data, your servers, your platform. +

+ + + +
- - -
-
-
-

Your Canvassers Are Struggling

-

Traditional campaign tools weren't built for the reality of door-to-door work

+ +
+
+
100%
+
Local Data Ownership
-
-
-
📱
-

Can't Find Answers Fast

-

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
$0
+
Free to Self-Host
+
+
+
Canadian
+
Built by Locals
+
+
+
Open-Source
+
Built for Campaigns
+
+
+
+
+
+ + +
+
+
+

Your Canvassers Are Struggling

+

Traditional campaign tools weren't built for the reality of door-to-door work

+
+
+
+
📱
+

Can't Find Answers Fast

+

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
+
🗺️
+

Disconnected Data

+

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+
+
+
💸
+

Death by Subscription

+

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+
+
🔒
+

No Data Control

+

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+
+
+
📵
+

Not Mobile-Ready

+

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+
+
🏢
+

Foreign Dependencies

+

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+
+
+
+
+ + +
+
+
+

Documentation That Works at the Door

+

Everything your team needs, instantly searchable, always accessible

+
+ +
+
+

Mobile-First Everything

+

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

+
    +
  • Touch-optimized interfaces
  • +
  • Offline-capable after first load
  • +
  • Simple, easy to read, and clear buttons
  • +
  • One-thumb navigation
  • +
  • Instant load times
  • +
+
+
+ Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results +
+
+ +
+
+

Your Data, Your Servers, Your Rules

+

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

+
    +
  • 100% self-hosted infrastructure
  • +
  • Canadian data residency
  • +
  • Complete export capabilities
  • +
  • No vendor lock-in ever
  • +
  • Privacy-first architecture
  • +
+
+
+ Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings +
+
+ +
+
+

Instant Search Everything

+

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

+
    +
  • Full-text search across all documentation
  • +
  • Smart categorization and tagging
  • +
  • Works offline after first load
  • +
  • Mobile-optimized interface
  • +
+
+
+ Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons +
+
+ +
+
+

Interactive Canvassing Maps

+

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

+
    +
  • Real-time location tracking
  • +
  • Color-coded support levels
  • +
  • Add notes directly from the field
  • +
  • Track door knocks & interactions
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+

Living Documentation

+

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

+
    +
  • Real-time collaborative editing
  • +
  • Version control and history
  • +
  • Automatic mobile optimization
  • +
  • Beautiful, branded output
  • +
  • Thousands of plugins available
  • +
  • Local AI
  • +
+
+
+ Split view showing markdown editor on left, live preview on right with campaign branding +
+
+ +
+
+

Beautiul Websites

+

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

+
    +
  • Social media cards
  • +
  • Fully customizable
  • +
  • Custom pages and landers
  • +
  • Integrated blogging
  • +
  • Supports 60+ languages
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+ + +
+
+
+

Your Complete Campaign Power Tool Suite

+

Everything works together. No integrations needed. No monthly fees.

+
+ +
+
+
+
📝
+
+

Smart Documentation Hub

+
MkDocs + Code Server
+ mkdocs/docs/blog
-
-
🗺️
-

Disconnected Data

-

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+

Create beautiful, searchable documentation that your team will actually use.

+
    +
  • Instant search across everything
  • +
  • Mobile-first responsive design
  • +
  • Automatic table of contents
  • +
  • Deep linking to any section
  • +
+ -
-
💸
-

Death by Subscription

-

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+ +
+
+
🗺️
+
+

BNKops Map

+
Interactive Canvassing System
+
-
-
🔒
-

No Data Control

-

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+

Turn voter data into visual intelligence your canvassers can use.

+
    +
  • Real-time GPS tracking
  • +
  • Support level heat maps
  • +
  • Instant data entry
  • +
  • Offline-capable
  • +
+ -
-
📵
-

Not Mobile-Ready

-

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+ +
+
+
📊
+
+

Voter Database

+
NocoDB Spreadsheet Interface
+
-
-
🏢
-

Foreign Dependencies

-

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+

Manage voter data like a spreadsheet, access it like a database.

+
    +
  • Familiar Excel-like interface
  • +
  • Custom forms for data entry
  • +
  • Advanced filtering & search
  • +
  • API access for automation
  • +
+ +
+ +
+
+
📧
+
+

Email Command Center

+
Listmonk Email Platform
+
+
+

Professional email campaigns without the professional price tag.

+
    +
  • Unlimited subscribers
  • +
  • Beautiful templates
  • +
  • Open & click tracking
  • +
  • Automated sequences
  • +
+ +
+ +
+
+
🤖
+
+

Campaign Automation

+
n8n Workflow Engine
+
+
+

Automate repetitive tasks so your team can focus on voters.

+
    +
  • Visual workflow builder
  • +
  • Connect any service
  • +
  • Trigger-based automation
  • +
  • No coding required
  • +
+ +
+ +
+
+
🗃️
+
+

Version Control

+
Gitea Repository
+
+
+

Track changes, collaborate safely, and never lose work again.

+
    +
  • Full version history
  • +
  • Collaborative editing
  • +
  • Backup everything
  • +
  • Roll back changes
  • +
+
-
- - -
-
-
-

Documentation That Works at the Door

-

Everything your team needs, instantly searchable, always accessible

-
- -
-
-

Mobile-First Everything

-

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

-
    -
  • Touch-optimized interfaces
  • -
  • Offline-capable after first load
  • -
  • Simple, easy to read, and clear buttons
  • -
  • One-thumb navigation
  • -
  • Instant load times
  • -
-
-
- Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results -
-
- -
-
-

Your Data, Your Servers, Your Rules

-

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

-
    -
  • 100% self-hosted infrastructure
  • -
  • Canadian data residency
  • -
  • Complete export capabilities
  • -
  • No vendor lock-in ever
  • -
  • Privacy-first architecture
  • -
-
-
- Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings -
-
- -
-
-

Instant Search Everything

-

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

-
    -
  • Full-text search across all documentation
  • -
  • Smart categorization and tagging
  • -
  • Works offline after first load
  • -
  • Mobile-optimized interface
  • -
-
-
- Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons -
-
- -
-
-

Interactive Canvassing Maps

-

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

-
    -
  • Real-time location tracking
  • -
  • Color-coded support levels
  • -
  • Add notes directly from the field
  • -
  • Track door knocks & interactions
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
- -
-
-

Living Documentation

-

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

-
    -
  • Real-time collaborative editing
  • -
  • Version control and history
  • -
  • Automatic mobile optimization
  • -
  • Beautiful, branded output
  • -
  • Thousands of plugins available
  • -
  • Local AI
  • -
-
-
- Split view showing markdown editor on left, live preview on right with campaign branding -
-
- -
-
-

Beautiul Websites

-

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

-
    -
  • Social media cards
  • -
  • Fully customizable
  • -
  • Custom pages and landers
  • -
  • Integrated blogging
  • -
  • Supports 60+ languages
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
+
+
+ +
+
+
+

Canadian Tech for Canadian Campaigns

+

Why trust your movement's future to foreign corporations?

-
- - -
-
-
-

Your Complete Campaign Power Tool Suite

-

Everything works together. No integrations needed. No monthly fees.

+ +
+
+
🇨🇦
+

100% Canadian

+

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

- -
-
-
-
📝
-
-

Smart Documentation Hub

-
MkDocs + Code Server
- mkdocs/docs/blog
-
-

Create beautiful, searchable documentation that your team will actually use.

-
    -
  • Instant search across everything
  • -
  • Mobile-first responsive design
  • -
  • Automatic table of contents
  • -
  • Deep linking to any section
  • +
    +
    🔐
    +

    True Ownership

    +

    Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

    +
    +
    +
    🛡️
    +

    Privacy First

    +

    Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

    +
    +
+ +
+

The Sovereignty Difference

+
+
+

US Corporate Platforms

+
    +
  • ❌ Subject to Patriot Act
  • +
  • ❌ NSA surveillance
  • +
  • ❌ Corporate data mining
  • +
  • ❌ Foreign jurisdiction
-
- -
-
-
🗺️
-
-

BNKops Map

-
Interactive Canvassing System
-
-
-

Turn voter data into visual intelligence your canvassers can use.

-
    -
  • Real-time GPS tracking
  • -
  • Support level heat maps
  • -
  • Instant data entry
  • -
  • Offline-capable
  • +
    +

    Changemaker Lite

    +
      +
    • ✅ Canadian sovereignty
    • +
    • ✅ Zero surveillance
    • +
    • ✅ Complete privacy
    • +
    • ✅ Your servers, your rules
    - -
    - -
    -
    -
    📊
    -
    -

    Voter Database

    -
    NocoDB Spreadsheet Interface
    -
    -
    -

    Manage voter data like a spreadsheet, access it like a database.

    -
      -
    • Familiar Excel-like interface
    • -
    • Custom forms for data entry
    • -
    • Advanced filtering & search
    • -
    • API access for automation
    • -
    - -
    - -
    -
    -
    📧
    -
    -

    Email Command Center

    -
    Listmonk Email Platform
    -
    -
    -

    Professional email campaigns without the professional price tag.

    -
      -
    • Unlimited subscribers
    • -
    • Beautiful templates
    • -
    • Open & click tracking
    • -
    • Automated sequences
    • -
    - -
    - -
    -
    -
    🤖
    -
    -

    Campaign Automation

    -
    n8n Workflow Engine
    -
    -
    -

    Automate repetitive tasks so your team can focus on voters.

    -
      -
    • Visual workflow builder
    • -
    • Connect any service
    • -
    • Trigger-based automation
    • -
    • No coding required
    • -
    - -
    - -
    -
    -
    🗃️
    -
    -

    Version Control

    -
    Gitea Repository
    -
    -
    -

    Track changes, collaborate safely, and never lose work again.

    -
      -
    • Full version history
    • -
    • Collaborative editing
    • -
    • Backup everything
    • -
    • Roll back changes
    • -
    -
-
+
+
- -
-
-
-

Canadian Tech for Canadian Campaigns

-

Why trust your movement's future to foreign corporations?

+ +
+
+
+

Simple, Transparent Pricing

+

No hidden fees. No usage limits. No surprises.

+
+ +
+
+
+

Self-Hosted

+
$0
+
forever
+
+
    +
  • ✓ All 11 campaign tools
  • +
  • ✓ Unlimited users
  • +
  • ✓ Unlimited data
  • +
  • ✓ Complete documentation
  • +
  • ✓ Community support
  • +
  • ✓ Your infrastructure
  • +
  • ✓ 100% open source
  • +
+ Installation Guide +

Perfect for tech-savvy campaigns

-
-
-
🇨🇦
-

100% Canadian

-

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

-
-
-
🔐
-

True Ownership

-

Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

-
-
-
🛡️
-

Privacy First

-

Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

+ -
-

The Sovereignty Difference

-
-
-

US Corporate Platforms

-
    -
  • ❌ Subject to Patriot Act
  • -
  • ❌ NSA surveillance
  • -
  • ❌ Corporate data mining
  • -
  • ❌ Foreign jurisdiction
  • -
-
-
-

Changemaker Lite

-
    -
  • ✅ Canadian sovereignty
  • -
  • ✅ Zero surveillance
  • -
  • ✅ Complete privacy
  • -
  • ✅ Your servers, your rules
  • -
-
+
+
+

Managed Hosting

+
Custom
+
monthly
+
    +
  • ✓ We handle everything
  • +
  • ✓ Canadian data centers
  • +
  • ✓ Daily backups
  • +
  • ✓ 24/7 monitoring
  • +
  • ✓ Security updates
  • +
  • ✓ Priority support
  • +
  • ✓ Still your data
  • +
+ Contact Sales +

For larger campaigns

-
- - -
-
-
-

Simple, Transparent Pricing

-

No hidden fees. No usage limits. No surprises.

-
- -
-
-
-

Self-Hosted

-
$0
-
forever
-
-
    -
  • ✓ All 11 campaign tools
  • -
  • ✓ Unlimited users
  • -
  • ✓ Unlimited data
  • -
  • ✓ Complete documentation
  • -
  • ✓ Community support
  • -
  • ✓ Your infrastructure
  • -
  • ✓ 100% open source
  • -
- Installation Guide -

Perfect for tech-savvy campaigns

-
- - - -
-
-

Managed Hosting

-
Custom
-
monthly
-
-
    -
  • ✓ We handle everything
  • -
  • ✓ Canadian data centers
  • -
  • ✓ Daily backups
  • -
  • ✓ 24/7 monitoring
  • -
  • ✓ Security updates
  • -
  • ✓ Priority support
  • -
  • ✓ Still your data
  • -
- Contact Sales -

For larger campaigns

-
-
- -
-

Compare Your Savings

-

Average campaign using corporate tools: $1,200-$4,000/month

-

Same capabilities with Changemaker Lite: $0 (self-hosted)

- See detailed cost breakdown → -
+ +
+

Compare Your Savings

+

Average campaign using corporate tools: $1,200-$4,000/month

+

Same capabilities with Changemaker Lite: $0 (self-hosted)

+ See detailed cost breakdown →
-
+
+
- -
-
-
-

Everything Works Together

-

One login. One system. Infinite possibilities.

-
- -
-
-
- All systems communicate and build on one another -
-
-

- 🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees -

-
+ +
+
+
+

Everything Works Together

+

One login. One system. Infinite possibilities.

-
- - -
-
- - -
-
-

Ready to Power Up Your Campaign?

-

Join hundreds of campaigns using open-source tools to win elections and save money.

-

🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees

-
+
+
- + + + + // Observe all cards and sections + document.querySelectorAll('.problem-card, .tool-card, .solution-showcase, .testimonial-card').forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(20px)'; + el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + observer.observe(el); + }); + \ No newline at end of file diff --git a/mkdocs/site/manual/index.html b/mkdocs/site/manual/index.html index 79c5f30..1c6dac3 100644 --- a/mkdocs/site/manual/index.html +++ b/mkdocs/site/manual/index.html @@ -1598,12 +1598,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/manual/map/index.html b/mkdocs/site/manual/map/index.html index c271bf0..74d9677 100644 --- a/mkdocs/site/manual/map/index.html +++ b/mkdocs/site/manual/map/index.html @@ -111,7 +111,7 @@
- + Skip to content @@ -1288,9 +1288,192 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/overrides/lander.html b/mkdocs/site/overrides/lander.html index bbf391b..aecc973 100644 --- a/mkdocs/site/overrides/lander.html +++ b/mkdocs/site/overrides/lander.html @@ -5,1647 +5,2110 @@ Changemaker Lite - Campaign Power Tools + } + - -
- +
- -
-
-
🚀 Campaign Documentation Reimagined
-

Power Tools for Modern Campaign Documentation

-

- Give your canvassers instant answers at the door. Turn your campaign website & knowledge into a searchable, - mobile-first documentation system that actually works in the field. Your data, your servers, your control. -

-
- - Get Started - - - Explore Tools + +
+
+
🚀 Hardware Up This Site Served by Changemaker - Lite
+

Power Tools for Modern Campaign Documentation

+

+ Give your supporters instant answers at the door, on the phone, or in person. Turn your campaign website & knowledge into a searchable, + mobile-first documentation system that actually works in the field or at the party. No corporate middlemen; your data, your servers, your platform. +

+ + + +
- - -
-
-
-

Your Canvassers Are Struggling

-

Traditional campaign tools weren't built for the reality of door-to-door work

+ +
+
+
100%
+
Local Data Ownership
-
-
-
📱
-

Can't Find Answers Fast

-

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
$0
+
Free to Self-Host
+
+
+
Canadian
+
Built by Locals
+
+
+
Open-Source
+
Built for Campaigns
+
+
+
+
+
+ + +
+
+
+

Your Canvassers Are Struggling

+

Traditional campaign tools weren't built for the reality of door-to-door work

+
+
+
+
📱
+

Can't Find Answers Fast

+

Voters ask tough questions. Your team fumbles through PDFs, emails, and scattered Google Docs while the voter loses interest.

+
+
+
🗺️
+

Disconnected Data

+

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+
+
+
💸
+

Death by Subscription

+

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+
+
🔒
+

No Data Control

+

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+
+
+
📵
+

Not Mobile-Ready

+

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+
+
🏢
+

Foreign Dependencies

+

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+
+
+
+
+ + +
+
+
+

Documentation That Works at the Door

+

Everything your team needs, instantly searchable, always accessible

+
+ +
+
+

Mobile-First Everything

+

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

+
    +
  • Touch-optimized interfaces
  • +
  • Offline-capable after first load
  • +
  • Simple, easy to read, and clear buttons
  • +
  • One-thumb navigation
  • +
  • Instant load times
  • +
+
+
+ Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results +
+
+ +
+
+

Your Data, Your Servers, Your Rules

+

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

+
    +
  • 100% self-hosted infrastructure
  • +
  • Canadian data residency
  • +
  • Complete export capabilities
  • +
  • No vendor lock-in ever
  • +
  • Privacy-first architecture
  • +
+
+
+ Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings +
+
+ +
+
+

Instant Search Everything

+

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

+
    +
  • Full-text search across all documentation
  • +
  • Smart categorization and tagging
  • +
  • Works offline after first load
  • +
  • Mobile-optimized interface
  • +
+
+
+ Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons +
+
+ +
+
+

Interactive Canvassing Maps

+

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

+
    +
  • Real-time location tracking
  • +
  • Color-coded support levels
  • +
  • Add notes directly from the field
  • +
  • Track door knocks & interactions
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+

Living Documentation

+

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

+
    +
  • Real-time collaborative editing
  • +
  • Version control and history
  • +
  • Automatic mobile optimization
  • +
  • Beautiful, branded output
  • +
  • Thousands of plugins available
  • +
  • Local AI
  • +
+
+
+ Split view showing markdown editor on left, live preview on right with campaign branding +
+
+ +
+
+

Beautiul Websites

+

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

+
    +
  • Social media cards
  • +
  • Fully customizable
  • +
  • Custom pages and landers
  • +
  • Integrated blogging
  • +
  • Supports 60+ languages
  • +
+
+
+ Map interface showing color-coded houses, with popup showing voter details and previous interaction history +
+
+ +
+
+ + +
+
+
+

Your Complete Campaign Power Tool Suite

+

Everything works together. No integrations needed. No monthly fees.

+
+ +
+
+
+
📝
+
+

Smart Documentation Hub

+
MkDocs + Code Server
+ mkdocs/docs/blog
-
-
🗺️
-

Disconnected Data

-

Walk lists in one app, voter info in another, campaign policies somewhere else. Nothing talks to each other.

+

Create beautiful, searchable documentation that your team will actually use.

+
    +
  • Instant search across everything
  • +
  • Mobile-first responsive design
  • +
  • Automatic table of contents
  • +
  • Deep linking to any section
  • +
+ -
-
💸
-

Death by Subscription

-

$100 here, $500 there. Before you know it, you're spending thousands monthly on tools that don't even work together.

+
+ +
+
+
🗺️
+
+

BNKops Map

+
Interactive Canvassing System
+
-
-
🔒
-

No Data Control

-

Your data is locked behind expensive subscriptions. Your strategies in corporate clouds. Your movement's future in someone else's hands.

+

Turn voter data into visual intelligence your canvassers can use.

+
    +
  • Real-time GPS tracking
  • +
  • Support level heat maps
  • +
  • Instant data entry
  • +
  • Offline-capable
  • +
+ -
-
📵
-

Not Mobile-Ready

-

Desktop-first tools that barely work on phones. Canvassers struggling with tiny text and broken interfaces.

+
+ +
+
+
📊
+
+

Voter Database

+
NocoDB Spreadsheet Interface
+
-
-
🏢
-

Foreign Dependencies

-

US companies with US regulations. Your Canadian campaign data subject to foreign laws and surveillance.

+

Manage voter data like a spreadsheet, access it like a database.

+
    +
  • Familiar Excel-like interface
  • +
  • Custom forms for data entry
  • +
  • Advanced filtering & search
  • +
  • API access for automation
  • +
+ +
+ +
+
+
📧
+
+

Email Command Center

+
Listmonk Email Platform
+
+
+

Professional email campaigns without the professional price tag.

+
    +
  • Unlimited subscribers
  • +
  • Beautiful templates
  • +
  • Open & click tracking
  • +
  • Automated sequences
  • +
+ +
+ +
+
+
🤖
+
+

Campaign Automation

+
n8n Workflow Engine
+
+
+

Automate repetitive tasks so your team can focus on voters.

+
    +
  • Visual workflow builder
  • +
  • Connect any service
  • +
  • Trigger-based automation
  • +
  • No coding required
  • +
+ +
+ +
+
+
🗃️
+
+

Version Control

+
Gitea Repository
+
+
+

Track changes, collaborate safely, and never lose work again.

+
    +
  • Full version history
  • +
  • Collaborative editing
  • +
  • Backup everything
  • +
  • Roll back changes
  • +
+
-
- - -
-
-
-

Documentation That Works at the Door

-

Everything your team needs, instantly searchable, always accessible

-
- -
-
-

Mobile-First Everything

-

Built for phones first, because that's what your canvassers carry. Every feature, every interface, optimized for one-handed use in the field.

-
    -
  • Touch-optimized interfaces
  • -
  • Offline-capable after first load
  • -
  • Simple, easy to read, and clear buttons
  • -
  • One-thumb navigation
  • -
  • Instant load times
  • -
-
-
- Phone showing mobile-optimized interface with large touch targets, clear typography, and instant search results -
-
- -
-
-

Your Data, Your Servers, Your Rules

-

Complete Data Ownership. Run it on Canadian soil, in your office, or anywhere you trust. No foreign surveillance, no corporate access, no compromises.

-
    -
  • 100% self-hosted infrastructure
  • -
  • Canadian data residency
  • -
  • Complete export capabilities
  • -
  • No vendor lock-in ever
  • -
  • Privacy-first architecture
  • -
-
-
- Server dashboard showing Canadian hosting location, data ownership controls, and privacy settings -
-
- -
-
-

Instant Search Everything

-

Your entire campaign knowledge base at your fingertips. Policy positions, talking points, FAQs - all searchable in milliseconds.

-
    -
  • Full-text search across all documentation
  • -
  • Smart categorization and tagging
  • -
  • Works offline after first load
  • -
  • Mobile-optimized interface
  • -
-
-
- Mobile view showing instant search results for healthcare policy with highlighted snippets and quick access buttons -
-
- -
-
-

Interactive Canvassing Maps

-

See everything about a neighborhood before you knock. Previous interactions, support levels, local issues - all on one map.

-
    -
  • Real-time location tracking
  • -
  • Color-coded support levels
  • -
  • Add notes directly from the field
  • -
  • Track door knocks & interactions
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
- -
-
-

Living Documentation

-

Your campaign evolves daily. Your documentation should too. Update once, everyone gets it instantly.

-
    -
  • Real-time collaborative editing
  • -
  • Version control and history
  • -
  • Automatic mobile optimization
  • -
  • Beautiful, branded output
  • -
  • Thousands of plugins available
  • -
  • Local AI
  • -
-
-
- Split view showing markdown editor on left, live preview on right with campaign branding -
-
- -
-
-

Beautiul Websites

-

Build and deploy beautiful websites & documentation using the tools already used by the worlds largest organizations.

-
    -
  • Social media cards
  • -
  • Fully customizable
  • -
  • Custom pages and landers
  • -
  • Integrated blogging
  • -
  • Supports 60+ languages
  • -
-
-
- Map interface showing color-coded houses, with popup showing voter details and previous interaction history -
-
+
+
+ +
+
+
+

Canadian Tech for Canadian Campaigns

+

Why trust your movement's future to foreign corporations?

-
- - -
-
-
-

Your Complete Campaign Power Tool Suite

-

Everything works together. No integrations needed. No monthly fees.

+ +
+
+
🇨🇦
+

100% Canadian

+

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

- -
-
-
-
📝
-
-

Smart Documentation Hub

-
MkDocs + Code Server
- mkdocs/docs/blog
-
-

Create beautiful, searchable documentation that your team will actually use.

-
    -
  • Instant search across everything
  • -
  • Mobile-first responsive design
  • -
  • Automatic table of contents
  • -
  • Deep linking to any section
  • +
    +
    🔐
    +

    True Ownership

    +

    Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

    +
    +
    +
    🛡️
    +

    Privacy First

    +

    Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

    +
    +
+ +
+

The Sovereignty Difference

+
+
+

US Corporate Platforms

+
    +
  • ❌ Subject to Patriot Act
  • +
  • ❌ NSA surveillance
  • +
  • ❌ Corporate data mining
  • +
  • ❌ Foreign jurisdiction
-
- -
-
-
🗺️
-
-

BNKops Map

-
Interactive Canvassing System
-
-
-

Turn voter data into visual intelligence your canvassers can use.

-
    -
  • Real-time GPS tracking
  • -
  • Support level heat maps
  • -
  • Instant data entry
  • -
  • Offline-capable
  • +
    +

    Changemaker Lite

    +
      +
    • ✅ Canadian sovereignty
    • +
    • ✅ Zero surveillance
    • +
    • ✅ Complete privacy
    • +
    • ✅ Your servers, your rules
    - -
    - -
    -
    -
    📊
    -
    -

    Voter Database

    -
    NocoDB Spreadsheet Interface
    -
    -
    -

    Manage voter data like a spreadsheet, access it like a database.

    -
      -
    • Familiar Excel-like interface
    • -
    • Custom forms for data entry
    • -
    • Advanced filtering & search
    • -
    • API access for automation
    • -
    - -
    - -
    -
    -
    📧
    -
    -

    Email Command Center

    -
    Listmonk Email Platform
    -
    -
    -

    Professional email campaigns without the professional price tag.

    -
      -
    • Unlimited subscribers
    • -
    • Beautiful templates
    • -
    • Open & click tracking
    • -
    • Automated sequences
    • -
    - -
    - -
    -
    -
    🤖
    -
    -

    Campaign Automation

    -
    n8n Workflow Engine
    -
    -
    -

    Automate repetitive tasks so your team can focus on voters.

    -
      -
    • Visual workflow builder
    • -
    • Connect any service
    • -
    • Trigger-based automation
    • -
    • No coding required
    • -
    - -
    - -
    -
    -
    🗃️
    -
    -

    Version Control

    -
    Gitea Repository
    -
    -
    -

    Track changes, collaborate safely, and never lose work again.

    -
      -
    • Full version history
    • -
    • Collaborative editing
    • -
    • Backup everything
    • -
    • Roll back changes
    • -
    -
-
+
+
- -
-
-
-

Canadian Tech for Canadian Campaigns

-

Why trust your movement's future to foreign corporations?

+ +
+
+
+

Simple, Transparent Pricing

+

No hidden fees. No usage limits. No surprises.

+
+ +
+
+
+

Self-Hosted

+
$0
+
forever
+
+
    +
  • ✓ All 11 campaign tools
  • +
  • ✓ Unlimited users
  • +
  • ✓ Unlimited data
  • +
  • ✓ Complete documentation
  • +
  • ✓ Community support
  • +
  • ✓ Your infrastructure
  • +
  • ✓ 100% open source
  • +
+ Installation Guide +

Perfect for tech-savvy campaigns

-
-
-
🇨🇦
-

100% Canadian

-

Built in Edmonton, Alberta. Supported by Canadian developers. Hosted on Canadian soil. Subject only to Canadian law.

-
-
-
🔐
-

True Ownership

-

Your data never leaves your control. Export everything anytime. No algorithms, no surveillance, no corporate oversight.

-
-
-
🛡️
-

Privacy First

-

Built to respect privacy from day one. Your supporters' data protected by design, not by policy.

+ -
-

The Sovereignty Difference

-
-
-

US Corporate Platforms

-
    -
  • ❌ Subject to Patriot Act
  • -
  • ❌ NSA surveillance
  • -
  • ❌ Corporate data mining
  • -
  • ❌ Foreign jurisdiction
  • -
-
-
-

Changemaker Lite

-
    -
  • ✅ Canadian sovereignty
  • -
  • ✅ Zero surveillance
  • -
  • ✅ Complete privacy
  • -
  • ✅ Your servers, your rules
  • -
-
+
+
+

Managed Hosting

+
Custom
+
monthly
+
    +
  • ✓ We handle everything
  • +
  • ✓ Canadian data centers
  • +
  • ✓ Daily backups
  • +
  • ✓ 24/7 monitoring
  • +
  • ✓ Security updates
  • +
  • ✓ Priority support
  • +
  • ✓ Still your data
  • +
+ Contact Sales +

For larger campaigns

-
- - -
-
-
-

Simple, Transparent Pricing

-

No hidden fees. No usage limits. No surprises.

-
- -
-
-
-

Self-Hosted

-
$0
-
forever
-
-
    -
  • ✓ All 11 campaign tools
  • -
  • ✓ Unlimited users
  • -
  • ✓ Unlimited data
  • -
  • ✓ Complete documentation
  • -
  • ✓ Community support
  • -
  • ✓ Your infrastructure
  • -
  • ✓ 100% open source
  • -
- Installation Guide -

Perfect for tech-savvy campaigns

-
- - - -
-
-

Managed Hosting

-
Custom
-
monthly
-
-
    -
  • ✓ We handle everything
  • -
  • ✓ Canadian data centers
  • -
  • ✓ Daily backups
  • -
  • ✓ 24/7 monitoring
  • -
  • ✓ Security updates
  • -
  • ✓ Priority support
  • -
  • ✓ Still your data
  • -
- Contact Sales -

For larger campaigns

-
-
- -
-

Compare Your Savings

-

Average campaign using corporate tools: $1,200-$4,000/month

-

Same capabilities with Changemaker Lite: $0 (self-hosted)

- See detailed cost breakdown → -
+ +
+

Compare Your Savings

+

Average campaign using corporate tools: $1,200-$4,000/month

+

Same capabilities with Changemaker Lite: $0 (self-hosted)

+ See detailed cost breakdown →
-
+
+
- -
-
-
-

Everything Works Together

-

One login. One system. Infinite possibilities.

-
- -
-
-
- All systems communicate and build on one another -
-
-

- 🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees -

-
+ +
+
+
+

Everything Works Together

+

One login. One system. Infinite possibilities.

-
- - -
-
- - -
-
-

Ready to Power Up Your Campaign?

-

Join hundreds of campaigns using open-source tools to win elections and save money.

-

🎯 30-minute setup • 🔒 Your data stays yours • 🚀 No monthly fees

-
+
+
- + + + + // Observe all cards and sections + document.querySelectorAll('.problem-card, .tool-card, .solution-showcase, .testimonial-card').forEach(el => { + el.style.opacity = '0'; + el.style.transform = 'translateY(20px)'; + el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; + observer.observe(el); + }); + \ No newline at end of file diff --git a/mkdocs/site/phil/cost-comparison/index.html b/mkdocs/site/phil/cost-comparison/index.html index d4bffab..30bcec1 100644 --- a/mkdocs/site/phil/cost-comparison/index.html +++ b/mkdocs/site/phil/cost-comparison/index.html @@ -2032,12 +2032,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/phil/index.html b/mkdocs/site/phil/index.html index cd6a105..7a05fad 100644 --- a/mkdocs/site/phil/index.html +++ b/mkdocs/site/phil/index.html @@ -1418,12 +1418,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/search/search_index.json b/mkdocs/site/search/search_index.json index a0bf76e..b438278 100644 --- a/mkdocs/site/search/search_index.json +++ b/mkdocs/site/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\u200b\\-_,:!=\\[\\]()\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Changemaker Lite","text":"

Stop feeding your secrets to corporations. Own your political infrastructure.

"},{"location":"#quick-start","title":"Quick Start","text":"

Get up and running in minutes:

# Clone the repository\ngit clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n\n# Configure environment\n./config.sh\n\n# Start all services\ndocker compose up -d\n\n# For production deployment with Cloudflare tunnels\n./start-production.sh\n
"},{"location":"#services","title":"Services","text":"

Changemaker Lite includes these essential services:

"},{"location":"#core-services","title":"Core Services","text":""},{"location":"#communication-automation","title":"Communication & Automation","text":""},{"location":"#data-development","title":"Data & Development","text":""},{"location":"#interactive-tools","title":"Interactive Tools","text":""},{"location":"#getting-started","title":"Getting Started","text":"
  1. Setup: Run ./config.sh to configure your environment
  2. Launch: Start services with docker compose up -d
  3. Dashboard: Access the Homepage at http://localhost:3010
  4. Production: Deploy with Cloudflare tunnels using ./start-production.sh
"},{"location":"#project-structure","title":"Project Structure","text":"
changemaker.lite/\n\u251c\u2500\u2500 docker-compose.yml    # Service definitions\n\u251c\u2500\u2500 config.sh            # Configuration wizard\n\u251c\u2500\u2500 start-production.sh  # Production deployment script\n\u251c\u2500\u2500 mkdocs/              # Documentation source\n\u2502   \u251c\u2500\u2500 docs/            # Markdown files\n\u2502   \u2514\u2500\u2500 mkdocs.yml       # MkDocs configuration\n\u251c\u2500\u2500 configs/             # Service configurations\n\u2502   \u251c\u2500\u2500 homepage/        # Homepage dashboard config\n\u2502   \u251c\u2500\u2500 code-server/     # VS Code settings\n\u2502   \u2514\u2500\u2500 cloudflare/      # Tunnel configurations\n\u251c\u2500\u2500 map/                 # Map application\n\u2502   \u251c\u2500\u2500 app/             # Node.js application\n\u2502   \u251c\u2500\u2500 Dockerfile       # Container definition\n\u2502   \u2514\u2500\u2500 .env             # Map configuration\n\u2514\u2500\u2500 assets/              # Shared assets\n    \u251c\u2500\u2500 images/          # Image files\n    \u251c\u2500\u2500 icons/           # Service icons\n    \u2514\u2500\u2500 uploads/         # Listmonk uploads\n
"},{"location":"#key-features","title":"Key Features","text":""},{"location":"#system-requirements","title":"System Requirements","text":""},{"location":"#learn-more","title":"Learn More","text":""},{"location":"test/","title":"Test","text":"

lololol

okay well just doing some fast writing because why the heck not.

\"I would ask for an apology from the city (Municipality of Jasper) as a result,\"

lololol

"},{"location":"adv/","title":"Advanced Configurations","text":"

We are also publishing how BNKops does several advanced workflows. These include things like assembling hardware, how to manage a network, how to manage several changemakers simultaneously, and integrating AI.

"},{"location":"adv/ansible/","title":"Setting Up Ansible with Tailscale for Remote Server Management","text":""},{"location":"adv/ansible/#overview","title":"Overview","text":"

This guide walks you through setting up Ansible to manage remote servers (like ThinkCentre units) using Tailscale for secure networking. This approach provides reliable remote access without complex port forwarding or VPN configurations.

In plainer language; this allows you to manage several Changemaker nodes remotely. If you are a full time campaigner, this can enable you to manage several campaigns infrastructure from a central location while each user gets their own Changemaker box.

"},{"location":"adv/ansible/#what-youll-learn","title":"What You'll Learn","text":""},{"location":"adv/ansible/#prerequisites","title":"Prerequisites","text":""},{"location":"adv/ansible/#part-1-initial-setup-on-master-node","title":"Part 1: Initial Setup on Master Node","text":""},{"location":"adv/ansible/#1-create-ansible-directory-structure","title":"1. Create Ansible Directory Structure","text":"
# Create project directory\nmkdir ~/ansible_quickstart\ncd ~/ansible_quickstart\n\n# Create directory structure\nmkdir -p group_vars host_vars roles playbooks\n
"},{"location":"adv/ansible/#2-install-ansible","title":"2. Install Ansible","text":"
sudo apt update\nsudo apt install ansible\n
"},{"location":"adv/ansible/#3-generate-ssh-keys-if-not-already-done","title":"3. Generate SSH Keys (if not already done)","text":"
# Generate SSH key pair\nssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa\n\n# Display public key (save this for later)\ncat ~/.ssh/id_rsa.pub\n
"},{"location":"adv/ansible/#part-2-target-node-setup-physical-access-required-initially","title":"Part 2: Target Node Setup (Physical Access Required Initially)","text":""},{"location":"adv/ansible/#1-enable-ssh-on-target-node","title":"1. Enable SSH on Target Node","text":"

Access each target node physically (monitor + keyboard):

# Update system\nsudo apt update && sudo apt upgrade -y\n\n# Install and enable SSH\nsudo apt install openssh-server\nsudo systemctl enable ssh\nsudo systemctl start ssh\n\n# Check SSH status\nsudo systemctl status ssh\n

Note: If you get \"Unit ssh.service could not be found\", you need to install the SSH server first:

# Install OpenSSH server\nsudo apt install openssh-server\n\n# Then start and enable SSH\nsudo systemctl start ssh\nsudo systemctl enable ssh\n\n# Verify SSH is running and listening\nsudo ss -tlnp | grep :22\n

You should see SSH listening on port 22.

"},{"location":"adv/ansible/#2-configure-ssh-key-authentication","title":"2. Configure SSH Key Authentication","text":"
# Create .ssh directory\nmkdir -p ~/.ssh\nchmod 700 ~/.ssh\n\n# Create authorized_keys file\nnano ~/.ssh/authorized_keys\n

Paste your public key from the master node, then:

# Set proper permissions\nchmod 600 ~/.ssh/authorized_keys\n
"},{"location":"adv/ansible/#3-configure-ssh-security","title":"3. Configure SSH Security","text":"
# Edit SSH config\nsudo nano /etc/ssh/sshd_config\n

Ensure these lines are uncommented:

PubkeyAuthentication yes\nAuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2\n
# Restart SSH service\nsudo systemctl restart ssh\n
"},{"location":"adv/ansible/#4-configure-firewall","title":"4. Configure Firewall","text":"
# Check firewall status\nsudo ufw status\n\n# Allow SSH through firewall\nsudo ufw allow ssh\n\n# Fix home directory permissions (required for SSH keys)\nchmod 755 ~/\n
"},{"location":"adv/ansible/#part-3-test-local-ssh-connection","title":"Part 3: Test Local SSH Connection","text":"

Before proceeding with remote access, test SSH connectivity locally:

# From master node, test SSH to target\nssh username@<target-local-ip>\n

Common Issues and Solutions:

"},{"location":"adv/ansible/#part-4-set-up-tailscale-for-remote-access","title":"Part 4: Set Up Tailscale for Remote Access","text":""},{"location":"adv/ansible/#why-tailscale-over-alternatives","title":"Why Tailscale Over Alternatives","text":"

We initially tried Cloudflare Tunnels but encountered complexity with:

Tailscale is superior because:

"},{"location":"adv/ansible/#1-install-tailscale-on-master-node","title":"1. Install Tailscale on Master Node","text":"
# Install Tailscale\ncurl -fsSL https://tailscale.com/install.sh | sh\n\n# Connect to Tailscale network\nsudo tailscale up\n

Follow the authentication URL to connect with your Google/Microsoft/GitHub account.

"},{"location":"adv/ansible/#2-install-tailscale-on-target-nodes","title":"2. Install Tailscale on Target Nodes","text":"

On each target node:

# Install Tailscale\ncurl -fsSL https://tailscale.com/install.sh | sh\n\n# Connect to Tailscale network\nsudo tailscale up\n

Authenticate each device through the provided URL.

"},{"location":"adv/ansible/#3-get-tailscale-ip-addresses","title":"3. Get Tailscale IP Addresses","text":"

On each machine:

# Get your Tailscale IP\ntailscale ip -4\n

Each device receives a persistent IP like 100.x.x.x.

"},{"location":"adv/ansible/#part-5-configure-ansible","title":"Part 5: Configure Ansible","text":""},{"location":"adv/ansible/#1-create-inventory-file","title":"1. Create Inventory File","text":"
# Create inventory.ini\ncd ~/ansible_quickstart\nnano inventory.ini\n

Content:

[thinkcenter]\ntc-node1 ansible_host=100.x.x.x ansible_user=your-username\ntc-node2 ansible_host=100.x.x.x ansible_user=your-username\n\n[all:vars]\nansible_ssh_private_key_file=~/.ssh/id_rsa\nansible_host_key_checking=False\n

Replace:

"},{"location":"adv/ansible/#2-test-ansible-connectivity","title":"2. Test Ansible Connectivity","text":"
# Test connection to all nodes\nansible all -i inventory.ini -m ping\n

Expected output:

tc-node1 | SUCCESS => {\n    \"changed\": false,\n    \"ping\": \"pong\"\n}\n
"},{"location":"adv/ansible/#part-6-create-and-run-playbooks","title":"Part 6: Create and Run Playbooks","text":""},{"location":"adv/ansible/#1-simple-information-gathering-playbook","title":"1. Simple Information Gathering Playbook","text":"
mkdir -p playbooks\nnano playbooks/info-playbook.yml\n

Content:

---\n- name: Gather Node Information\n  hosts: all\n  tasks:\n    - name: Get system information\n      setup:\n\n    - name: Display basic system info\n      debug:\n        msg: |\n          Hostname: {{ ansible_hostname }}\n          Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}\n          Architecture: {{ ansible_architecture }}\n          Memory: {{ ansible_memtotal_mb }}MB\n          CPU Cores: {{ ansible_processor_vcpus }}\n\n    - name: Show disk usage\n      command: df -h /\n      register: disk_info\n\n    - name: Display disk usage\n      debug:\n        msg: \"Root filesystem usage: {{ disk_info.stdout_lines[1] }}\"\n\n    - name: Check uptime\n      command: uptime\n      register: uptime_info\n\n    - name: Display uptime\n      debug:\n        msg: \"System uptime: {{ uptime_info.stdout }}\"\n
"},{"location":"adv/ansible/#2-run-the-playbook","title":"2. Run the Playbook","text":"
ansible-playbook -i inventory.ini playbooks/info-playbook.yml\n
"},{"location":"adv/ansible/#part-7-advanced-playbook-example","title":"Part 7: Advanced Playbook Example","text":""},{"location":"adv/ansible/#system-setup-playbook","title":"System Setup Playbook","text":"
nano playbooks/setup-node.yml\n

Content:

---\n- name: Setup ThinkCentre Node\n  hosts: all\n  become: yes\n  tasks:\n    - name: Update package cache\n      apt:\n        update_cache: yes\n\n    - name: Install essential packages\n      package:\n        name:\n          - htop\n          - vim\n          - curl\n          - git\n          - docker.io\n        state: present\n\n    - name: Add user to docker group\n      user:\n        name: \"{{ ansible_user }}\"\n        groups: docker\n        append: yes\n\n    - name: Create management directory\n      file:\n        path: /opt/management\n        state: directory\n        owner: \"{{ ansible_user }}\"\n        group: \"{{ ansible_user }}\"\n
"},{"location":"adv/ansible/#troubleshooting-guide","title":"Troubleshooting Guide","text":""},{"location":"adv/ansible/#ssh-issues","title":"SSH Issues","text":"

Problem: SSH connection hangs

Problem: Permission denied (publickey)

Problem: Bad owner or permissions on SSH config

chmod 600 ~/.ssh/config\n
"},{"location":"adv/ansible/#ansible-issues","title":"Ansible Issues","text":"

Problem: Host key verification failed

Problem: Ansible command not found

sudo apt install ansible\n

Problem: Connection timeouts

"},{"location":"adv/ansible/#tailscale-issues","title":"Tailscale Issues","text":"

Problem: Can't connect to Tailscale IP

"},{"location":"adv/ansible/#scaling-to-multiple-nodes","title":"Scaling to Multiple Nodes","text":""},{"location":"adv/ansible/#adding-new-nodes","title":"Adding New Nodes","text":"
  1. Install Tailscale on new node
  2. Set up SSH access (repeat Part 2)
  3. Add to inventory.ini:
[thinkcenter]\ntc-node1 ansible_host=100.125.148.60 ansible_user=bunker-admin\ntc-node2 ansible_host=100.x.x.x ansible_user=bunker-admin\ntc-node3 ansible_host=100.x.x.x ansible_user=bunker-admin\n
"},{"location":"adv/ansible/#group-management","title":"Group Management","text":"
[webservers]\ntc-node1 ansible_host=100.x.x.x ansible_user=bunker-admin\ntc-node2 ansible_host=100.x.x.x ansible_user=bunker-admin\n\n[databases]\ntc-node3 ansible_host=100.x.x.x ansible_user=bunker-admin\n\n[all:vars]\nansible_ssh_private_key_file=~/.ssh/id_rsa\nansible_host_key_checking=False\n

Run playbooks on specific groups:

ansible-playbook -i inventory.ini -l webservers playbook.yml\n
"},{"location":"adv/ansible/#best-practices","title":"Best Practices","text":""},{"location":"adv/ansible/#security","title":"Security","text":""},{"location":"adv/ansible/#organization","title":"Organization","text":"
ansible_quickstart/\n\u251c\u2500\u2500 inventory.ini\n\u251c\u2500\u2500 group_vars/\n\u251c\u2500\u2500 host_vars/\n\u251c\u2500\u2500 roles/\n\u2514\u2500\u2500 playbooks/\n    \u251c\u2500\u2500 info-playbook.yml\n    \u251c\u2500\u2500 setup-node.yml\n    \u2514\u2500\u2500 maintenance.yml\n
"},{"location":"adv/ansible/#monitoring-and-maintenance","title":"Monitoring and Maintenance","text":"

Create regular maintenance playbooks:

- name: System maintenance\n  hosts: all\n  become: yes\n  tasks:\n    - name: Update all packages\n      apt:\n        upgrade: dist\n        update_cache: yes\n\n    - name: Clean package cache\n      apt:\n        autoclean: yes\n        autoremove: yes\n
"},{"location":"adv/ansible/#alternative-approaches-we-considered","title":"Alternative Approaches We Considered","text":""},{"location":"adv/ansible/#cloudflare-tunnels","title":"Cloudflare Tunnels","text":""},{"location":"adv/ansible/#traditional-vpn","title":"Traditional VPN","text":""},{"location":"adv/ansible/#ssh-reverse-tunnels","title":"SSH Reverse Tunnels","text":""},{"location":"adv/ansible/#conclusion","title":"Conclusion","text":"

This setup provides:

The combination of Ansible + Tailscale is ideal for managing distributed infrastructure without the complexity of traditional VPN setups or the limitations of cloud-specific solutions.

"},{"location":"adv/ansible/#quick-reference-commands","title":"Quick Reference Commands","text":"
# Check Tailscale status\ntailscale status\n\n# Test Ansible connectivity\nansible all -i inventory.ini -m ping\n\n# Run playbook on all hosts\nansible-playbook -i inventory.ini playbook.yml\n\n# Run playbook on specific group\nansible-playbook -i inventory.ini -l groupname playbook.yml\n\n# Run single command on all hosts\nansible all -i inventory.ini -m command -a \"uptime\"\n\n# SSH to node via Tailscale\nssh username@100.x.x.x\n
"},{"location":"adv/vscode-ssh/","title":"Remote Development with VSCode over Tailscale","text":""},{"location":"adv/vscode-ssh/#overview","title":"Overview","text":"

This guide describes how to set up Visual Studio Code for remote development on servers using the Tailscale network. This enables development directly on remote machines as if they were local, with full access to files, terminals, and debugging capabilities.

"},{"location":"adv/vscode-ssh/#what-youll-learn","title":"What You'll Learn","text":""},{"location":"adv/vscode-ssh/#prerequisites","title":"Prerequisites","text":""},{"location":"adv/vscode-ssh/#verify-prerequisites","title":"Verify Prerequisites","text":"

Before starting, verify the setup:

# Check Tailscale connectivity\ntailscale status\n\n# Test SSH access\nssh <username>@<tailscale-ip>\n\n# Check VSCode is installed\ncode --version\n
"},{"location":"adv/vscode-ssh/#part-1-install-and-configure-remote-ssh-extension","title":"Part 1: Install and Configure Remote-SSH Extension","text":""},{"location":"adv/vscode-ssh/#1-install-the-remote-development-extensions","title":"1. Install the Remote Development Extensions","text":"

Option A: Install Remote Development Pack (Recommended)

  1. Open VSCode
  2. Press Ctrl+Shift+X (or Cmd+Shift+X on Mac)
  3. Search for \"Remote Development\"
  4. Install the Remote Development extension pack by Microsoft

This pack includes:

Option B: Install Individual Extension

  1. Search for \"Remote - SSH\"
  2. Install Remote - SSH by Microsoft
"},{"location":"adv/vscode-ssh/#2-verify-installation","title":"2. Verify Installation","text":"

After installation, the following should be visible:

"},{"location":"adv/vscode-ssh/#part-2-configure-ssh-connections","title":"Part 2: Configure SSH Connections","text":""},{"location":"adv/vscode-ssh/#1-access-ssh-configuration","title":"1. Access SSH Configuration","text":"

Method A: Through VSCode

  1. Press Ctrl+Shift+P to open Command Palette
  2. Type \"Remote-SSH: Open SSH Configuration File...\"
  3. Select the SSH config file (usually the first option)

Method B: Direct File Editing

# Edit SSH config file directly\nnano ~/.ssh/config\n

"},{"location":"adv/vscode-ssh/#2-add-server-configurations","title":"2. Add Server Configurations","text":"

Add servers to the SSH config file:

# Example Node\nHost node1\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n    ForwardAgent yes\n    ServerAliveInterval 60\n    ServerAliveCountMax 3\n\n# Additional nodes (add as needed)\nHost node2\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n    ForwardAgent yes\n    ServerAliveInterval 60\n    ServerAliveCountMax 3\n

Configuration Options Explained:

"},{"location":"adv/vscode-ssh/#3-set-proper-ssh-key-permissions","title":"3. Set Proper SSH Key Permissions","text":"
# Ensure SSH config has correct permissions\nchmod 600 ~/.ssh/config\n\n# Verify SSH key permissions\nchmod 600 ~/.ssh/id_rsa\nchmod 644 ~/.ssh/id_rsa.pub\n
"},{"location":"adv/vscode-ssh/#part-3-connect-to-remote-servers","title":"Part 3: Connect to Remote Servers","text":""},{"location":"adv/vscode-ssh/#1-connect-via-command-palette","title":"1. Connect via Command Palette","text":"
  1. Press Ctrl+Shift+P
  2. Type \"Remote-SSH: Connect to Host...\"
  3. Select the server (e.g., node1)
  4. VSCode will open a new window connected to the remote server
"},{"location":"adv/vscode-ssh/#2-connect-via-remote-explorer","title":"2. Connect via Remote Explorer","text":"
  1. Click the Remote Explorer icon in Activity Bar
  2. Expand SSH Targets
  3. Click the connect icon next to the server name
"},{"location":"adv/vscode-ssh/#3-connect-via-quick-menu","title":"3. Connect via Quick Menu","text":"
  1. Click the remote indicator in bottom-left corner (looks like ><)
  2. Select \"Connect to Host...\"
  3. Choose the server from the list
"},{"location":"adv/vscode-ssh/#4-first-connection-process","title":"4. First Connection Process","text":"

On first connection, VSCode will:

  1. Verify the host key (click \"Continue\" if prompted)
  2. Install VSCode Server on the remote machine (automatic)
  3. Open a remote window with access to the remote file system

Expected Timeline: - First connection: 1-3 minutes (installs VSCode Server) - Subsequent connections: 10-30 seconds

"},{"location":"adv/vscode-ssh/#part-4-remote-development-environment-setup","title":"Part 4: Remote Development Environment Setup","text":""},{"location":"adv/vscode-ssh/#1-open-remote-workspace","title":"1. Open Remote Workspace","text":"

Once connected:

# In the VSCode terminal (now running on remote server)\n# Navigate to the project directory\ncd /home/<username>/projects\n\n# Open current directory in VSCode\ncode .\n\n# Or open a specific project\ncode /opt/myproject\n
"},{"location":"adv/vscode-ssh/#2-install-extensions-on-remote-server","title":"2. Install Extensions on Remote Server","text":"

Extensions must be installed separately on the remote server:

Essential Development Extensions:

  1. Python (Microsoft) - Python development
  2. GitLens (GitKraken) - Enhanced Git capabilities
  3. Docker (Microsoft) - Container development
  4. Prettier - Code formatting
  5. ESLint - JavaScript linting
  6. Auto Rename Tag - HTML/XML tag editing

To Install:

  1. Go to Extensions (Ctrl+Shift+X)
  2. Find the desired extension
  3. Click \"Install in SSH: node1\" (not local install)
"},{"location":"adv/vscode-ssh/#3-configure-git-on-remote-server","title":"3. Configure Git on Remote Server","text":"
# In VSCode terminal (remote)\ngit config --global user.name \"<Full Name>\"\ngit config --global user.email \"<email@example.com>\"\n\n# Test Git connectivity\ngit clone https://github.com/<username>/<repo>.git\n
"},{"location":"adv/vscode-ssh/#part-5-remote-development-workflows","title":"Part 5: Remote Development Workflows","text":""},{"location":"adv/vscode-ssh/#1-file-management","title":"1. File Management","text":"

File Explorer:

File Transfer:

# Upload files to remote (from local terminal)\nscp localfile.txt <username>@<tailscale-ip>:/home/<username>/\n\n# Download files from remote\nscp <username>@<tailscale-ip>:/remote/path/file.txt ./local/path/\n

"},{"location":"adv/vscode-ssh/#2-terminal-usage","title":"2. Terminal Usage","text":"

Integrated Terminal:

Common Remote Terminal Commands:

# Check system resources\nhtop\ndf -h\nfree -h\n\n# Install packages\nsudo apt update\nsudo apt install nodejs npm\n\n# Start services\nsudo systemctl start nginx\nsudo docker-compose up -d\n

"},{"location":"adv/vscode-ssh/#3-port-forwarding","title":"3. Port Forwarding","text":"

Automatic Port Forwarding: VSCode automatically detects and forwards common development ports.

Manual Port Forwarding:

  1. Open Ports tab in terminal panel
  2. Click \"Forward a Port\"
  3. Enter port number (e.g., 3000, 8080, 5000)
  4. Access via http://localhost:port on the local machine

Example: Web Development

# Start a web server on remote (port 3000)\nnpm start\n\n# VSCode automatically suggests forwarding port 3000\n# Access at http://localhost:3000 on the local machine\n

"},{"location":"adv/vscode-ssh/#4-debugging-remote-applications","title":"4. Debugging Remote Applications","text":"

Python Debugging:

// .vscode/launch.json on remote server\n{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Python: Current File\",\n            \"type\": \"python\",\n            \"request\": \"launch\",\n            \"program\": \"${file}\",\n            \"console\": \"integratedTerminal\"\n        }\n    ]\n}\n

Node.js Debugging:

// .vscode/launch.json\n{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Launch Program\",\n            \"type\": \"node\",\n            \"request\": \"launch\",\n            \"program\": \"${workspaceFolder}/app.js\"\n        }\n    ]\n}\n

"},{"location":"adv/vscode-ssh/#part-6-advanced-configuration","title":"Part 6: Advanced Configuration","text":""},{"location":"adv/vscode-ssh/#1-workspace-settings","title":"1. Workspace Settings","text":"

Create remote-specific settings:

// .vscode/settings.json (on remote server)\n{\n    \"python.defaultInterpreterPath\": \"/usr/bin/python3\",\n    \"terminal.integrated.shell.linux\": \"/bin/bash\",\n    \"files.autoSave\": \"afterDelay\",\n    \"editor.formatOnSave\": true,\n    \"remote.SSH.remotePlatform\": {\n        \"node1\": \"linux\"\n    }\n}\n
"},{"location":"adv/vscode-ssh/#2-multi-server-management","title":"2. Multi-Server Management","text":"

Switch Between Servers:

  1. Click remote indicator (bottom-left)
  2. Select \"Connect to Host...\"
  3. Choose a different server

Compare Files Across Servers:

  1. Open file from server A
  2. Connect to server B in new window
  3. Open corresponding file
  4. Use \"Compare with...\" command
"},{"location":"adv/vscode-ssh/#3-sync-configuration","title":"3. Sync Configuration","text":"

Settings Sync:

  1. Enable Settings Sync in VSCode
  2. Settings, extensions, and keybindings sync to remote
  3. Consistent experience across all servers
"},{"location":"adv/vscode-ssh/#part-7-project-specific-setups","title":"Part 7: Project-Specific Setups","text":""},{"location":"adv/vscode-ssh/#1-python-development","title":"1. Python Development","text":"
# On remote server\n# Create virtual environment\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install packages\npip install flask django requests\n\n# VSCode automatically detects Python interpreter\n

VSCode Python Configuration:

// .vscode/settings.json\n{\n    \"python.defaultInterpreterPath\": \"./venv/bin/python\",\n    \"python.linting.enabled\": true,\n    \"python.linting.pylintEnabled\": true\n}\n

"},{"location":"adv/vscode-ssh/#2-nodejs-development","title":"2. Node.js Development","text":"
# On remote server\n# Install Node.js\ncurl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\n# Create project\nmkdir myapp && cd myapp\nnpm init -y\nnpm install express\n
"},{"location":"adv/vscode-ssh/#3-docker-development","title":"3. Docker Development","text":"
# On remote server\n# Install Docker (if not already done via Ansible)\nsudo apt install docker.io docker-compose\nsudo usermod -aG docker $USER\n\n# Create Dockerfile\ncat > Dockerfile << EOF\nFROM node:18\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nEXPOSE 3000\nCMD [\"npm\", \"start\"]\nEOF\n

VSCode Docker Integration:

"},{"location":"adv/vscode-ssh/#part-8-troubleshooting-guide","title":"Part 8: Troubleshooting Guide","text":""},{"location":"adv/vscode-ssh/#common-connection-issues","title":"Common Connection Issues","text":"

Problem: \"Could not establish connection to remote host\"

Solutions:

# Check Tailscale connectivity\ntailscale status\nping <tailscale-ip>\n\n# Test SSH manually\nssh <username>@<tailscale-ip>\n\n# Check SSH config syntax\nssh -T node1\n

Problem: \"Permission denied (publickey)\"

Solutions:

# Check SSH key permissions\nchmod 600 ~/.ssh/id_rsa\nchmod 600 ~/.ssh/config\n\n# Verify SSH agent\nssh-add ~/.ssh/id_rsa\nssh-add -l\n\n# Test SSH connection verbosely\nssh -v <username>@<tailscale-ip>\n

Problem: \"Host key verification failed\"

Solutions:

# Remove old host key\nssh-keygen -R <tailscale-ip>\n\n# Or disable host key checking (less secure)\n# Add to SSH config:\n# StrictHostKeyChecking no\n

"},{"location":"adv/vscode-ssh/#vscode-specific-issues","title":"VSCode-Specific Issues","text":"

Problem: Extensions not working on remote

Solutions:

  1. Install extensions specifically for the remote server
  2. Check extension compatibility with remote development
  3. Reload VSCode window: Ctrl+Shift+P \u2192 \"Developer: Reload Window\"

Problem: Slow performance

Solutions: - Use .vscode/settings.json to exclude large directories:

{\n    \"files.watcherExclude\": {\n        \"**/node_modules/**\": true,\n        \"**/.git/objects/**\": true,\n        \"**/dist/**\": true\n    }\n}\n

Problem: Terminal not starting

Solutions:

# Check shell path in remote settings\n\"terminal.integrated.shell.linux\": \"/bin/bash\"\n\n# Or let VSCode auto-detect\n\"terminal.integrated.defaultProfile.linux\": \"bash\"\n

"},{"location":"adv/vscode-ssh/#network-and-performance-issues","title":"Network and Performance Issues","text":"

Problem: Connection timeouts

Solutions: Add to SSH config:

ServerAliveInterval 60\nServerAliveCountMax 3\nTCPKeepAlive yes\n

Problem: File transfer slow

Solutions: - Use .vscodeignore to exclude unnecessary files - Compress large files before transfer - Use rsync for large file operations:

rsync -avz --progress localdir/ <username>@<tailscale-ip>:remotedir/\n

"},{"location":"adv/vscode-ssh/#part-9-best-practices","title":"Part 9: Best Practices","text":""},{"location":"adv/vscode-ssh/#security-best-practices","title":"Security Best Practices","text":"
  1. Use SSH keys, never passwords
  2. Keep SSH agent secure
  3. Regular security updates on remote servers
  4. Use VSCode's secure connection verification
"},{"location":"adv/vscode-ssh/#performance-optimization","title":"Performance Optimization","text":"
  1. Exclude unnecessary files:

    // .vscode/settings.json\n{\n    \"files.watcherExclude\": {\n        \"**/node_modules/**\": true,\n        \"**/.git/**\": true,\n        \"**/dist/**\": true,\n        \"**/build/**\": true\n    },\n    \"search.exclude\": {\n        \"**/node_modules\": true,\n        \"**/bower_components\": true,\n        \"**/*.code-search\": true\n    }\n}\n

  2. Use remote workspace for large projects

  3. Close unnecessary windows and extensions
  4. Use efficient development workflows
"},{"location":"adv/vscode-ssh/#development-workflow","title":"Development Workflow","text":"
  1. Use version control effectively:

    # Always work in Git repositories\ngit status\ngit add .\ngit commit -m \"feature: add new functionality\"\ngit push origin main\n

  2. Environment separation:

    # Development\nssh node1\ncd /home/<username>/dev-projects\n\n# Production\nssh node2\ncd /opt/production-apps\n

  3. Backup important work:

    # Regular backups via Git\ngit push origin main\n\n# Or manual backup\nscp -r <username>@<tailscale-ip>:/important/project ./backup/\n

"},{"location":"adv/vscode-ssh/#part-10-team-collaboration","title":"Part 10: Team Collaboration","text":""},{"location":"adv/vscode-ssh/#shared-development-servers","title":"Shared Development Servers","text":"

SSH Config for Team:

# Shared development server\nHost team-dev\n    HostName <tailscale-ip>\n    User <team-user>\n    IdentityFile ~/.ssh/team_dev_key\n    ForwardAgent yes\n\n# Personal development\nHost my-dev\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n

"},{"location":"adv/vscode-ssh/#project-structure","title":"Project Structure","text":"
/opt/projects/\n\u251c\u2500\u2500 project-a/\n\u2502   \u251c\u2500\u2500 dev/          # Development branch\n\u2502   \u251c\u2500\u2500 staging/      # Staging environment\n\u2502   \u2514\u2500\u2500 docs/         # Documentation\n\u251c\u2500\u2500 project-b/\n\u2514\u2500\u2500 shared-tools/     # Common utilities\n
"},{"location":"adv/vscode-ssh/#access-management","title":"Access Management","text":"
# Create shared project directory\nsudo mkdir -p /opt/projects\nsudo chown -R :developers /opt/projects\nsudo chmod -R g+w /opt/projects\n\n# Add users to developers group\nsudo usermod -a -G developers <username>\n
"},{"location":"adv/vscode-ssh/#quick-reference","title":"Quick Reference","text":""},{"location":"adv/vscode-ssh/#essential-vscode-remote-commands","title":"Essential VSCode Remote Commands","text":"
# Command Palette shortcuts\nCtrl+Shift+P \u2192 \"Remote-SSH: Connect to Host...\"\nCtrl+Shift+P \u2192 \"Remote-SSH: Open SSH Configuration File...\"\nCtrl+Shift+P \u2192 \"Remote-SSH: Kill VS Code Server on Host...\"\n\n# Terminal\nCtrl+` \u2192 Open integrated terminal\nCtrl+Shift+` \u2192 Create new terminal\n\n# File operations\nCtrl+O \u2192 Open file\nCtrl+S \u2192 Save file\nCtrl+Shift+E \u2192 Focus file explorer\n
"},{"location":"adv/vscode-ssh/#ssh-connection-quick-test","title":"SSH Connection Quick Test","text":"
# Test connectivity\nssh -T node1\n\n# Connect with verbose output\nssh -v <username>@<tailscale-ip>\n\n# Check SSH config\nssh -F ~/.ssh/config node1\n
"},{"location":"adv/vscode-ssh/#port-forwarding-commands","title":"Port Forwarding Commands","text":"
# Manual port forwarding\nssh -L 3000:localhost:3000 <username>@<tailscale-ip>\n\n# Background tunnel\nssh -f -N -L 8080:localhost:80 <username>@<tailscale-ip>\n
"},{"location":"adv/vscode-ssh/#conclusion","title":"Conclusion","text":"

This remote development setup provides:

The combination of VSCode Remote Development with Tailscale networking creates a powerful, flexible development environment that works from anywhere while maintaining security and performance.

Whether developing Python applications, Node.js services, or managing Docker containers, this setup provides a professional remote development experience that rivals local development while leveraging the power and resources of remote servers.

"},{"location":"blog/2025/07/03/blog-1/","title":"Blog 1","text":"

Hello! Just putting something up here because, well, gosh darn, feels like the right thing to do.

Making swift progress. Can now write things fast as heck lad.

"},{"location":"blog/2025/07/10/2/","title":"2","text":"

Wow. Big build day. Added (admittedly still buggy) shifts support to the system. Power did it in a day.

Other updates recently include:

Need to make more content about how to use the system in general too.

"},{"location":"build/","title":"Getting Started","text":"

Welcome to Changemaker-Lite! You're about to reclaim your digital sovereignty and stop feeding your secrets to corporations. This guide will help you set up your own political infrastructure that you actually own and control.

This documentation is broken into a few sections:

"},{"location":"build/#quick-start","title":"Quick Start","text":""},{"location":"build/#build-changemaker-lite","title":"Build Changemaker-Lite","text":"
# Clone the repository\ngit clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n

Cloudflare Credentials

The config.sh script will ask you for your Cloudflare credentials to get started. You can find more information on how to find this in the Cloudlflare Configuration

# Configure environment (creates .env file)\n./config.sh\n
# Start all services\ndocker compose up -d\n
"},{"location":"build/#optional-site-builld","title":"Optional - Site Builld","text":"

If you want to have your site prepared for launch, you can now proceed with reseting the site build. See Build Site for more detials.

"},{"location":"build/#deploy","title":"Deploy","text":"

Cloudflare

Right now, we suggest deploying using Cloudflare for simplicity and protections against 99% of surface level attacks to digital infrastructure. If you want to avoid using this service, we recommend checking out Pagolin as a drop in replacement.

For secure public access, use the production deployment script:

./start-production.sh\n
"},{"location":"build/#why-changemaker-lite","title":"Why Changemaker Lite?","text":"

Before we dive into the technical setup, let's be clear about what you're doing here:

The Reality

If you do politics, who is reading your secrets? Every corporate platform you use is extracting your power, selling your data, and building profiles on your community. It's time to break free.

"},{"location":"build/#what-youre-getting","title":"What You're Getting","text":""},{"location":"build/#what-youre-leaving-behind","title":"What You're Leaving Behind","text":""},{"location":"build/#system-requirements","title":"System Requirements","text":""},{"location":"build/#operating-system","title":"Operating System","text":"

Getting Started on Ubunut

Want some help getting started with a baseline buildout for a Ubunut server? You can use our BNKops Server Build Script

New to Linux?

Consider Linux Mint - it looks like Windows but opens the door to true digital freedom.

"},{"location":"build/#hardware-requirements","title":"Hardware Requirements","text":"

Cloud Hosting

You can run this on a VPS from providers like Hetzner, DigitalOcean, or Linode for ~$20/month.

"},{"location":"build/#software-prerequisites","title":"Software Prerequisites","text":"

Getting Started on Docker

Want some help getting started with a baseline buildout for a Ubunutu server? You can use our BNKops Server Build Script to roll out a configured server in about 20 mins!

  1. Docker Engine (24.0+)
# Install Docker\ncurl -fsSL https://get.docker.com | sudo sh\n\n# Add your user to docker group\nsudo usermod -aG docker $USER\n\n# Log out and back in for group changes to take effect\n
  1. Docker Compose (v2.20+)
# Verify Docker Compose v2 is installed\ndocker compose version\n
  1. Essential Tools
# Install required packages\nsudo apt update\nsudo apt install -y git curl jq openssl\n
"},{"location":"build/#installation","title":"Installation","text":""},{"location":"build/#1-clone-repository","title":"1. Clone Repository","text":"
git clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n
"},{"location":"build/#2-run-configuration-wizard","title":"2. Run Configuration Wizard","text":"

The config.sh script will guide you through the initial setup:

./config.sh\n

This wizard will:

"},{"location":"build/#configuration-options","title":"Configuration Options","text":"

During setup, you'll be prompted for:

  1. Domain Name: Your primary domain (e.g., example.com)
  2. Cloudflare Settings (optional):
  3. API Token
  4. Zone ID
  5. Account ID
  6. Admin Credentials:
  7. Listmonk admin email and password
  8. n8n admin email and password
"},{"location":"build/#3-start-services","title":"3. Start Services","text":"

Launch all services with Docker Compose:

docker compose up -d\n

Wait for services to initialize (first run may take 5-10 minutes):

# Watch container status\ndocker compose ps\n\n# View logs\ndocker compose logs -f\n
"},{"location":"build/#4-verify-installation","title":"4. Verify Installation","text":"

Check that all services are running:

docker compose ps\n

Expected output should show all services as \"Up\":

"},{"location":"build/#local-access","title":"Local Access","text":"

Once services are running, access them locally:

"},{"location":"build/#homepage-dashboard","title":"\ud83c\udfe0 Homepage Dashboard","text":""},{"location":"build/#development-tools","title":"\ud83d\udcbb Development Tools","text":""},{"location":"build/#communication","title":"\ud83d\udce7 Communication","text":""},{"location":"build/#automation-data","title":"\ud83d\udd04 Automation & Data","text":""},{"location":"build/#interactive-tools","title":"\ud83d\udee0\ufe0f Interactive Tools","text":""},{"location":"build/#map","title":"Map","text":"

Map

Map is the canvassing application that is custom view of nocodb data. Map is best built after production deployment to reduce duplicate build efforts.

"},{"location":"build/#map-manual","title":"Map Manual","text":""},{"location":"build/#production-deployment","title":"Production Deployment","text":""},{"location":"build/#deploy-with-cloudflare-tunnels","title":"Deploy with Cloudflare Tunnels","text":"

For secure public access, use the production deployment script:

./start-production.sh\n

This script will:

  1. Install and configure cloudflared
  2. Create a Cloudflare tunnel
  3. Set up DNS records automatically
  4. Configure access policies
  5. Create a systemd service for persistence
"},{"location":"build/#what-happens-during-production-setup","title":"What Happens During Production Setup","text":"
  1. Cloudflare Authentication: Browser-based login to Cloudflare
  2. Tunnel Creation: Secure tunnel named changemaker-lite
  3. DNS Configuration: Automatic CNAME records for all services
  4. Access Policies: Email-based authentication for sensitive services
  5. Service Installation: Systemd service for automatic startup
"},{"location":"build/#production-urls","title":"Production URLs","text":"

After successful deployment, services will be available at:

Public Services:

Protected Services (require authentication):

"},{"location":"build/#configuration-management","title":"Configuration Management","text":""},{"location":"build/#environment-variables","title":"Environment Variables","text":"

Key settings in .env file:

# Domain Configuration\nDOMAIN=yourdomain.com\nBASE_DOMAIN=https://yourdomain.com\n\n# Service Ports (automatically assigned to avoid conflicts)\nHOMEPAGE_PORT=3010\nCODE_SERVER_PORT=8888\nLISTMONK_PORT=9000\nMKDOCS_PORT=4000\nMKDOCS_SITE_SERVER_PORT=4001\nN8N_PORT=5678\nNOCODB_PORT=8090\nGITEA_WEB_PORT=3030\nGITEA_SSH_PORT=2222\nMAP_PORT=3000\nMINI_QR_PORT=8089\n\n# Cloudflare (for production)\nCF_API_TOKEN=your_token\nCF_ZONE_ID=your_zone_id\nCF_ACCOUNT_ID=your_account_id\n
"},{"location":"build/#reconfigure-services","title":"Reconfigure Services","text":"

To update configuration:

# Re-run configuration wizard\n./config.sh\n\n# Restart services\ndocker compose down && docker compose up -d\n
"},{"location":"build/#common-tasks","title":"Common Tasks","text":""},{"location":"build/#service-management","title":"Service Management","text":"
# View all services\ndocker compose ps\n\n# View logs for specific service\ndocker compose logs -f [service-name]\n\n# Restart a service\ndocker compose restart [service-name]\n\n# Stop all services\ndocker compose down\n\n# Stop and remove all data (CAUTION!)\ndocker compose down -v\n
"},{"location":"build/#backup-data","title":"Backup Data","text":"
# Backup all volumes\ndocker run --rm -v changemaker_listmonk-data:/data -v $(pwd):/backup alpine tar czf /backup/listmonk-backup.tar.gz -C /data .\n\n# Backup configuration\ntar czf configs-backup.tar.gz configs/\n\n# Backup documentation\ntar czf docs-backup.tar.gz mkdocs/docs/\n
"},{"location":"build/#update-services","title":"Update Services","text":"
# Pull latest images\ndocker compose pull\n\n# Recreate containers with new images\ndocker compose up -d\n
"},{"location":"build/#troubleshooting","title":"Troubleshooting","text":""},{"location":"build/#port-conflicts","title":"Port Conflicts","text":"

If services fail to start due to port conflicts:

  1. Check which ports are in use:
sudo ss -tulpn | grep LISTEN\n
  1. Re-run configuration to get new ports:
./config.sh\n
  1. Or manually edit .env file and change conflicting ports
"},{"location":"build/#permission-issues","title":"Permission Issues","text":"

Fix permission problems:

# Get your user and group IDs\nid -u  # User ID\nid -g  # Group ID\n\n# Update .env file with correct IDs\nUSER_ID=1000\nGROUP_ID=1000\n\n# Restart services\ndocker compose down && docker compose up -d\n
"},{"location":"build/#service-wont-start","title":"Service Won't Start","text":"

Debug service issues:

# Check detailed logs\ndocker compose logs [service-name] --tail 50\n\n# Check container status\ndocker ps -a\n\n# Inspect container\ndocker inspect [container-name]\n
"},{"location":"build/#cloudflare-tunnel-issues","title":"Cloudflare Tunnel Issues","text":"
# Check tunnel service status\nsudo systemctl status cloudflared-changemaker\n\n# View tunnel logs\nsudo journalctl -u cloudflared-changemaker -f\n\n# Restart tunnel\nsudo systemctl restart cloudflared-changemaker\n
"},{"location":"build/#next-steps","title":"Next Steps","text":"

Now that your Changemaker Lite instance is running:

  1. Set up Listmonk - Configure SMTP and create your first campaign
  2. Create workflows - Build automations in n8n
  3. Import data - Set up your NocoDB databases
  4. Configure map - Add location data for the map viewer
  5. Write documentation - Start creating content in MkDocs
  6. Set up Git - Initialize repositories in Gitea
"},{"location":"build/#getting-help","title":"Getting Help","text":""},{"location":"build/map/","title":"Map Build Guide","text":"

Map is BNKops canvassing application built for community organizing and door-to-door canvassing.

Complete Configuration

For detailed configuration, usage instructions, and troubleshooting, see the Map Configuration Guide.

Clean NocoDB

Currently the way to get a good result is to ensure the target nocodb database is empty. You can do this by deleting all bases. The script should still work with other volumes however may insert tables into odd locations; still debugging. Again, see config if needing to do manually.

"},{"location":"build/map/#prerequisites","title":"Prerequisites","text":""},{"location":"build/map/#quick-build-process","title":"Quick Build Process","text":""},{"location":"build/map/#1-get-nocodb-api-token","title":"1. Get NocoDB API Token","text":"
  1. Login to your NocoDB instance
  2. Click user icon \u2192 Account Settings \u2192 API Tokens
  3. Create new token with read/write permissions
  4. Copy the token for the next step
"},{"location":"build/map/#2-configure-environment","title":"2. Configure Environment","text":"

Edit the .env file in the map/ directory:

cd map\n

Update your .env file with your NocoDB details, specifically the instance and api token:

# NocoDB API Configuration\nNOCODB_API_URL=https://your-nocodb-instance.com/api/v1\nNOCODB_API_TOKEN=your-api-token-here\n\n# These will be populated after running build-nocodb.sh\nNOCODB_VIEW_URL=\nNOCODB_LOGIN_SHEET=\nNOCODB_SETTINGS_SHEET=\n\n# Server Configuration\nPORT=3000\nNODE_ENV=production\nSESSION_SECRET=your-secure-random-string\n\n# Map Defaults (Edmonton, AB)\nDEFAULT_LAT=53.5461\nDEFAULT_LNG=-113.4938\nDEFAULT_ZOOM=11\n\n# Production Settings\nCOOKIE_DOMAIN=.yourdomain.com\nALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000\n
"},{"location":"build/map/#3-auto-create-database-structure","title":"3. Auto-Create Database Structure","text":"

Run the build script to create required tables:

chmod +x build-nocodb.sh\n./build-nocodb.sh\n

This creates three tables: - Locations - Main map data with geo-location, contact info, support levels - Login - User authentication (email, name, admin flag) - Settings - Admin configuration and QR codes

"},{"location":"build/map/#4-get-table-urls","title":"4. Get Table URLs","text":"

After the script completes:

  1. Login to your NocoDB instance
  2. Navigate to your project (\"Map Viewer Project\")
  3. Copy the view URLs for each table from your browser address bar
  4. URLs should look like: https://your-nocodb.com/dashboard/#/nc/project-id/table-id
"},{"location":"build/map/#5-update-environment-with-urls","title":"5. Update Environment with URLs","text":"

Edit your .env file and add the table URLs:

NOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id\nNOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id\nNOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id\n
"},{"location":"build/map/#6-build-and-deploy","title":"6. Build and Deploy","text":"

Build the Docker image and start the application:

# Build the Docker image\ndocker-compose build\n\n# Start the application\ndocker-compose up -d\n
"},{"location":"build/map/#verify-installation","title":"Verify Installation","text":"
  1. Check container status:

    docker-compose ps\n

  2. View logs:

    docker-compose logs -f map-viewer\n

  3. Access the application at http://localhost:3000

"},{"location":"build/map/#quick-start","title":"Quick Start","text":"
  1. Login: Use an email from your Login table
  2. Add Locations: Click on the map to add new locations
  3. Admin Panel: Admin users can access /admin.html for configuration
  4. Walk Sheets: Generate printable canvassing forms with QR codes
"},{"location":"build/map/#maintenance-commands","title":"Maintenance Commands","text":""},{"location":"build/map/#update-application","title":"Update Application","text":"
docker-compose down\ngit pull origin main\ndocker-compose build\ndocker-compose up -d\n
"},{"location":"build/map/#development-mode","title":"Development Mode","text":"
cd app\nnpm install\nnpm run dev\n
"},{"location":"build/map/#health-check","title":"Health Check","text":"
curl http://localhost:3000/health\n
"},{"location":"build/map/#support","title":"Support","text":"

For detailed configuration, troubleshooting, and usage instructions, see the Map Configuration Guide.

"},{"location":"build/server/","title":"BNKops Server Build","text":"

Purpose: a Ubuntu server build-out for general application

This documentation is a overview of the full build out for a server OS and baseline for running Changemaker-lite. It is a manual to re-install this server on any machine.

All of the following systems are free and the majority are open source.

"},{"location":"build/server/#ubuntu-os","title":"Ubuntu OS","text":"

Ubuntu is a Linux distribution derived from Debian and composed mostly of free and open-source software.

"},{"location":"build/server/#install-ubuntu","title":"Install Ubuntu","text":""},{"location":"build/server/#post-install","title":"Post Install","text":"

Post installation, run update:

sudo apt update\n

sudo apt upgrade\n
"},{"location":"build/server/#configuration","title":"Configuration","text":"

Further configurations:

"},{"location":"build/server/#vscode-insiders","title":"VSCode Insiders","text":"

Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle.

"},{"location":"build/server/#install-using-app-centre","title":"Install Using App Centre","text":""},{"location":"build/server/#obsidian","title":"Obsidian","text":"

The free and flexible app for your private\u00a0thoughts.

"},{"location":"build/server/#install-using-app-center","title":"Install Using App Center","text":""},{"location":"build/server/#curl","title":"Curl","text":"

command line tool and library for transferring data with URLs (since 1998)

"},{"location":"build/server/#install","title":"Install","text":"
sudo apt install curl \n
"},{"location":"build/server/#glances","title":"Glances","text":"

Glances an Eye on your system. A top/htop alternative for GNU/Linux, BSD, Mac OS and Windows operating systems.

"},{"location":"build/server/#install_1","title":"Install","text":"
sudo snap install glances \n
"},{"location":"build/server/#syncthing","title":"Syncthing","text":"

Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it\u2019s transmitted over the internet.

"},{"location":"build/server/#install_2","title":"Install","text":"
# Add the release PGP keys:\nsudo mkdir -p /etc/apt/keyrings\nsudo curl -L -o /etc/apt/keyrings/syncthing-archive-keyring.gpg https://syncthing.net/release-key.gpg\n
# Add the \"stable\" channel to your APT sources:\necho \"deb [signed-by=/etc/apt/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable\" | sudo tee /etc/apt/sources.list.d/syncthing.list\n
# Update and install syncthing:\nsudo apt-get update\nsudo apt-get install syncthing\n
"},{"location":"build/server/#post-install_1","title":"Post Install","text":"

Run syncthing as a system service.

sudo systemctl start syncthing@yourusername\n

sudo systemctl enable syncthing@yourusername\n
"},{"location":"build/server/#docker","title":"Docker","text":"

Docker helps developers build, share, run, and verify applications anywhere \u2014 without tedious environment configuration or management.

# Add Docker's official GPG key:\nsudo apt-get update\nsudo apt-get install ca-certificates curl\nsudo install -m 0755 -d /etc/apt/keyrings\nsudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc\nsudo chmod a+r /etc/apt/keyrings/docker.asc\n\n# Add the repository to Apt sources:\necho \\\n  \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \\\n  $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable\" | \\\n  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\nsudo apt-get update\n

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n
"},{"location":"build/server/#update-users","title":"Update Users","text":"
sudo groupadd docker\n
sudo usermod -aG docker $USER\n
newgrp docker\n
"},{"location":"build/server/#enable-on-boot","title":"Enable on Boot","text":"
sudo systemctl enable docker.service\nsudo systemctl enable containerd.service\n
"},{"location":"build/server/#cloudflared","title":"Cloudflared","text":"

Connect, protect, and build everywhere. We make websites, apps, and networks faster and more secure. Our developer platform is the best place to build modern apps and deliver AI initiatives.

sudo mkdir -p --mode=0755 /usr/share/keyrings\ncurl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null\n
echo \"deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main\" | sudo tee /etc/apt/sources.list.d/cloudflared.list\n
sudo apt-get update && sudo apt-get install cloudflared\n
"},{"location":"build/server/#post-install_2","title":"Post Install","text":"

Login to Cloudflare

cloudflared login\n

"},{"location":"build/server/#configuration_1","title":"Configuration","text":"

The ./config.sh and ./start-production.sh scripts will properly configure a Cloudflare tunnel and service to put your system online. More info in the Cloudflare Configuration.

"},{"location":"build/server/#pandoc","title":"Pandoc","text":"

If you need to convert files from one markup format into another, pandoc is your swiss-army knife.

sudo apt install pandoc\n
"},{"location":"build/site/","title":"Building the Site with MkDocs Material","text":"

Welcome! This guide will help you get started building and customizing your site using MkDocs Material.

"},{"location":"build/site/#reset-site","title":"Reset Site","text":"

You can read through all the BNKops cmlite documentation already in your docs folder or you can reset your docs folder to a baseline to start and read more manuals here. To reset docs folder to baseline, run the following:

./reset-site.sh\n
"},{"location":"build/site/#how-to-build-your-site-step-by-step","title":"\ud83d\ude80 How to Build Your Site (Step by Step)","text":"
  1. Open your Coder instance. For example: coder.yourdomain.com
  2. Go to the mkdocs folder: In the terminal (for a new terminal press Crtl - Shift - ~), type:
    cd mkdocs\n
  3. Build the site: Type:
    mkdocs build\n
    This creates the static website from your documents and places them in the mkdocs/site directory.

Preview your site locally: Visit localhost:4000 for local development or live.youdomain.com to see a public live load.

Material for MkDocs Documentation

Build vs Serve

Your website is built in stages. Any edits to documents in the mkdocs directory are instantly served and visible at localhost:4000 or if in production mode live.yourdomain.com. The live site is not meant as a public access point and will crash if too many requests are made to it.

Running mkdocs build pushes any changes to the site directory, which then a ngnix server pushes them to the production server for public access at your root domain (yourdomain.com).

You can think of it as serve/live = draft for personal review and build = save/push to production for the public.

This combination allows for rapid development of documentation while ensuring your live site does not get updated until your content is ready.

"},{"location":"build/site/#resetting-the-site","title":"\ud83e\uddf9 Resetting the Site","text":"

If you want to start fresh:

  1. Delete all folders EXCEPT these folders:

  2. Reset the landing page:

  3. Reset the mkdocs.yml

"},{"location":"build/site/#using-ai-to-help-build-your-site","title":"\ud83e\udd16 Using AI to Help Build Your Site","text":""},{"location":"build/site/#first-time-setup-tips","title":"\ud83d\udee0\ufe0f First-Time Setup Tips","text":"

Quick Start Guide

"},{"location":"build/site/#more-resources","title":"\ud83d\udcda More Resources","text":"

Happy building!

"},{"location":"config/","title":"Configuration","text":"

There are several configuration steps to building a production ready Changemaker-Lite.

In the order we suggest doing them:

"},{"location":"config/cloudflare-config/","title":"Configure Cloudflare","text":"

Cloudflare is the largest DNS routing service on the planet. We use their free service tier to provide Changemaker users with a fast, secure, and reliable way to get online that blocks 99% of surface level attacks and has built in user authenticaion (if you so choose to use it)

"},{"location":"config/cloudflare-config/#credentials","title":"Credentials","text":"

The config.sh and start-production.sh scripts require the following Cloudflare credentials to function properly:

"},{"location":"config/cloudflare-config/#1-cloudflare-api-token","title":"1. Cloudflare API Token","text":""},{"location":"config/cloudflare-config/#2-cloudflare-zone-id","title":"2. Cloudflare Zone ID","text":""},{"location":"config/cloudflare-config/#3-cloudflare-account-id","title":"3. Cloudflare Account ID","text":""},{"location":"config/cloudflare-config/#4-cloudflare-tunnel-id-optional-in-configsh-required-in-start-productionsh","title":"4. Cloudflare Tunnel ID (Optional in config.sh, Required in start-production.sh)","text":"

Automatic Configuration of Tunnel

The start-production.sh script will automatically create a tunnel and system service for Cloudflare.

"},{"location":"config/cloudflare-config/#summary-of-required-credentials","title":"Summary of Required Credentials:","text":"
# In .env file\nCF_API_TOKEN=your_cloudflare_api_token\nCF_ZONE_ID=your_cloudflare_zone_id\nCF_ACCOUNT_ID=your_cloudflare_account_id\nCF_TUNNEL_ID=will_be_set_by_start_production  # This will be set by start-production.sh\n
"},{"location":"config/cloudflare-config/#notes","title":"Notes:","text":""},{"location":"config/coder/","title":"Coder Server Configuration","text":"

This section describes the configuration and features of the code-server environment.

"},{"location":"config/coder/#accessing-code-server","title":"Accessing Code Server","text":""},{"location":"config/coder/#retrieving-the-code-server-password","title":"Retrieving the Code Server Password","text":"

After the first build, the code-server password is stored in:

configs/code-server/.config/code-server/config.yaml\n

Look for the password: field in that file. For example:

password: 0c0dca951a2d12eff1665817\n

Note: It is recommended not to change this password manually, as it is securely generated.

"},{"location":"config/coder/#main-configuration-options","title":"Main Configuration Options","text":""},{"location":"config/coder/#installed-tools-and-features","title":"Installed Tools and Features","text":"

The code-server environment includes:

"},{"location":"config/coder/#using-mkdocs","title":"Using MkDocs","text":"

The virtual environment for MkDocs is automatically added to your PATH. You can run MkDocs commands directly, or use the provided script. For example, to build the site, from a clean terminal we would rung:

cd mkdocs \nmkdocs build\n
"},{"location":"config/coder/#claude-code-integration","title":"Claude Code Integration","text":"

The code-server environment comes with Claude Code (@anthropic-ai/claude-code) globally installed via npm.

"},{"location":"config/coder/#what-is-claude-code","title":"What is Claude Code?","text":"

Claude Code is an AI-powered coding assistant by Anthropic, designed to help you write, refactor, and understand code directly within your development environment.

"},{"location":"config/coder/#usage","title":"Usage","text":"

Note: Claude Code requires an API key or account with Anthropic for full functionality. Refer to the extension settings for configuration.

"},{"location":"config/coder/#call-claude","title":"Call Claude","text":"

To use claude simply type claude into the terminal and follow instructions.

claude\n
"},{"location":"config/coder/#shell-environment","title":"Shell Environment","text":"

The .bashrc is configured to include the MkDocs virtual environment and user-local binaries in your PATH for convenience.

"},{"location":"config/coder/#code-navigation-and-editing-features","title":"Code Navigation and Editing Features","text":"

The code-server environment provides robust code navigation and editing features, including:

"},{"location":"config/coder/#collaboration-features","title":"Collaboration Features","text":"

Code-server includes features to support collaboration:

"},{"location":"config/coder/#security-considerations","title":"Security Considerations","text":"

When using code-server, consider the following security aspects:

"},{"location":"config/coder/#ollama-integration","title":"Ollama Integration","text":"

The code-server environment includes Ollama, a tool for running large language models locally on your machine.

"},{"location":"config/coder/#what-is-ollama","title":"What is Ollama?","text":"

Ollama is a lightweight, extensible framework for building and running language models locally. It provides a simple API for creating, running, and managing models, making it easy to integrate AI capabilities into your development workflow without relying on external services.

"},{"location":"config/coder/#getting-started-with-ollama","title":"Getting Started with Ollama","text":""},{"location":"config/coder/#staring-ollama","title":"Staring Ollama","text":"

For ollama to be available, you need to open a terminal and run:

ollama serve\n

This will start the ollama server and you can then proceed to pulling a model and chatting.

"},{"location":"config/coder/#pulling-a-model","title":"Pulling a Model","text":"

To get started, you'll need to pull a model. For development and testing, we recommend starting with a smaller model like Gemma 2B:

ollama pull gemma2:2b\n

For even lighter resource usage, you can use the 1B parameter version:

ollama pull gemma2:1b\n
"},{"location":"config/coder/#running-a-model","title":"Running a Model","text":"

Once you've pulled a model, you can start an interactive session:

ollama run gemma2:2b\n
"},{"location":"config/coder/#available-models","title":"Available Models","text":"

Popular models available through Ollama include:

"},{"location":"config/coder/#using-ollama-in-your-development-workflow","title":"Using Ollama in Your Development Workflow","text":""},{"location":"config/coder/#api-access","title":"API Access","text":"

Ollama provides a REST API that runs on http://localhost:11434 by default. You can integrate this into your applications:

curl http://localhost:11434/api/generate -d '{\n  \"model\": \"gemma2:2b\",\n  \"prompt\": \"Write a Python function to calculate fibonacci numbers\",\n  \"stream\": false\n}'\n
"},{"location":"config/coder/#model-management","title":"Model Management","text":"

List installed models:

ollama list\n

Remove a model:

ollama rm gemma2:2b\n

Show model information:

ollama show gemma2:2b\n

"},{"location":"config/coder/#resource-considerations","title":"Resource Considerations","text":""},{"location":"config/coder/#integration-with-development-tools","title":"Integration with Development Tools","text":"

Ollama can be integrated with various development tools and editors through its API, enabling features like:

For more information, visit the Ollama documentation.

For more detailed information on configuring and using code-server, refer to the official code-server documentation.

"},{"location":"config/map/","title":"Map Configuration","text":"

The Map system is a containerized web application that visualizes geographic data from NocoDB on an interactive map using Leaflet.js. It's designed for canvassing applications and community organizing.

"},{"location":"config/map/#features","title":"Features","text":""},{"location":"config/map/#setup-process-overview","title":"Setup Process Overview","text":"

The setup process involves several steps that must be completed in order:

  1. Get NocoDB API Token - Create an API token in your NocoDB instance
  2. Configure Environment - Update the .env file with your NocoDB details
  3. Auto-Create Database Structure - Run the build script to create required tables
  4. Get Table URLs - Find and copy the URLs for the newly created tables
  5. Update Environment with URLs - Add the table URLs to your .env file
  6. Build and Deploy - Build the Docker image and start the application
"},{"location":"config/map/#prerequisites","title":"Prerequisites","text":""},{"location":"config/map/#step-1-get-nocodb-api-token","title":"Step 1: Get NocoDB API Token","text":"
  1. Login to your NocoDB instance
  2. Click your user icon \u2192 Account Settings
  3. Go to the API Tokens tab
  4. Click Create new token
  5. Set the following permissions:
  6. Read: Yes
  7. Write: Yes
  8. Delete: Yes (optional, for admin functions)
  9. Copy the generated token - you'll need it for the next step

Token Security

Keep your API token secure and never commit it to version control. The token provides full access to your NocoDB data.

"},{"location":"config/map/#step-2-configure-environment","title":"Step 2: Configure Environment","text":"

Edit the .env file in the map/ directory:

# NocoDB API Configuration\nNOCODB_API_URL=https://your-nocodb-instance.com/api/v1\nNOCODB_API_TOKEN=your-api-token-here\n\n# These URLs will be populated after running build-nocodb.sh\nNOCODB_VIEW_URL=\nNOCODB_LOGIN_SHEET=\nNOCODB_SETTINGS_SHEET=\n\n# Server Configuration\nPORT=3000\nNODE_ENV=production\n\n# Session Secret (generate with: openssl rand -hex 32)\nSESSION_SECRET=your-secure-random-string\n\n# Map Defaults (Edmonton, Alberta, Canada)\nDEFAULT_LAT=53.5461\nDEFAULT_LNG=-113.4938\nDEFAULT_ZOOM=11\n\n# Optional: Map Boundaries (prevents users from adding points outside area)\n# BOUND_NORTH=53.7\n# BOUND_SOUTH=53.4\n# BOUND_EAST=-113.3\n# BOUND_WEST=-113.7\n\n# Production Settings\nTRUST_PROXY=true\nCOOKIE_DOMAIN=.yourdomain.com\nALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000\n
"},{"location":"config/map/#required-configuration","title":"Required Configuration","text":""},{"location":"config/map/#optional-configuration","title":"Optional Configuration","text":""},{"location":"config/map/#step-3-auto-create-database-structure","title":"Step 3: Auto-Create Database Structure","text":"

The build-nocodb.sh script will automatically create the required tables in your NocoDB instance.

cd map\nchmod +x build-nocodb.sh\n./build-nocodb.sh\n
"},{"location":"config/map/#what-the-script-creates","title":"What the Script Creates","text":"

The script creates three tables with the following structure:

"},{"location":"config/map/#1-locations-table","title":"1. Locations Table","text":"

Main table for storing map data:

"},{"location":"config/map/#2-login-table","title":"2. Login Table","text":"

User authentication table:

"},{"location":"config/map/#3-settings-table","title":"3. Settings Table","text":"

Admin configuration table:

"},{"location":"config/map/#default-data","title":"Default Data","text":"

The script also creates: - A default admin user (admin@example.com) - A default start location setting

"},{"location":"config/map/#step-4-get-table-urls","title":"Step 4: Get Table URLs","text":"

After the script completes successfully:

  1. Login to your NocoDB instance
  2. Navigate to your project (should be named \"Map Viewer Project\")
  3. For each table, get the view URL:
  4. Click on the table name
  5. Copy the URL from your browser's address bar
  6. The URL should look like: https://your-nocodb.com/dashboard/#/nc/project-id/table-id

You need URLs for: - Locations table \u2192 NOCODB_VIEW_URL - Login table \u2192 NOCODB_LOGIN_SHEET - Settings table \u2192 NOCODB_SETTINGS_SHEET

"},{"location":"config/map/#step-5-update-environment-with-urls","title":"Step 5: Update Environment with URLs","text":"

Edit your .env file and add the table URLs:

# Update these with the actual URLs from your NocoDB instance\nNOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id\nNOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id\nNOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id\n

URL Format

Make sure to use the complete dashboard URLs, not the API URLs. The application will automatically extract the project and table IDs from these URLs.

"},{"location":"config/map/#step-6-build-and-deploy","title":"Step 6: Build and Deploy","text":"

Build the Docker image and start the application:

# Build the Docker image\ndocker-compose build\n\n# Start the application\ndocker-compose up -d\n
"},{"location":"config/map/#verify-deployment","title":"Verify Deployment","text":"
  1. Check that the container is running:

    docker-compose ps\n

  2. Check the logs:

    docker-compose logs -f map-viewer\n

  3. Access the application at http://localhost:3000 (or your configured domain)

"},{"location":"config/map/#using-the-map-system","title":"Using the Map System","text":""},{"location":"config/map/#user-interface","title":"User Interface","text":""},{"location":"config/map/#main-map-view","title":"Main Map View","text":""},{"location":"config/map/#location-markers","title":"Location Markers","text":""},{"location":"config/map/#adding-locations","title":"Adding Locations","text":"
  1. Click on the map where you want to add a location
  2. Fill out the form with contact information
  3. Select support level and sign information
  4. Add any relevant notes
  5. Click \"Save Location\"
"},{"location":"config/map/#authentication","title":"Authentication","text":""},{"location":"config/map/#user-login","title":"User Login","text":""},{"location":"config/map/#admin-access","title":"Admin Access","text":""},{"location":"config/map/#admin-panel-features","title":"Admin Panel Features","text":""},{"location":"config/map/#start-location-configuration","title":"Start Location Configuration","text":""},{"location":"config/map/#walk-sheet-generator","title":"Walk Sheet Generator","text":""},{"location":"config/map/#api-endpoints","title":"API Endpoints","text":""},{"location":"config/map/#public-endpoints","title":"Public Endpoints","text":""},{"location":"config/map/#authentication-endpoints","title":"Authentication Endpoints","text":""},{"location":"config/map/#admin-endpoints-requires-admin-privileges","title":"Admin Endpoints (requires admin privileges)","text":""},{"location":"config/map/#troubleshooting","title":"Troubleshooting","text":""},{"location":"config/map/#common-issues","title":"Common Issues","text":""},{"location":"config/map/#locations-not-showing","title":"Locations not showing","text":""},{"location":"config/map/#cannot-add-locations","title":"Cannot add locations","text":""},{"location":"config/map/#authentication-issues","title":"Authentication issues","text":""},{"location":"config/map/#build-script-failures","title":"Build script failures","text":""},{"location":"config/map/#development-mode","title":"Development Mode","text":"

For development and debugging:

cd map/app\nnpm install\nnpm run dev\n

This will start the application with hot reload and detailed logging.

"},{"location":"config/map/#logs-and-monitoring","title":"Logs and Monitoring","text":"

View application logs:

docker-compose logs -f map-viewer\n

Check health status:

curl http://localhost:3000/health\n

"},{"location":"config/map/#security-considerations","title":"Security Considerations","text":"
  1. API Token Security: Keep tokens secure and rotate regularly
  2. HTTPS: Use HTTPS in production
  3. CORS Configuration: Set appropriate ALLOWED_ORIGINS
  4. Cookie Security: Configure COOKIE_DOMAIN properly
  5. Input Validation: All inputs are validated server-side
  6. Rate Limiting: API endpoints have rate limiting
  7. Session Security: Use a strong SESSION_SECRET
"},{"location":"config/map/#maintenance","title":"Maintenance","text":""},{"location":"config/map/#regular-updates","title":"Regular Updates","text":"
# Stop the application\ndocker-compose down\n\n# Pull updates (if using git)\ngit pull origin main\n\n# Rebuild and restart\ndocker-compose build\ndocker-compose up -d\n
"},{"location":"config/map/#backup-considerations","title":"Backup Considerations","text":""},{"location":"config/map/#performance-tips","title":"Performance Tips","text":""},{"location":"config/map/#support","title":"Support","text":"

For issues or questions: 1. Check the troubleshooting section above 2. Review NocoDB documentation 3. Check Docker and Docker Compose documentation 4. Open an issue on GitHub

"},{"location":"config/mkdocs/","title":"MkDocs Customization & Features Overview","text":"

BNKops has been building our own features, widgets, and css styles for MKdocs material theme.

This document explains the custom styling, repository widgets, and key features enabled in this MkDocs site.

For more info on how to build your site see Site Build

"},{"location":"config/mkdocs/#using-the-repository-widget-in-documentation","title":"Using the Repository Widget in Documentation","text":"

You can embed repository widgets directly in your Markdown documentation to display live repository stats and metadata. To do this, add a div with the appropriate class and data-repo attribute for the repository you want to display.

Example (for a Gitea repository):

<div class=\"gitea-widget\" data-repo=\"admin/changemaker.lite\"></div>\n

This will render a styled card with information about the admin/changemaker.lite repository:

Options: You can control the widget display with additional data attributes: - data-show-description=\"false\" \u2014 Hide the description - data-show-language=\"false\" \u2014 Hide the language - data-show-last-update=\"false\" \u2014 Hide the last update date

Example with options:

<div class=\"gitea-widget\" data-repo=\"admin/changemaker.lite\" data-show-description=\"false\"></div>\n

For GitHub repositories, use the github-widget class:

<div class=\"github-widget\" data-repo=\"lyqht/mini-qr\"></div>\n

"},{"location":"config/mkdocs/#custom-css-styling-stylesheetsextracss","title":"Custom CSS Styling (stylesheets/extra.css)","text":"

The extra.css file provides extensive custom styling for the site, including:

"},{"location":"config/mkdocs/#repository-widgets","title":"Repository Widgets","text":""},{"location":"config/mkdocs/#data-generation-hooksrepo_widget_hookpy","title":"Data Generation (hooks/repo_widget_hook.py)","text":""},{"location":"config/mkdocs/#github-widget-javascriptsgithub-widgetjs","title":"GitHub Widget (javascripts/github-widget.js)","text":""},{"location":"config/mkdocs/#gitea-widget-javascriptsgitea-widgetjs","title":"Gitea Widget (javascripts/gitea-widget.js)","text":""},{"location":"config/mkdocs/#mkdocs-features-mkdocsyml","title":"MkDocs Features (mkdocs.yml)","text":"

Key features and plugins enabled:

"},{"location":"config/mkdocs/#summary","title":"Summary","text":"

This MkDocs site is highly customized for developer documentation, with visually rich repository widgets, improved code and table rendering, and a modern, responsive UI. All repository stats are fetched at build time for performance and reliability.

"},{"location":"how%20to/canvass/","title":"Canvas","text":"

This is BNKops canvassing how to! In the following document, you will find all sorts of tips and tricks for door knocking, canvassing, and using the BNKops canvassing app.

"},{"location":"manual/","title":"Manuals","text":"

The following are manuals, some accompanied by videos, on the use of the system.

"},{"location":"manual/map/","title":"Map Manual","text":"

Quick Tips:

"},{"location":"manual/map/#how-to-add-new-location-video","title":"How to add new location - Video","text":""},{"location":"phil/","title":"Philosophy: Your Secrets, Your Power, Your Movement","text":""},{"location":"phil/#the-question-that-changes-everything","title":"The Question That Changes Everything!","text":"

If you are a political actor, who do you trust with your secrets?

This isn't just a technical question\u2014it's the core political question of our time. Every email you send, every document you create, every contact list you build, every strategy you develop: where does it live? Who owns the servers? Who has the keys?

"},{"location":"phil/#the-corporate-extraction-machine","title":"The Corporate Extraction Machine","text":""},{"location":"phil/#how-they-hook-you","title":"How They Hook You","text":"

Corporate software companies have perfected the art of digital snake oil sales:

  1. Free Trials - They lure you in with \"free\" accounts
  2. Feature Creep - Essential features require paid tiers
  3. Data Lock-In - Your data becomes harder to export
  4. Price Escalation - $40/month becomes $750/month as you grow
  5. Surveillance Integration - Your organizing becomes their intelligence
"},{"location":"phil/#the-real-product","title":"The Real Product","text":"

You Are Not the Customer

If you're not paying for the product, you ARE the product. But even when you are paying, you're often still the product.

Corporate platforms don't make money from your subscription fees\u2014they make money from:

"},{"location":"phil/#the-bnkops-alternative","title":"The BNKops Alternative","text":""},{"location":"phil/#who-we-are","title":"Who We Are","text":"

BNKops is a cooperative based in amiskwaciy-w\u00e2skahikan (Edmonton, Alberta) on Treaty 6 territory. We're not a corporation\u2014we're a collective of skilled organizers, developers, and community builders who believe technology should serve liberation, not oppression.

"},{"location":"phil/#our-principles","title":"Our Principles","text":""},{"location":"phil/#liberation-first","title":"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f \ud83c\udff3\ufe0f\u200d\ud83c\udf08 \ud83c\uddf5\ud83c\uddf8 Liberation First","text":"

Technology that centers the most marginalized voices and fights for collective liberation. We believe strongly that the medium is the message; if you the use the medium of fascists, what does that say about your movement?

"},{"location":"phil/#community-over-profit","title":"\ud83e\udd1d Community Over Profit","text":"

We operate as a cooperative because we believe in shared ownership and democratic decision-making. No venture capitalists, no shareholders, no extraction.

"},{"location":"phil/#data-sovereignty","title":"\u26a1 Data Sovereignty","text":"

Your data belongs to you and your community. We build tools that let you own your digital infrastructure completely.

"},{"location":"phil/#security-culture","title":"\ud83d\udd12 Security Culture","text":"

Real security comes from community control, not corporate promises. We integrate security culture practices into our technology design.

"},{"location":"phil/#the-changemaker-difference","title":"The Changemaker Difference","text":""},{"location":"phil/#traditional-corporate-flow","title":"Traditional Corporate Flow","text":"
Your Data \u2192 Corporate Server \u2192 Surveillance \u2192 Profit \u2192 Your Oppression\n
"},{"location":"phil/#changemaker-flow","title":"Changemaker Flow","text":"
Your Data \u2192 Your Server \u2192 Your Community \u2192 Your Power \u2192 Liberation\n
"},{"location":"phil/#why-this-matters","title":"Why This Matters","text":"

When you control your technology infrastructure:

"},{"location":"phil/#the-philosophy-in-practice","title":"The Philosophy in Practice","text":""},{"location":"phil/#security-culture-meets-technology","title":"Security Culture Meets Technology","text":"

Traditional security culture asks: \"Who needs to know this information?\"

Digital security culture asks: \"Who controls the infrastructure where this information lives?\"

"},{"location":"phil/#community-technology","title":"Community Technology","text":"

We believe in community technology - tools that:

"},{"location":"phil/#prefigurative-politics","title":"Prefigurative Politics","text":"

The tools we use shape the movements we build. Corporate tools create corporate movements\u2014hierarchical, surveilled, and dependent. Community-controlled tools create community-controlled movements\u2014democratic, secure, and sovereign.

"},{"location":"phil/#common-questions","title":"Common Questions","text":""},{"location":"phil/#isnt-this-just-for-tech-people","title":"\"Isn't this just for tech people?\"","text":"

No. We specifically designed Changemaker Lite for organizers, activists, and movement builders who may not have technical backgrounds. Our philosophy is that everyone deserves digital sovereignty, not just people with computer science degrees.

This is not to say that you won't need to learn! These tools are just that; tools. They have no fancy or white-labeled marketing and are technical in nature. You will need to learn to use them, just as any worker needs to learn the power tools they use on the job.

"},{"location":"phil/#what-about-convenience","title":"\"What about convenience?\"","text":"

Corporate platforms are convenient because they've extracted billions of dollars from users to fund that convenience. When you own your tools, there's a learning curve\u2014but it's the same learning curve as learning to organize, learning to build power, learning to create change.

"},{"location":"phil/#cant-we-just-use-corporate-tools-carefully","title":"\"Can't we just use corporate tools carefully?\"","text":"

Would you hold your most sensitive organizing meetings in a room owned by your opposition? Would you store your membership lists in filing cabinets at a corporation that profits from surveillance? Digital tools are the same.

"},{"location":"phil/#what-about-security","title":"\"What about security?\"","text":"

Real security comes from community control, not corporate promises. When you control your infrastructure:

"},{"location":"phil/#historical-context","title":"Historical Context","text":""},{"location":"phil/#learning-from-past-struggles","title":"Learning from Past Struggles","text":"

Every liberation movement has had to solve the problem of secure communication and information sharing:

The internet should expand these traditions, not replace them with corporate surveillance.

"},{"location":"phil/#the-surveillance-capitalism-trap","title":"The Surveillance Capitalism Trap","text":"

As Shoshana Zuboff documents in \"The Age of Surveillance Capitalism,\" we're living through a new form of capitalism that extracts value from human experience itself. Political movements are particularly valuable targets because:

"},{"location":"phil/#taking-action","title":"Taking Action","text":""},{"location":"phil/#start-where-you-are","title":"Start Where You Are","text":"

You don't have to replace everything at once. Start with one tool, one campaign, one project. Learn the technology alongside your organizing.

"},{"location":"phil/#build-community-capacity","title":"Build Community Capacity","text":"

The goal isn't individual self-sufficiency\u2014it's community technological sovereignty. Share skills, pool resources, learn together.

"},{"location":"phil/#connect-with-others","title":"Connect with Others","text":"

You're not alone in this. The free and open source software community, the digital security community, and the appropriate technology movement are all working on similar problems.

"},{"location":"phil/#remember-why","title":"Remember Why","text":"

This isn't about technology for its own sake. It's about building the infrastructure for the world we want to see\u2014where communities have power, where people control their own data, where technology serves liberation.

"},{"location":"phil/#resources-for-deeper-learning","title":"Resources for Deeper Learning","text":""},{"location":"phil/#essential-reading","title":"Essential Reading","text":""},{"location":"phil/#community-resources","title":"Community Resources","text":""},{"location":"phil/#technical-learning","title":"Technical Learning","text":"

This philosophy document is a living document. Contribute your thoughts, experiences, and improvements through the BNKops documentation platform.

"},{"location":"phil/cost-comparison/","title":"Cost Comparison: Corporation vs. Community","text":""},{"location":"phil/cost-comparison/#the-true-cost-of-corporate-dependency","title":"The True Cost of Corporate Dependency","text":"

When movements choose corporate software, they're not just paying subscription fees\u2014they're paying with their power, their privacy, and their future. Let's break down the real costs.

"},{"location":"phil/cost-comparison/#monthly-cost-analysis","title":"Monthly Cost Analysis","text":""},{"location":"phil/cost-comparison/#small-campaign-50-supporters-5000-emailsmonth","title":"Small Campaign (50 supporters, 5,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $59/month Listmonk $0* Database & CRM Airtable Pro $240/month NocoDB $0* Website Hosting Squarespace $40/month Static Server $0* Documentation Notion Team $96/month MkDocs $0* Development GitHub Codespaces $87/month Code Server $0* Automation Zapier Professional $73/month n8n $0* File Storage Google Workspace $72/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $667/month $50/month

*Included in base Changemaker Lite hosting cost \u2020Privacy costs are incalculable but include surveillance, data sales, and community manipulation

"},{"location":"phil/cost-comparison/#medium-campaign-500-supporters-50000-emailsmonth","title":"Medium Campaign (500 supporters, 50,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $299/month Listmonk $0* Database & CRM Airtable Pro $600/month NocoDB $0* Website Hosting Squarespace $65/month Static Server $0* Documentation Notion Team $240/month MkDocs $0* Development GitHub Codespaces $174/month Code Server $0* Automation Zapier Professional $146/month n8n $0* File Storage Google Workspace $144/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $1,668/month $75/month"},{"location":"phil/cost-comparison/#large-campaign-5000-supporters-500000-emailsmonth","title":"Large Campaign (5,000 supporters, 500,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $1,499/month Listmonk $0* Database & CRM Airtable Pro $1,200/month NocoDB $0* Website Hosting Squarespace + CDN $120/month Static Server $0* Documentation Notion Team $480/month MkDocs $0* Development GitHub Codespaces $348/month Code Server $0* Automation Zapier Professional $292/month n8n $0* File Storage Google Workspace $288/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $4,227/month $150/month"},{"location":"phil/cost-comparison/#annual-savings-breakdown","title":"Annual Savings Breakdown","text":""},{"location":"phil/cost-comparison/#3-year-cost-comparison","title":"3-Year Cost Comparison","text":"Campaign Size Corporate Total Changemaker Total Savings Small $24,012 $1,800 $22,212 Medium $60,048 $2,700 $57,348 Large $152,172 $5,400 $146,772"},{"location":"phil/cost-comparison/#hidden-costs-of-corporate-software","title":"Hidden Costs of Corporate Software","text":""},{"location":"phil/cost-comparison/#what-you-cant-put-a-price-on","title":"What You Can't Put a Price On","text":""},{"location":"phil/cost-comparison/#privacy-violations","title":"Privacy Violations","text":""},{"location":"phil/cost-comparison/#political-manipulation","title":"Political Manipulation","text":""},{"location":"phil/cost-comparison/#movement-disruption","title":"Movement Disruption","text":""},{"location":"phil/cost-comparison/#the-changemaker-advantage","title":"The Changemaker Advantage","text":""},{"location":"phil/cost-comparison/#what-you-get-for-50-150month","title":"What You Get for $50-150/month","text":""},{"location":"phil/cost-comparison/#complete-infrastructure","title":"Complete Infrastructure","text":""},{"location":"phil/cost-comparison/#true-ownership","title":"True Ownership","text":""},{"location":"phil/cost-comparison/#community-support","title":"Community Support","text":""},{"location":"phil/cost-comparison/#the-compound-effect","title":"The Compound Effect","text":""},{"location":"phil/cost-comparison/#year-over-year-savings","title":"Year Over Year Savings","text":"

Corporate software costs grow exponentially: - Year 1: \"Starter\" pricing to hook you - Year 2: Feature limits force tier upgrades - Year 3: Usage growth triggers premium pricing - Year 4: Platform changes force expensive migrations - Year 5: Lock-in enables arbitrary price increases

Changemaker Lite costs grow linearly with actual infrastructure needs: - Year 1: Base infrastructure costs - Year 2: Modest increases for storage/bandwidth only - Year 3: Scale only with actual technical requirements - Year 4: Community-driven improvements at no extra cost - Year 5: Established infrastructure with declining per-user costs

"},{"location":"phil/cost-comparison/#10-year-projection","title":"10-Year Projection","text":"Year Corporate (Medium Campaign) Changemaker Lite Annual Savings 1 $20,016 $900 $19,116 2 $22,017 $900 $21,117 3 $24,219 $1,080 $23,139 4 $26,641 $1,080 $25,561 5 $29,305 $1,260 $28,045 6 $32,235 $1,260 $30,975 7 $35,459 $1,440 $34,019 8 $39,005 $1,440 $37,565 9 $42,905 $1,620 $41,285 10 $47,196 $1,620 $45,576 TOTAL $318,998 $12,600 $306,398"},{"location":"phil/cost-comparison/#calculate-your-own-savings","title":"Calculate Your Own Savings","text":""},{"location":"phil/cost-comparison/#current-corporate-costs-worksheet","title":"Current Corporate Costs Worksheet","text":"

Email Marketing: $____/month Database/CRM: $____/month Website Hosting: $____/month Documentation: $____/month Development Tools: $____/month Automation: $____/month File Storage: $____/month Other SaaS: $____/month

Monthly Total: $____ Annual Total: $____

Changemaker Alternative: $50-150/month Your Annual Savings: $____

"},{"location":"phil/cost-comparison/#beyond-the-numbers","title":"Beyond the Numbers","text":""},{"location":"phil/cost-comparison/#what-movements-do-with-their-savings","title":"What Movements Do With Their Savings","text":"

The money saved by choosing community-controlled technology doesn't disappear\u2014it goes directly back into movement building:

"},{"location":"phil/cost-comparison/#making-the-switch","title":"Making the Switch","text":""},{"location":"phil/cost-comparison/#transition-strategy","title":"Transition Strategy","text":"

You don't have to switch everything at once:

  1. Start with documentation - Move your knowledge base to MkDocs
  2. Add email infrastructure - Set up Listmonk for newsletters
  3. Build your database - Move contact management to NocoDB
  4. Automate connections - Use n8n to integrate everything
  5. Phase out corporate tools - Cancel subscriptions as you replicate functionality
"},{"location":"phil/cost-comparison/#investment-timeline","title":"Investment Timeline","text":""},{"location":"phil/cost-comparison/#roi-calculation","title":"ROI Calculation","text":"

Most campaigns recover their entire first-year investment in 60-90 days through subscription savings alone.

Ready to stop feeding your budget to corporate surveillance? Get started with Changemaker Lite today and take control of your digital infrastructure.

"},{"location":"services/","title":"Services","text":"

Changemaker Lite includes several powerful services that work together to provide a complete documentation and development platform. Each service is containerized and can be accessed through its dedicated port.

"},{"location":"services/#available-services","title":"Available Services","text":""},{"location":"services/#code-server","title":"Code Server","text":"

Port: 8888 | Visual Studio Code in your browser for remote development

"},{"location":"services/#listmonk","title":"Listmonk","text":"

Port: 9000 | Self-hosted newsletter and mailing list manager

"},{"location":"services/#postgresql","title":"PostgreSQL","text":"

Port: 5432 | Reliable database backend - Data persistence for Listmonk - ACID compliance - High performance - Backup and restore capabilities

"},{"location":"services/#mkdocs-material","title":"MkDocs Material","text":"

Port: 4000 | Documentation site generator with live preview

"},{"location":"services/#static-site-server","title":"Static Site Server","text":"

Port: 4001 | Nginx-powered static site hosting - High-performance serving - Built documentation hosting - Caching and compression - Security headers

"},{"location":"services/#n8n","title":"n8n","text":"

Port: 5678 | Workflow automation tool

"},{"location":"services/#nocodb","title":"NocoDB","text":"

Port: 8090 | No-code database platform

"},{"location":"services/#homepage","title":"Homepage","text":"

Port: 3010 | Modern dashboard for all services

"},{"location":"services/#gitea","title":"Gitea","text":"

Port: 3030 | Self-hosted Git service

"},{"location":"services/#mini-qr","title":"Mini QR","text":"

Port: 8089 | Simple QR code generator service

"},{"location":"services/#map","title":"Map","text":"

Port: 3000 | Canvassing and community organizing application

"},{"location":"services/#service-architecture","title":"Service Architecture","text":"
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   Homepage      \u2502    \u2502   Code Server   \u2502    \u2502     MkDocs      \u2502\n\u2502     :3010       \u2502    \u2502     :8888       \u2502    \u2502     :4000       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Static Server   \u2502    \u2502    Listmonk     \u2502    \u2502      n8n        \u2502\n\u2502     :4001       \u2502    \u2502     :9000       \u2502    \u2502     :5678       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502     NocoDB      \u2502    \u2502 PostgreSQL      \u2502    \u2502 PostgreSQL      \u2502\n\u2502     :8090       \u2502    \u2502 (listmonk-db)   \u2502    \u2502 (root_db)       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2502     :5432       \u2502    \u2502     :5432       \u2502\n                      \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502      Map        \u2502\n\u2502     :3000       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n
"},{"location":"services/code-server/","title":"Code Server","text":""},{"location":"services/code-server/#overview","title":"Overview","text":"

Code Server provides a full Visual Studio Code experience in your web browser, allowing you to develop from any device. It runs on your server and provides access to your development environment through a web interface.

"},{"location":"services/code-server/#features","title":"Features","text":""},{"location":"services/code-server/#access","title":"Access","text":""},{"location":"services/code-server/#configuration","title":"Configuration","text":""},{"location":"services/code-server/#environment-variables","title":"Environment Variables","text":""},{"location":"services/code-server/#volumes","title":"Volumes","text":""},{"location":"services/code-server/#usage","title":"Usage","text":"
  1. Access Code Server at http://localhost:8888
  2. Open the /home/coder/mkdocs/ workspace
  3. Start editing your documentation files
  4. Install extensions as needed
  5. Use the integrated terminal for commands
"},{"location":"services/code-server/#useful-extensions","title":"Useful Extensions","text":"

Consider installing these extensions for better documentation work:

"},{"location":"services/code-server/#official-documentation","title":"Official Documentation","text":"

For more detailed information, visit the official Code Server documentation.

"},{"location":"services/gitea/","title":"Gitea","text":"

Self-hosted Git service for collaborative development.

"},{"location":"services/gitea/#overview","title":"Overview","text":"

Gitea is a lightweight, self-hosted Git service similar to GitHub, GitLab, and Bitbucket. It provides a web interface for managing repositories, issues, pull requests, and more.

"},{"location":"services/gitea/#features","title":"Features","text":""},{"location":"services/gitea/#access","title":"Access","text":""},{"location":"services/gitea/#configuration","title":"Configuration","text":""},{"location":"services/gitea/#environment-variables","title":"Environment Variables","text":""},{"location":"services/gitea/#volumes","title":"Volumes","text":""},{"location":"services/gitea/#usage","title":"Usage","text":"
  1. Access Gitea at http://localhost:${GITEA_WEB_PORT:-3030}
  2. Register or log in as an admin user
  3. Create or import repositories
  4. Collaborate with your team
"},{"location":"services/gitea/#official-documentation","title":"Official Documentation","text":"

For more details, visit the official Gitea documentation.

"},{"location":"services/homepage/","title":"Homepage","text":"

Modern dashboard for accessing all your self-hosted services.

"},{"location":"services/homepage/#overview","title":"Overview","text":"

Homepage is a modern, fully static, fast, secure fully configurable application dashboard with integrations for over 100 services. It provides a beautiful and customizable interface to access all your Changemaker Lite services from a single location.

"},{"location":"services/homepage/#features","title":"Features","text":""},{"location":"services/homepage/#access","title":"Access","text":""},{"location":"services/homepage/#configuration","title":"Configuration","text":""},{"location":"services/homepage/#environment-variables","title":"Environment Variables","text":""},{"location":"services/homepage/#configuration-files","title":"Configuration Files","text":"

Homepage uses YAML configuration files located in ./configs/homepage/:

"},{"location":"services/homepage/#volumes","title":"Volumes","text":""},{"location":"services/homepage/#changemaker-lite-services","title":"Changemaker Lite Services","text":"

Homepage is pre-configured with all Changemaker Lite services:

"},{"location":"services/homepage/#essential-tools","title":"Essential Tools","text":""},{"location":"services/homepage/#content-documentation","title":"Content & Documentation","text":""},{"location":"services/homepage/#automation-data","title":"Automation & Data","text":""},{"location":"services/homepage/#customization","title":"Customization","text":""},{"location":"services/homepage/#adding-custom-services","title":"Adding Custom Services","text":"

Edit configs/homepage/services.yaml to add new services:

- Custom Category:\n    - My Service:\n        href: http://localhost:8080\n        description: Custom service description\n        icon: mdi-application\n        widget:\n          type: ping\n          url: http://localhost:8080\n
"},{"location":"services/homepage/#custom-icons","title":"Custom Icons","text":"

Add custom icons to ./assets/icons/ directory and reference them in services.yaml:

icon: /icons/my-custom-icon.png\n
"},{"location":"services/homepage/#themes-and-styling","title":"Themes and Styling","text":"

Modify configs/homepage/settings.yaml to customize appearance:

theme: dark  # or light\ncolor: purple  # slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose\n
"},{"location":"services/homepage/#widgets","title":"Widgets","text":"

Enable live monitoring widgets in configs/homepage/services.yaml:

- Service Name:\n    widget:\n      type: docker\n      container: container-name\n      server: my-docker\n
"},{"location":"services/homepage/#service-monitoring","title":"Service Monitoring","text":"

Homepage can display real-time status information for your services:

"},{"location":"services/homepage/#docker-integration","title":"Docker Integration","text":"

Homepage monitors Docker containers automatically when configured:

  1. Ensure Docker socket is mounted (/var/run/docker.sock)
  2. Configure container mappings in docker.yaml
  3. Add widget configurations to services.yaml
"},{"location":"services/homepage/#security-considerations","title":"Security Considerations","text":""},{"location":"services/homepage/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/homepage/#common-issues","title":"Common Issues","text":"

Configuration not loading: Check YAML syntax in configuration files

docker logs homepage-changemaker\n

Icons not displaying: Verify icon paths and file permissions

ls -la ./assets/icons/\n

Services not reachable: Verify network connectivity between containers

docker exec homepage-changemaker ping service-name\n

Widget data not updating: Check Docker socket permissions and container access

docker exec homepage-changemaker ls -la /var/run/docker.sock\n
"},{"location":"services/homepage/#configuration-examples","title":"Configuration Examples","text":""},{"location":"services/homepage/#basic-service-widget","title":"Basic Service Widget","text":"
- Code Server:\n    href: http://localhost:8888\n    description: VS Code in the browser\n    icon: code-server\n    widget:\n      type: docker\n      container: code-server-changemaker\n
"},{"location":"services/homepage/#custom-dashboard-layout","title":"Custom Dashboard Layout","text":"
# settings.yaml\nlayout:\n  style: columns\n  columns: 3\n\n# Responsive breakpoints\nresponsive:\n  mobile: 1\n  tablet: 2\n  desktop: 3\n
"},{"location":"services/homepage/#official-documentation","title":"Official Documentation","text":"

For comprehensive configuration guides and advanced features:

"},{"location":"services/listmonk/","title":"Listmonk","text":"

Self-hosted newsletter and mailing list manager.

"},{"location":"services/listmonk/#overview","title":"Overview","text":"

Listmonk is a modern, feature-rich newsletter and mailing list manager designed for high performance and easy management. It provides a complete solution for email campaigns, subscriber management, and analytics.

"},{"location":"services/listmonk/#features","title":"Features","text":""},{"location":"services/listmonk/#access","title":"Access","text":""},{"location":"services/listmonk/#configuration","title":"Configuration","text":""},{"location":"services/listmonk/#environment-variables","title":"Environment Variables","text":""},{"location":"services/listmonk/#database","title":"Database","text":"

Listmonk uses PostgreSQL as its backend database. The database is automatically configured through the docker-compose setup.

"},{"location":"services/listmonk/#uploads","title":"Uploads","text":""},{"location":"services/listmonk/#getting-started","title":"Getting Started","text":"
  1. Access Listmonk at http://localhost:9000
  2. Log in with your admin credentials
  3. Set up your first mailing list
  4. Configure SMTP settings for sending emails
  5. Import subscribers or create subscription forms
  6. Create your first campaign
"},{"location":"services/listmonk/#important-notes","title":"Important Notes","text":""},{"location":"services/listmonk/#official-documentation","title":"Official Documentation","text":"

For comprehensive guides and API documentation, visit: - Listmonk Documentation - GitHub Repository

"},{"location":"services/map/","title":"Map","text":"

Interactive map service for geospatial data visualization, powered by NocoDB and Leaflet.js.

"},{"location":"services/map/#overview","title":"Overview","text":"

The Map service provides an interactive web-based map for displaying, searching, and analyzing geospatial data from a NocoDB backend. It supports real-time geolocation, adding new locations, and is optimized for both desktop and mobile use.

"},{"location":"services/map/#features","title":"Features","text":""},{"location":"services/map/#access","title":"Access","text":""},{"location":"services/map/#configuration","title":"Configuration","text":"

All configuration is done via environment variables:

Variable Description Default NOCODB_API_URL NocoDB API base URL Required NOCODB_API_TOKEN API authentication token Required NOCODB_VIEW_URL Full NocoDB view URL Required PORT Server port 3000 DEFAULT_LAT Default map latitude 53.5461 DEFAULT_LNG Default map longitude -113.4938 DEFAULT_ZOOM Default map zoom level 11"},{"location":"services/map/#volumes","title":"Volumes","text":""},{"location":"services/map/#usage","title":"Usage","text":"
  1. Access the map at http://localhost:${MAP_PORT:-3000}
  2. Search for locations or addresses
  3. Add or view custom markers
  4. Analyze geospatial data as needed
"},{"location":"services/map/#nocodb-table-setup","title":"NocoDB Table Setup","text":""},{"location":"services/map/#required-columns","title":"Required Columns","text":""},{"location":"services/map/#form-fields-as-seen-in-the-interface","title":"Form Fields (as seen in the interface)","text":""},{"location":"services/map/#api-endpoints","title":"API Endpoints","text":""},{"location":"services/map/#security-considerations","title":"Security Considerations","text":""},{"location":"services/map/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/mini-qr/","title":"Mini QR","text":"

Simple QR code generator service.

"},{"location":"services/mini-qr/#overview","title":"Overview","text":"

Mini QR is a lightweight service for generating QR codes for URLs, text, or other data. It provides a web interface for quick QR code creation and download.

"},{"location":"services/mini-qr/#features","title":"Features","text":""},{"location":"services/mini-qr/#access","title":"Access","text":""},{"location":"services/mini-qr/#configuration","title":"Configuration","text":""},{"location":"services/mini-qr/#environment-variables","title":"Environment Variables","text":""},{"location":"services/mini-qr/#volumes","title":"Volumes","text":""},{"location":"services/mini-qr/#usage","title":"Usage","text":"
  1. Access Mini QR at http://localhost:${MINI_QR_PORT:-8089}
  2. Enter the text or URL to encode
  3. Download or share the generated QR code
"},{"location":"services/mkdocs/","title":"MkDocs Material","text":"

Modern documentation site generator with live preview.

Looking for more info on BNKops code-server integration?

\u2192 Code Server Configuration

"},{"location":"services/mkdocs/#overview","title":"Overview","text":"

MkDocs Material is a powerful documentation framework built on top of MkDocs, providing a beautiful Material Design theme and advanced features for creating professional documentation sites.

"},{"location":"services/mkdocs/#features","title":"Features","text":""},{"location":"services/mkdocs/#access","title":"Access","text":""},{"location":"services/mkdocs/#configuration","title":"Configuration","text":""},{"location":"services/mkdocs/#main-configuration","title":"Main Configuration","text":"

Configuration is managed through mkdocs.yml in the project root.

"},{"location":"services/mkdocs/#volumes","title":"Volumes","text":""},{"location":"services/mkdocs/#environment-variables","title":"Environment Variables","text":""},{"location":"services/mkdocs/#directory-structure","title":"Directory Structure","text":"
mkdocs/\n\u251c\u2500\u2500 mkdocs.yml          # Configuration file\n\u251c\u2500\u2500 docs/               # Documentation source\n\u2502   \u251c\u2500\u2500 index.md       # Homepage\n\u2502   \u251c\u2500\u2500 services/      # Service documentation\n\u2502   \u251c\u2500\u2500 blog/          # Blog posts\n\u2502   \u2514\u2500\u2500 overrides/     # Template overrides\n\u2514\u2500\u2500 site/              # Built static site\n
"},{"location":"services/mkdocs/#writing-documentation","title":"Writing Documentation","text":""},{"location":"services/mkdocs/#markdown-basics","title":"Markdown Basics","text":""},{"location":"services/mkdocs/#example-page","title":"Example Page","text":"
# Page Title\n\nThis is a sample documentation page.\n\n## Section\n\nContent goes here with **bold** and *italic* text.\n\n### Code Example\n\n```python\ndef hello_world():\n    print(\"Hello, World!\")\n

Note

This is an informational note.

## Building and Deployment\n\n### Development\n\nThe development server runs automatically with live reload.\n\n### Building Static Site\n\n```bash\ndocker exec mkdocs-changemaker mkdocs build\n

The built site will be available in the mkdocs/site/ directory.

"},{"location":"services/mkdocs/#customization","title":"Customization","text":""},{"location":"services/mkdocs/#themes-and-colors","title":"Themes and Colors","text":"

Customize appearance in mkdocs.yml:

theme:\n  name: material\n  palette:\n    primary: blue\n    accent: indigo\n
"},{"location":"services/mkdocs/#custom-css","title":"Custom CSS","text":"

Add custom styles in docs/stylesheets/extra.css.

"},{"location":"services/mkdocs/#official-documentation","title":"Official Documentation","text":"

For comprehensive MkDocs Material documentation: - MkDocs Material - MkDocs Documentation - Markdown Guide

"},{"location":"services/n8n/","title":"n8n","text":"

Workflow automation tool for connecting services and automating tasks.

"},{"location":"services/n8n/#overview","title":"Overview","text":"

n8n is a powerful workflow automation tool that allows you to connect various apps and services together. It provides a visual interface for creating automated workflows, making it easy to integrate different systems and automate repetitive tasks.

"},{"location":"services/n8n/#features","title":"Features","text":""},{"location":"services/n8n/#access","title":"Access","text":""},{"location":"services/n8n/#configuration","title":"Configuration","text":""},{"location":"services/n8n/#environment-variables","title":"Environment Variables","text":""},{"location":"services/n8n/#volumes","title":"Volumes","text":""},{"location":"services/n8n/#getting-started","title":"Getting Started","text":"
  1. Access n8n at http://localhost:5678
  2. Log in with your admin credentials
  3. Create your first workflow
  4. Add nodes for different services
  5. Configure connections between nodes
  6. Test and activate your workflow
"},{"location":"services/n8n/#common-use-cases","title":"Common Use Cases","text":""},{"location":"services/n8n/#documentation-automation","title":"Documentation Automation","text":""},{"location":"services/n8n/#email-campaign-integration","title":"Email Campaign Integration","text":""},{"location":"services/n8n/#database-management-with-nocodb","title":"Database Management with NocoDB","text":""},{"location":"services/n8n/#development-workflows","title":"Development Workflows","text":""},{"location":"services/n8n/#data-processing","title":"Data Processing","text":""},{"location":"services/n8n/#example-workflows","title":"Example Workflows","text":""},{"location":"services/n8n/#simple-webhook-to-email","title":"Simple Webhook to Email","text":"
Webhook \u2192 Email\n
"},{"location":"services/n8n/#scheduled-documentation-backup","title":"Scheduled Documentation Backup","text":"
Schedule \u2192 Read Files \u2192 Compress \u2192 Upload to Storage\n
"},{"location":"services/n8n/#git-integration","title":"Git Integration","text":"
Git Webhook \u2192 Process Changes \u2192 Update Documentation \u2192 Notify Team\n
"},{"location":"services/n8n/#security-considerations","title":"Security Considerations","text":""},{"location":"services/n8n/#integration-with-other-services","title":"Integration with Other Services","text":"

n8n can integrate with all services in your Changemaker Lite setup:

"},{"location":"services/n8n/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/n8n/#common-issues","title":"Common Issues","text":""},{"location":"services/n8n/#debugging","title":"Debugging","text":"
# Check container logs\ndocker logs n8n-changemaker\n\n# Access container shell\ndocker exec -it n8n-changemaker sh\n\n# Check workflow executions in the UI\n# Visit http://localhost:5678 \u2192 Executions\n
"},{"location":"services/n8n/#official-documentation","title":"Official Documentation","text":"

For comprehensive n8n documentation:

"},{"location":"services/nocodb/","title":"NocoDB","text":"

No-code database platform that turns any database into a smart spreadsheet.

"},{"location":"services/nocodb/#overview","title":"Overview","text":"

NocoDB is an open-source no-code platform that transforms any database into a smart spreadsheet interface. It provides a user-friendly way to manage data, create forms, build APIs, and collaborate on database operations without requiring extensive technical knowledge.

"},{"location":"services/nocodb/#features","title":"Features","text":""},{"location":"services/nocodb/#access","title":"Access","text":""},{"location":"services/nocodb/#configuration","title":"Configuration","text":""},{"location":"services/nocodb/#environment-variables","title":"Environment Variables","text":""},{"location":"services/nocodb/#database-backend","title":"Database Backend","text":"

NocoDB uses a dedicated PostgreSQL instance (root_db) with the following configuration:

"},{"location":"services/nocodb/#volumes","title":"Volumes","text":""},{"location":"services/nocodb/#getting-started","title":"Getting Started","text":"
  1. Access NocoDB: Navigate to http://localhost:8090
  2. Initial Setup: Complete the onboarding process
  3. Create Project: Start with a new project or connect existing databases
  4. Add Tables: Import data or create new tables
  5. Configure Views: Set up different views (Grid, Form, Gallery, etc.)
  6. Set Permissions: Configure user access and sharing settings
"},{"location":"services/nocodb/#common-use-cases","title":"Common Use Cases","text":""},{"location":"services/nocodb/#content-management","title":"Content Management","text":""},{"location":"services/nocodb/#project-management","title":"Project Management","text":""},{"location":"services/nocodb/#data-collection","title":"Data Collection","text":""},{"location":"services/nocodb/#integration-with-other-services","title":"Integration with Other Services","text":"

NocoDB can integrate well with other Changemaker Lite services:

"},{"location":"services/nocodb/#api-usage","title":"API Usage","text":"

NocoDB automatically generates REST APIs for all your tables:

# Get all records from a table\nGET http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}\n\n# Create a new record\nPOST http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}\n\n# Update a record\nPATCH http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}/{id}\n
"},{"location":"services/nocodb/#backup-and-data-management","title":"Backup and Data Management","text":""},{"location":"services/nocodb/#database-backup","title":"Database Backup","text":"

Since NocoDB uses PostgreSQL, you can backup the database:

# Backup NocoDB database\ndocker exec root_db pg_dump -U postgres root_db > nocodb_backup.sql\n\n# Restore from backup\ndocker exec -i root_db psql -U postgres root_db < nocodb_backup.sql\n
"},{"location":"services/nocodb/#application-data","title":"Application Data","text":"

Application settings and metadata are stored in the nc_data volume.

"},{"location":"services/nocodb/#security-considerations","title":"Security Considerations","text":""},{"location":"services/nocodb/#performance-tips","title":"Performance Tips","text":""},{"location":"services/nocodb/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/nocodb/#common-issues","title":"Common Issues","text":"

Service won't start: Check if the PostgreSQL database is healthy

docker logs root_db\n

Database connection errors: Verify database credentials and network connectivity

docker exec nocodb nc_data nc\n

Performance issues: Monitor resource usage and optimize queries

docker stats nocodb root_db\n
"},{"location":"services/nocodb/#official-documentation","title":"Official Documentation","text":"

For comprehensive guides and advanced features:

"},{"location":"services/postgresql/","title":"PostgreSQL Database","text":"

Reliable database backend for applications.

"},{"location":"services/postgresql/#overview","title":"Overview","text":"

PostgreSQL is a powerful, open-source relational database system. In Changemaker Lite, it serves as the backend database for Listmonk and can be used by other applications requiring persistent data storage.

"},{"location":"services/postgresql/#features","title":"Features","text":""},{"location":"services/postgresql/#access","title":"Access","text":""},{"location":"services/postgresql/#configuration","title":"Configuration","text":""},{"location":"services/postgresql/#environment-variables","title":"Environment Variables","text":""},{"location":"services/postgresql/#health-checks","title":"Health Checks","text":"

The PostgreSQL container includes health checks to ensure the database is ready before dependent services start.

"},{"location":"services/postgresql/#data-persistence","title":"Data Persistence","text":"

Database data is stored in a Docker volume (listmonk-data) to ensure persistence across container restarts.

"},{"location":"services/postgresql/#connecting-to-the-database","title":"Connecting to the Database","text":""},{"location":"services/postgresql/#from-host-machine","title":"From Host Machine","text":"

You can connect to PostgreSQL from your host machine using:

psql -h localhost -p 5432 -U [username] -d [database]\n
"},{"location":"services/postgresql/#from-other-containers","title":"From Other Containers","text":"

Other containers can connect using the internal hostname listmonk-db on port 5432.

"},{"location":"services/postgresql/#backup-and-restore","title":"Backup and Restore","text":""},{"location":"services/postgresql/#backup","title":"Backup","text":"
docker exec listmonk-db pg_dump -U [username] [database] > backup.sql\n
"},{"location":"services/postgresql/#restore","title":"Restore","text":"
docker exec -i listmonk-db psql -U [username] [database] < backup.sql\n
"},{"location":"services/postgresql/#monitoring","title":"Monitoring","text":"

Monitor database health and performance through: - Container logs: docker logs listmonk-db - Database metrics and queries - Connection monitoring

"},{"location":"services/postgresql/#security-considerations","title":"Security Considerations","text":""},{"location":"services/postgresql/#official-documentation","title":"Official Documentation","text":"

For comprehensive PostgreSQL documentation: - PostgreSQL Documentation - Docker PostgreSQL Image

"},{"location":"services/static-server/","title":"Static Site Server","text":"

Nginx-powered static site server for hosting built documentation and websites.

"},{"location":"services/static-server/#overview","title":"Overview","text":"

The Static Site Server uses Nginx to serve your built documentation and static websites. It's configured to serve the built MkDocs site and other static content with high performance and reliability.

"},{"location":"services/static-server/#features","title":"Features","text":""},{"location":"services/static-server/#access","title":"Access","text":""},{"location":"services/static-server/#configuration","title":"Configuration","text":""},{"location":"services/static-server/#environment-variables","title":"Environment Variables","text":""},{"location":"services/static-server/#volumes","title":"Volumes","text":""},{"location":"services/static-server/#usage","title":"Usage","text":"
  1. Build your MkDocs site: docker exec mkdocs-changemaker mkdocs build
  2. The built site is automatically available at http://localhost:4001
  3. Any files in ./mkdocs/site/ will be served statically
"},{"location":"services/static-server/#file-structure","title":"File Structure","text":"
mkdocs/site/           # Served at /\n\u251c\u2500\u2500 index.html         # Homepage\n\u251c\u2500\u2500 assets/           # CSS, JS, images\n\u251c\u2500\u2500 services/         # Service documentation\n\u2514\u2500\u2500 search/           # Search functionality\n
"},{"location":"services/static-server/#performance-features","title":"Performance Features","text":""},{"location":"services/static-server/#custom-configuration","title":"Custom Configuration","text":"

For advanced Nginx configuration, you can: 1. Create custom Nginx config files 2. Mount them as volumes 3. Restart the container

"},{"location":"services/static-server/#monitoring","title":"Monitoring","text":"

Monitor the static site server through: - Container logs: docker logs mkdocs-site-server-changemaker - Access logs for traffic analysis - Performance metrics

"},{"location":"services/static-server/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/static-server/#common-issues","title":"Common Issues","text":""},{"location":"services/static-server/#debugging","title":"Debugging","text":"
# Check container logs\ndocker logs mkdocs-site-server-changemaker\n\n# Verify files are present\ndocker exec mkdocs-site-server-changemaker ls -la /config/www\n\n# Test file serving\ncurl -I http://localhost:4001\n
"},{"location":"services/static-server/#official-documentation","title":"Official Documentation","text":"

For more information about the underlying Nginx server: - LinuxServer.io Nginx - Nginx Documentation

"},{"location":"blog/archive/2025/","title":"2025","text":""}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\u200b\\-_,:!=\\[\\]()\"`/]+|\\.(?!\\d)|&[lg]t;|(?!\\b)(?=[A-Z][a-z])","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Welcome to Changemaker Lite","text":"

Stop feeding your secrets to corporations. Own your political infrastructure.

"},{"location":"#quick-start","title":"Quick Start","text":"

Get up and running in minutes:

# Clone the repository\ngit clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n\n# Configure environment\n./config.sh\n\n# Start all services\ndocker compose up -d\n\n# For production deployment with Cloudflare tunnels\n./start-production.sh\n
"},{"location":"#services","title":"Services","text":"

Changemaker Lite includes these essential services:

"},{"location":"#core-services","title":"Core Services","text":""},{"location":"#communication-automation","title":"Communication & Automation","text":""},{"location":"#data-development","title":"Data & Development","text":""},{"location":"#interactive-tools","title":"Interactive Tools","text":""},{"location":"#getting-started","title":"Getting Started","text":"
  1. Setup: Run ./config.sh to configure your environment
  2. Launch: Start services with docker compose up -d
  3. Dashboard: Access the Homepage at http://localhost:3010
  4. Production: Deploy with Cloudflare tunnels using ./start-production.sh
"},{"location":"#project-structure","title":"Project Structure","text":"
changemaker.lite/\n\u251c\u2500\u2500 docker-compose.yml    # Service definitions\n\u251c\u2500\u2500 config.sh            # Configuration wizard\n\u251c\u2500\u2500 start-production.sh  # Production deployment script\n\u251c\u2500\u2500 mkdocs/              # Documentation source\n\u2502   \u251c\u2500\u2500 docs/            # Markdown files\n\u2502   \u2514\u2500\u2500 mkdocs.yml       # MkDocs configuration\n\u251c\u2500\u2500 configs/             # Service configurations\n\u2502   \u251c\u2500\u2500 homepage/        # Homepage dashboard config\n\u2502   \u251c\u2500\u2500 code-server/     # VS Code settings\n\u2502   \u2514\u2500\u2500 cloudflare/      # Tunnel configurations\n\u251c\u2500\u2500 map/                 # Map application\n\u2502   \u251c\u2500\u2500 app/             # Node.js application\n\u2502   \u251c\u2500\u2500 Dockerfile       # Container definition\n\u2502   \u2514\u2500\u2500 .env             # Map configuration\n\u2514\u2500\u2500 assets/              # Shared assets\n    \u251c\u2500\u2500 images/          # Image files\n    \u251c\u2500\u2500 icons/           # Service icons\n    \u2514\u2500\u2500 uploads/         # Listmonk uploads\n
"},{"location":"#key-features","title":"Key Features","text":""},{"location":"#system-requirements","title":"System Requirements","text":""},{"location":"#learn-more","title":"Learn More","text":""},{"location":"test/","title":"Test","text":"

lololol

okay well just doing some fast writing because why the heck not.

\"I would ask for an apology from the city (Municipality of Jasper) as a result,\"

lololol

"},{"location":"adv/","title":"Advanced Configurations","text":"

We are also publishing how BNKops does several advanced workflows. These include things like assembling hardware, how to manage a network, how to manage several changemakers simultaneously, and integrating AI.

"},{"location":"adv/ansible/","title":"Setting Up Ansible with Tailscale for Remote Server Management","text":""},{"location":"adv/ansible/#overview","title":"Overview","text":"

This guide walks you through setting up Ansible to manage remote servers (like ThinkCentre units) using Tailscale for secure networking. This approach provides reliable remote access without complex port forwarding or VPN configurations.

In plainer language; this allows you to manage several Changemaker nodes remotely. If you are a full time campaigner, this can enable you to manage several campaigns infrastructure from a central location while each user gets their own Changemaker box.

"},{"location":"adv/ansible/#what-youll-learn","title":"What You'll Learn","text":""},{"location":"adv/ansible/#prerequisites","title":"Prerequisites","text":""},{"location":"adv/ansible/#part-1-initial-setup-on-master-node","title":"Part 1: Initial Setup on Master Node","text":""},{"location":"adv/ansible/#1-create-ansible-directory-structure","title":"1. Create Ansible Directory Structure","text":"
# Create project directory\nmkdir ~/ansible_quickstart\ncd ~/ansible_quickstart\n\n# Create directory structure\nmkdir -p group_vars host_vars roles playbooks\n
"},{"location":"adv/ansible/#2-install-ansible","title":"2. Install Ansible","text":"
sudo apt update\nsudo apt install ansible\n
"},{"location":"adv/ansible/#3-generate-ssh-keys-if-not-already-done","title":"3. Generate SSH Keys (if not already done)","text":"
# Generate SSH key pair\nssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa\n\n# Display public key (save this for later)\ncat ~/.ssh/id_rsa.pub\n
"},{"location":"adv/ansible/#part-2-target-node-setup-physical-access-required-initially","title":"Part 2: Target Node Setup (Physical Access Required Initially)","text":""},{"location":"adv/ansible/#1-enable-ssh-on-target-node","title":"1. Enable SSH on Target Node","text":"

Access each target node physically (monitor + keyboard):

# Update system\nsudo apt update && sudo apt upgrade -y\n\n# Install and enable SSH\nsudo apt install openssh-server\nsudo systemctl enable ssh\nsudo systemctl start ssh\n\n# Check SSH status\nsudo systemctl status ssh\n

Note: If you get \"Unit ssh.service could not be found\", you need to install the SSH server first:

# Install OpenSSH server\nsudo apt install openssh-server\n\n# Then start and enable SSH\nsudo systemctl start ssh\nsudo systemctl enable ssh\n\n# Verify SSH is running and listening\nsudo ss -tlnp | grep :22\n

You should see SSH listening on port 22.

"},{"location":"adv/ansible/#2-configure-ssh-key-authentication","title":"2. Configure SSH Key Authentication","text":"
# Create .ssh directory\nmkdir -p ~/.ssh\nchmod 700 ~/.ssh\n\n# Create authorized_keys file\nnano ~/.ssh/authorized_keys\n

Paste your public key from the master node, then:

# Set proper permissions\nchmod 600 ~/.ssh/authorized_keys\n
"},{"location":"adv/ansible/#3-configure-ssh-security","title":"3. Configure SSH Security","text":"
# Edit SSH config\nsudo nano /etc/ssh/sshd_config\n

Ensure these lines are uncommented:

PubkeyAuthentication yes\nAuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2\n
# Restart SSH service\nsudo systemctl restart ssh\n
"},{"location":"adv/ansible/#4-configure-firewall","title":"4. Configure Firewall","text":"
# Check firewall status\nsudo ufw status\n\n# Allow SSH through firewall\nsudo ufw allow ssh\n\n# Fix home directory permissions (required for SSH keys)\nchmod 755 ~/\n
"},{"location":"adv/ansible/#part-3-test-local-ssh-connection","title":"Part 3: Test Local SSH Connection","text":"

Before proceeding with remote access, test SSH connectivity locally:

# From master node, test SSH to target\nssh username@<target-local-ip>\n

Common Issues and Solutions:

"},{"location":"adv/ansible/#part-4-set-up-tailscale-for-remote-access","title":"Part 4: Set Up Tailscale for Remote Access","text":""},{"location":"adv/ansible/#why-tailscale-over-alternatives","title":"Why Tailscale Over Alternatives","text":"

We initially tried Cloudflare Tunnels but encountered complexity with:

Tailscale is superior because:

"},{"location":"adv/ansible/#1-install-tailscale-on-master-node","title":"1. Install Tailscale on Master Node","text":"
# Install Tailscale\ncurl -fsSL https://tailscale.com/install.sh | sh\n\n# Connect to Tailscale network\nsudo tailscale up\n

Follow the authentication URL to connect with your Google/Microsoft/GitHub account.

"},{"location":"adv/ansible/#2-install-tailscale-on-target-nodes","title":"2. Install Tailscale on Target Nodes","text":"

On each target node:

# Install Tailscale\ncurl -fsSL https://tailscale.com/install.sh | sh\n\n# Connect to Tailscale network\nsudo tailscale up\n

Authenticate each device through the provided URL.

"},{"location":"adv/ansible/#3-get-tailscale-ip-addresses","title":"3. Get Tailscale IP Addresses","text":"

On each machine:

# Get your Tailscale IP\ntailscale ip -4\n

Each device receives a persistent IP like 100.x.x.x.

"},{"location":"adv/ansible/#part-5-configure-ansible","title":"Part 5: Configure Ansible","text":""},{"location":"adv/ansible/#1-create-inventory-file","title":"1. Create Inventory File","text":"
# Create inventory.ini\ncd ~/ansible_quickstart\nnano inventory.ini\n

Content:

[thinkcenter]\ntc-node1 ansible_host=100.x.x.x ansible_user=your-username\ntc-node2 ansible_host=100.x.x.x ansible_user=your-username\n\n[all:vars]\nansible_ssh_private_key_file=~/.ssh/id_rsa\nansible_host_key_checking=False\n

Replace:

"},{"location":"adv/ansible/#2-test-ansible-connectivity","title":"2. Test Ansible Connectivity","text":"
# Test connection to all nodes\nansible all -i inventory.ini -m ping\n

Expected output:

tc-node1 | SUCCESS => {\n    \"changed\": false,\n    \"ping\": \"pong\"\n}\n
"},{"location":"adv/ansible/#part-6-create-and-run-playbooks","title":"Part 6: Create and Run Playbooks","text":""},{"location":"adv/ansible/#1-simple-information-gathering-playbook","title":"1. Simple Information Gathering Playbook","text":"
mkdir -p playbooks\nnano playbooks/info-playbook.yml\n

Content:

---\n- name: Gather Node Information\n  hosts: all\n  tasks:\n    - name: Get system information\n      setup:\n\n    - name: Display basic system info\n      debug:\n        msg: |\n          Hostname: {{ ansible_hostname }}\n          Operating System: {{ ansible_distribution }} {{ ansible_distribution_version }}\n          Architecture: {{ ansible_architecture }}\n          Memory: {{ ansible_memtotal_mb }}MB\n          CPU Cores: {{ ansible_processor_vcpus }}\n\n    - name: Show disk usage\n      command: df -h /\n      register: disk_info\n\n    - name: Display disk usage\n      debug:\n        msg: \"Root filesystem usage: {{ disk_info.stdout_lines[1] }}\"\n\n    - name: Check uptime\n      command: uptime\n      register: uptime_info\n\n    - name: Display uptime\n      debug:\n        msg: \"System uptime: {{ uptime_info.stdout }}\"\n
"},{"location":"adv/ansible/#2-run-the-playbook","title":"2. Run the Playbook","text":"
ansible-playbook -i inventory.ini playbooks/info-playbook.yml\n
"},{"location":"adv/ansible/#part-7-advanced-playbook-example","title":"Part 7: Advanced Playbook Example","text":""},{"location":"adv/ansible/#system-setup-playbook","title":"System Setup Playbook","text":"
nano playbooks/setup-node.yml\n

Content:

---\n- name: Setup ThinkCentre Node\n  hosts: all\n  become: yes\n  tasks:\n    - name: Update package cache\n      apt:\n        update_cache: yes\n\n    - name: Install essential packages\n      package:\n        name:\n          - htop\n          - vim\n          - curl\n          - git\n          - docker.io\n        state: present\n\n    - name: Add user to docker group\n      user:\n        name: \"{{ ansible_user }}\"\n        groups: docker\n        append: yes\n\n    - name: Create management directory\n      file:\n        path: /opt/management\n        state: directory\n        owner: \"{{ ansible_user }}\"\n        group: \"{{ ansible_user }}\"\n
"},{"location":"adv/ansible/#troubleshooting-guide","title":"Troubleshooting Guide","text":""},{"location":"adv/ansible/#ssh-issues","title":"SSH Issues","text":"

Problem: SSH connection hangs

Problem: Permission denied (publickey)

Problem: Bad owner or permissions on SSH config

chmod 600 ~/.ssh/config\n
"},{"location":"adv/ansible/#ansible-issues","title":"Ansible Issues","text":"

Problem: Host key verification failed

Problem: Ansible command not found

sudo apt install ansible\n

Problem: Connection timeouts

"},{"location":"adv/ansible/#tailscale-issues","title":"Tailscale Issues","text":"

Problem: Can't connect to Tailscale IP

"},{"location":"adv/ansible/#scaling-to-multiple-nodes","title":"Scaling to Multiple Nodes","text":""},{"location":"adv/ansible/#adding-new-nodes","title":"Adding New Nodes","text":"
  1. Install Tailscale on new node
  2. Set up SSH access (repeat Part 2)
  3. Add to inventory.ini:
[thinkcenter]\ntc-node1 ansible_host=100.125.148.60 ansible_user=bunker-admin\ntc-node2 ansible_host=100.x.x.x ansible_user=bunker-admin\ntc-node3 ansible_host=100.x.x.x ansible_user=bunker-admin\n
"},{"location":"adv/ansible/#group-management","title":"Group Management","text":"
[webservers]\ntc-node1 ansible_host=100.x.x.x ansible_user=bunker-admin\ntc-node2 ansible_host=100.x.x.x ansible_user=bunker-admin\n\n[databases]\ntc-node3 ansible_host=100.x.x.x ansible_user=bunker-admin\n\n[all:vars]\nansible_ssh_private_key_file=~/.ssh/id_rsa\nansible_host_key_checking=False\n

Run playbooks on specific groups:

ansible-playbook -i inventory.ini -l webservers playbook.yml\n
"},{"location":"adv/ansible/#best-practices","title":"Best Practices","text":""},{"location":"adv/ansible/#security","title":"Security","text":""},{"location":"adv/ansible/#organization","title":"Organization","text":"
ansible_quickstart/\n\u251c\u2500\u2500 inventory.ini\n\u251c\u2500\u2500 group_vars/\n\u251c\u2500\u2500 host_vars/\n\u251c\u2500\u2500 roles/\n\u2514\u2500\u2500 playbooks/\n    \u251c\u2500\u2500 info-playbook.yml\n    \u251c\u2500\u2500 setup-node.yml\n    \u2514\u2500\u2500 maintenance.yml\n
"},{"location":"adv/ansible/#monitoring-and-maintenance","title":"Monitoring and Maintenance","text":"

Create regular maintenance playbooks:

- name: System maintenance\n  hosts: all\n  become: yes\n  tasks:\n    - name: Update all packages\n      apt:\n        upgrade: dist\n        update_cache: yes\n\n    - name: Clean package cache\n      apt:\n        autoclean: yes\n        autoremove: yes\n
"},{"location":"adv/ansible/#alternative-approaches-we-considered","title":"Alternative Approaches We Considered","text":""},{"location":"adv/ansible/#cloudflare-tunnels","title":"Cloudflare Tunnels","text":""},{"location":"adv/ansible/#traditional-vpn","title":"Traditional VPN","text":""},{"location":"adv/ansible/#ssh-reverse-tunnels","title":"SSH Reverse Tunnels","text":""},{"location":"adv/ansible/#conclusion","title":"Conclusion","text":"

This setup provides:

The combination of Ansible + Tailscale is ideal for managing distributed infrastructure without the complexity of traditional VPN setups or the limitations of cloud-specific solutions.

"},{"location":"adv/ansible/#quick-reference-commands","title":"Quick Reference Commands","text":"
# Check Tailscale status\ntailscale status\n\n# Test Ansible connectivity\nansible all -i inventory.ini -m ping\n\n# Run playbook on all hosts\nansible-playbook -i inventory.ini playbook.yml\n\n# Run playbook on specific group\nansible-playbook -i inventory.ini -l groupname playbook.yml\n\n# Run single command on all hosts\nansible all -i inventory.ini -m command -a \"uptime\"\n\n# SSH to node via Tailscale\nssh username@100.x.x.x\n
"},{"location":"adv/vscode-ssh/","title":"Remote Development with VSCode over Tailscale","text":""},{"location":"adv/vscode-ssh/#overview","title":"Overview","text":"

This guide describes how to set up Visual Studio Code for remote development on servers using the Tailscale network. This enables development directly on remote machines as if they were local, with full access to files, terminals, and debugging capabilities.

"},{"location":"adv/vscode-ssh/#what-youll-learn","title":"What You'll Learn","text":""},{"location":"adv/vscode-ssh/#prerequisites","title":"Prerequisites","text":""},{"location":"adv/vscode-ssh/#verify-prerequisites","title":"Verify Prerequisites","text":"

Before starting, verify the setup:

# Check Tailscale connectivity\ntailscale status\n\n# Test SSH access\nssh <username>@<tailscale-ip>\n\n# Check VSCode is installed\ncode --version\n
"},{"location":"adv/vscode-ssh/#part-1-install-and-configure-remote-ssh-extension","title":"Part 1: Install and Configure Remote-SSH Extension","text":""},{"location":"adv/vscode-ssh/#1-install-the-remote-development-extensions","title":"1. Install the Remote Development Extensions","text":"

Option A: Install Remote Development Pack (Recommended)

  1. Open VSCode
  2. Press Ctrl+Shift+X (or Cmd+Shift+X on Mac)
  3. Search for \"Remote Development\"
  4. Install the Remote Development extension pack by Microsoft

This pack includes:

Option B: Install Individual Extension

  1. Search for \"Remote - SSH\"
  2. Install Remote - SSH by Microsoft
"},{"location":"adv/vscode-ssh/#2-verify-installation","title":"2. Verify Installation","text":"

After installation, the following should be visible:

"},{"location":"adv/vscode-ssh/#part-2-configure-ssh-connections","title":"Part 2: Configure SSH Connections","text":""},{"location":"adv/vscode-ssh/#1-access-ssh-configuration","title":"1. Access SSH Configuration","text":"

Method A: Through VSCode

  1. Press Ctrl+Shift+P to open Command Palette
  2. Type \"Remote-SSH: Open SSH Configuration File...\"
  3. Select the SSH config file (usually the first option)

Method B: Direct File Editing

# Edit SSH config file directly\nnano ~/.ssh/config\n

"},{"location":"adv/vscode-ssh/#2-add-server-configurations","title":"2. Add Server Configurations","text":"

Add servers to the SSH config file:

# Example Node\nHost node1\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n    ForwardAgent yes\n    ServerAliveInterval 60\n    ServerAliveCountMax 3\n\n# Additional nodes (add as needed)\nHost node2\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n    ForwardAgent yes\n    ServerAliveInterval 60\n    ServerAliveCountMax 3\n

Configuration Options Explained:

"},{"location":"adv/vscode-ssh/#3-set-proper-ssh-key-permissions","title":"3. Set Proper SSH Key Permissions","text":"
# Ensure SSH config has correct permissions\nchmod 600 ~/.ssh/config\n\n# Verify SSH key permissions\nchmod 600 ~/.ssh/id_rsa\nchmod 644 ~/.ssh/id_rsa.pub\n
"},{"location":"adv/vscode-ssh/#part-3-connect-to-remote-servers","title":"Part 3: Connect to Remote Servers","text":""},{"location":"adv/vscode-ssh/#1-connect-via-command-palette","title":"1. Connect via Command Palette","text":"
  1. Press Ctrl+Shift+P
  2. Type \"Remote-SSH: Connect to Host...\"
  3. Select the server (e.g., node1)
  4. VSCode will open a new window connected to the remote server
"},{"location":"adv/vscode-ssh/#2-connect-via-remote-explorer","title":"2. Connect via Remote Explorer","text":"
  1. Click the Remote Explorer icon in Activity Bar
  2. Expand SSH Targets
  3. Click the connect icon next to the server name
"},{"location":"adv/vscode-ssh/#3-connect-via-quick-menu","title":"3. Connect via Quick Menu","text":"
  1. Click the remote indicator in bottom-left corner (looks like ><)
  2. Select \"Connect to Host...\"
  3. Choose the server from the list
"},{"location":"adv/vscode-ssh/#4-first-connection-process","title":"4. First Connection Process","text":"

On first connection, VSCode will:

  1. Verify the host key (click \"Continue\" if prompted)
  2. Install VSCode Server on the remote machine (automatic)
  3. Open a remote window with access to the remote file system

Expected Timeline: - First connection: 1-3 minutes (installs VSCode Server) - Subsequent connections: 10-30 seconds

"},{"location":"adv/vscode-ssh/#part-4-remote-development-environment-setup","title":"Part 4: Remote Development Environment Setup","text":""},{"location":"adv/vscode-ssh/#1-open-remote-workspace","title":"1. Open Remote Workspace","text":"

Once connected:

# In the VSCode terminal (now running on remote server)\n# Navigate to the project directory\ncd /home/<username>/projects\n\n# Open current directory in VSCode\ncode .\n\n# Or open a specific project\ncode /opt/myproject\n
"},{"location":"adv/vscode-ssh/#2-install-extensions-on-remote-server","title":"2. Install Extensions on Remote Server","text":"

Extensions must be installed separately on the remote server:

Essential Development Extensions:

  1. Python (Microsoft) - Python development
  2. GitLens (GitKraken) - Enhanced Git capabilities
  3. Docker (Microsoft) - Container development
  4. Prettier - Code formatting
  5. ESLint - JavaScript linting
  6. Auto Rename Tag - HTML/XML tag editing

To Install:

  1. Go to Extensions (Ctrl+Shift+X)
  2. Find the desired extension
  3. Click \"Install in SSH: node1\" (not local install)
"},{"location":"adv/vscode-ssh/#3-configure-git-on-remote-server","title":"3. Configure Git on Remote Server","text":"
# In VSCode terminal (remote)\ngit config --global user.name \"<Full Name>\"\ngit config --global user.email \"<email@example.com>\"\n\n# Test Git connectivity\ngit clone https://github.com/<username>/<repo>.git\n
"},{"location":"adv/vscode-ssh/#part-5-remote-development-workflows","title":"Part 5: Remote Development Workflows","text":""},{"location":"adv/vscode-ssh/#1-file-management","title":"1. File Management","text":"

File Explorer:

File Transfer:

# Upload files to remote (from local terminal)\nscp localfile.txt <username>@<tailscale-ip>:/home/<username>/\n\n# Download files from remote\nscp <username>@<tailscale-ip>:/remote/path/file.txt ./local/path/\n

"},{"location":"adv/vscode-ssh/#2-terminal-usage","title":"2. Terminal Usage","text":"

Integrated Terminal:

Common Remote Terminal Commands:

# Check system resources\nhtop\ndf -h\nfree -h\n\n# Install packages\nsudo apt update\nsudo apt install nodejs npm\n\n# Start services\nsudo systemctl start nginx\nsudo docker-compose up -d\n

"},{"location":"adv/vscode-ssh/#3-port-forwarding","title":"3. Port Forwarding","text":"

Automatic Port Forwarding: VSCode automatically detects and forwards common development ports.

Manual Port Forwarding:

  1. Open Ports tab in terminal panel
  2. Click \"Forward a Port\"
  3. Enter port number (e.g., 3000, 8080, 5000)
  4. Access via http://localhost:port on the local machine

Example: Web Development

# Start a web server on remote (port 3000)\nnpm start\n\n# VSCode automatically suggests forwarding port 3000\n# Access at http://localhost:3000 on the local machine\n

"},{"location":"adv/vscode-ssh/#4-debugging-remote-applications","title":"4. Debugging Remote Applications","text":"

Python Debugging:

// .vscode/launch.json on remote server\n{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Python: Current File\",\n            \"type\": \"python\",\n            \"request\": \"launch\",\n            \"program\": \"${file}\",\n            \"console\": \"integratedTerminal\"\n        }\n    ]\n}\n

Node.js Debugging:

// .vscode/launch.json\n{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"name\": \"Launch Program\",\n            \"type\": \"node\",\n            \"request\": \"launch\",\n            \"program\": \"${workspaceFolder}/app.js\"\n        }\n    ]\n}\n

"},{"location":"adv/vscode-ssh/#part-6-advanced-configuration","title":"Part 6: Advanced Configuration","text":""},{"location":"adv/vscode-ssh/#1-workspace-settings","title":"1. Workspace Settings","text":"

Create remote-specific settings:

// .vscode/settings.json (on remote server)\n{\n    \"python.defaultInterpreterPath\": \"/usr/bin/python3\",\n    \"terminal.integrated.shell.linux\": \"/bin/bash\",\n    \"files.autoSave\": \"afterDelay\",\n    \"editor.formatOnSave\": true,\n    \"remote.SSH.remotePlatform\": {\n        \"node1\": \"linux\"\n    }\n}\n
"},{"location":"adv/vscode-ssh/#2-multi-server-management","title":"2. Multi-Server Management","text":"

Switch Between Servers:

  1. Click remote indicator (bottom-left)
  2. Select \"Connect to Host...\"
  3. Choose a different server

Compare Files Across Servers:

  1. Open file from server A
  2. Connect to server B in new window
  3. Open corresponding file
  4. Use \"Compare with...\" command
"},{"location":"adv/vscode-ssh/#3-sync-configuration","title":"3. Sync Configuration","text":"

Settings Sync:

  1. Enable Settings Sync in VSCode
  2. Settings, extensions, and keybindings sync to remote
  3. Consistent experience across all servers
"},{"location":"adv/vscode-ssh/#part-7-project-specific-setups","title":"Part 7: Project-Specific Setups","text":""},{"location":"adv/vscode-ssh/#1-python-development","title":"1. Python Development","text":"
# On remote server\n# Create virtual environment\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install packages\npip install flask django requests\n\n# VSCode automatically detects Python interpreter\n

VSCode Python Configuration:

// .vscode/settings.json\n{\n    \"python.defaultInterpreterPath\": \"./venv/bin/python\",\n    \"python.linting.enabled\": true,\n    \"python.linting.pylintEnabled\": true\n}\n

"},{"location":"adv/vscode-ssh/#2-nodejs-development","title":"2. Node.js Development","text":"
# On remote server\n# Install Node.js\ncurl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -\nsudo apt-get install -y nodejs\n\n# Create project\nmkdir myapp && cd myapp\nnpm init -y\nnpm install express\n
"},{"location":"adv/vscode-ssh/#3-docker-development","title":"3. Docker Development","text":"
# On remote server\n# Install Docker (if not already done via Ansible)\nsudo apt install docker.io docker-compose\nsudo usermod -aG docker $USER\n\n# Create Dockerfile\ncat > Dockerfile << EOF\nFROM node:18\nWORKDIR /app\nCOPY package*.json ./\nRUN npm install\nCOPY . .\nEXPOSE 3000\nCMD [\"npm\", \"start\"]\nEOF\n

VSCode Docker Integration:

"},{"location":"adv/vscode-ssh/#part-8-troubleshooting-guide","title":"Part 8: Troubleshooting Guide","text":""},{"location":"adv/vscode-ssh/#common-connection-issues","title":"Common Connection Issues","text":"

Problem: \"Could not establish connection to remote host\"

Solutions:

# Check Tailscale connectivity\ntailscale status\nping <tailscale-ip>\n\n# Test SSH manually\nssh <username>@<tailscale-ip>\n\n# Check SSH config syntax\nssh -T node1\n

Problem: \"Permission denied (publickey)\"

Solutions:

# Check SSH key permissions\nchmod 600 ~/.ssh/id_rsa\nchmod 600 ~/.ssh/config\n\n# Verify SSH agent\nssh-add ~/.ssh/id_rsa\nssh-add -l\n\n# Test SSH connection verbosely\nssh -v <username>@<tailscale-ip>\n

Problem: \"Host key verification failed\"

Solutions:

# Remove old host key\nssh-keygen -R <tailscale-ip>\n\n# Or disable host key checking (less secure)\n# Add to SSH config:\n# StrictHostKeyChecking no\n

"},{"location":"adv/vscode-ssh/#vscode-specific-issues","title":"VSCode-Specific Issues","text":"

Problem: Extensions not working on remote

Solutions:

  1. Install extensions specifically for the remote server
  2. Check extension compatibility with remote development
  3. Reload VSCode window: Ctrl+Shift+P \u2192 \"Developer: Reload Window\"

Problem: Slow performance

Solutions: - Use .vscode/settings.json to exclude large directories:

{\n    \"files.watcherExclude\": {\n        \"**/node_modules/**\": true,\n        \"**/.git/objects/**\": true,\n        \"**/dist/**\": true\n    }\n}\n

Problem: Terminal not starting

Solutions:

# Check shell path in remote settings\n\"terminal.integrated.shell.linux\": \"/bin/bash\"\n\n# Or let VSCode auto-detect\n\"terminal.integrated.defaultProfile.linux\": \"bash\"\n

"},{"location":"adv/vscode-ssh/#network-and-performance-issues","title":"Network and Performance Issues","text":"

Problem: Connection timeouts

Solutions: Add to SSH config:

ServerAliveInterval 60\nServerAliveCountMax 3\nTCPKeepAlive yes\n

Problem: File transfer slow

Solutions: - Use .vscodeignore to exclude unnecessary files - Compress large files before transfer - Use rsync for large file operations:

rsync -avz --progress localdir/ <username>@<tailscale-ip>:remotedir/\n

"},{"location":"adv/vscode-ssh/#part-9-best-practices","title":"Part 9: Best Practices","text":""},{"location":"adv/vscode-ssh/#security-best-practices","title":"Security Best Practices","text":"
  1. Use SSH keys, never passwords
  2. Keep SSH agent secure
  3. Regular security updates on remote servers
  4. Use VSCode's secure connection verification
"},{"location":"adv/vscode-ssh/#performance-optimization","title":"Performance Optimization","text":"
  1. Exclude unnecessary files:

    // .vscode/settings.json\n{\n    \"files.watcherExclude\": {\n        \"**/node_modules/**\": true,\n        \"**/.git/**\": true,\n        \"**/dist/**\": true,\n        \"**/build/**\": true\n    },\n    \"search.exclude\": {\n        \"**/node_modules\": true,\n        \"**/bower_components\": true,\n        \"**/*.code-search\": true\n    }\n}\n

  2. Use remote workspace for large projects

  3. Close unnecessary windows and extensions
  4. Use efficient development workflows
"},{"location":"adv/vscode-ssh/#development-workflow","title":"Development Workflow","text":"
  1. Use version control effectively:

    # Always work in Git repositories\ngit status\ngit add .\ngit commit -m \"feature: add new functionality\"\ngit push origin main\n

  2. Environment separation:

    # Development\nssh node1\ncd /home/<username>/dev-projects\n\n# Production\nssh node2\ncd /opt/production-apps\n

  3. Backup important work:

    # Regular backups via Git\ngit push origin main\n\n# Or manual backup\nscp -r <username>@<tailscale-ip>:/important/project ./backup/\n

"},{"location":"adv/vscode-ssh/#part-10-team-collaboration","title":"Part 10: Team Collaboration","text":""},{"location":"adv/vscode-ssh/#shared-development-servers","title":"Shared Development Servers","text":"

SSH Config for Team:

# Shared development server\nHost team-dev\n    HostName <tailscale-ip>\n    User <team-user>\n    IdentityFile ~/.ssh/team_dev_key\n    ForwardAgent yes\n\n# Personal development\nHost my-dev\n    HostName <tailscale-ip>\n    User <username>\n    IdentityFile ~/.ssh/id_rsa\n

"},{"location":"adv/vscode-ssh/#project-structure","title":"Project Structure","text":"
/opt/projects/\n\u251c\u2500\u2500 project-a/\n\u2502   \u251c\u2500\u2500 dev/          # Development branch\n\u2502   \u251c\u2500\u2500 staging/      # Staging environment\n\u2502   \u2514\u2500\u2500 docs/         # Documentation\n\u251c\u2500\u2500 project-b/\n\u2514\u2500\u2500 shared-tools/     # Common utilities\n
"},{"location":"adv/vscode-ssh/#access-management","title":"Access Management","text":"
# Create shared project directory\nsudo mkdir -p /opt/projects\nsudo chown -R :developers /opt/projects\nsudo chmod -R g+w /opt/projects\n\n# Add users to developers group\nsudo usermod -a -G developers <username>\n
"},{"location":"adv/vscode-ssh/#quick-reference","title":"Quick Reference","text":""},{"location":"adv/vscode-ssh/#essential-vscode-remote-commands","title":"Essential VSCode Remote Commands","text":"
# Command Palette shortcuts\nCtrl+Shift+P \u2192 \"Remote-SSH: Connect to Host...\"\nCtrl+Shift+P \u2192 \"Remote-SSH: Open SSH Configuration File...\"\nCtrl+Shift+P \u2192 \"Remote-SSH: Kill VS Code Server on Host...\"\n\n# Terminal\nCtrl+` \u2192 Open integrated terminal\nCtrl+Shift+` \u2192 Create new terminal\n\n# File operations\nCtrl+O \u2192 Open file\nCtrl+S \u2192 Save file\nCtrl+Shift+E \u2192 Focus file explorer\n
"},{"location":"adv/vscode-ssh/#ssh-connection-quick-test","title":"SSH Connection Quick Test","text":"
# Test connectivity\nssh -T node1\n\n# Connect with verbose output\nssh -v <username>@<tailscale-ip>\n\n# Check SSH config\nssh -F ~/.ssh/config node1\n
"},{"location":"adv/vscode-ssh/#port-forwarding-commands","title":"Port Forwarding Commands","text":"
# Manual port forwarding\nssh -L 3000:localhost:3000 <username>@<tailscale-ip>\n\n# Background tunnel\nssh -f -N -L 8080:localhost:80 <username>@<tailscale-ip>\n
"},{"location":"adv/vscode-ssh/#conclusion","title":"Conclusion","text":"

This remote development setup provides:

The combination of VSCode Remote Development with Tailscale networking creates a powerful, flexible development environment that works from anywhere while maintaining security and performance.

Whether developing Python applications, Node.js services, or managing Docker containers, this setup provides a professional remote development experience that rivals local development while leveraging the power and resources of remote servers.

"},{"location":"blog/2025/07/03/blog-1/","title":"Blog 1","text":"

Hello! Just putting something up here because, well, gosh darn, feels like the right thing to do.

Making swift progress. Can now write things fast as heck lad.

"},{"location":"blog/2025/07/10/2/","title":"2","text":"

Wow. Big build day. Added (admittedly still buggy) shifts support to the system. Power did it in a day.

Other updates recently include:

Need to make more content about how to use the system in general too.

"},{"location":"build/","title":"Getting Started","text":"

Welcome to Changemaker-Lite! You're about to reclaim your digital sovereignty and stop feeding your secrets to corporations. This guide will help you set up your own political infrastructure that you actually own and control.

This documentation is broken into a few sections:

"},{"location":"build/#quick-start","title":"Quick Start","text":""},{"location":"build/#build-changemaker-lite","title":"Build Changemaker-Lite","text":"
# Clone the repository\ngit clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n

Cloudflare Credentials

The config.sh script will ask you for your Cloudflare credentials to get started. You can find more information on how to find this in the Cloudlflare Configuration

# Configure environment (creates .env file)\n./config.sh\n
# Start all services\ndocker compose up -d\n
"},{"location":"build/#optional-site-builld","title":"Optional - Site Builld","text":"

If you want to have your site prepared for launch, you can now proceed with reseting the site build. See Build Site for more detials.

"},{"location":"build/#deploy","title":"Deploy","text":"

Cloudflare

Right now, we suggest deploying using Cloudflare for simplicity and protections against 99% of surface level attacks to digital infrastructure. If you want to avoid using this service, we recommend checking out Pagolin as a drop in replacement.

For secure public access, use the production deployment script:

./start-production.sh\n
"},{"location":"build/#why-changemaker-lite","title":"Why Changemaker Lite?","text":"

Before we dive into the technical setup, let's be clear about what you're doing here:

The Reality

If you do politics, who is reading your secrets? Every corporate platform you use is extracting your power, selling your data, and building profiles on your community. It's time to break free.

"},{"location":"build/#what-youre-getting","title":"What You're Getting","text":""},{"location":"build/#what-youre-leaving-behind","title":"What You're Leaving Behind","text":""},{"location":"build/#system-requirements","title":"System Requirements","text":""},{"location":"build/#operating-system","title":"Operating System","text":"

Getting Started on Ubunut

Want some help getting started with a baseline buildout for a Ubunut server? You can use our BNKops Server Build Script

New to Linux?

Consider Linux Mint - it looks like Windows but opens the door to true digital freedom.

"},{"location":"build/#hardware-requirements","title":"Hardware Requirements","text":"

Cloud Hosting

You can run this on a VPS from providers like Hetzner, DigitalOcean, or Linode for ~$20/month.

"},{"location":"build/#software-prerequisites","title":"Software Prerequisites","text":"

Getting Started on Docker

Want some help getting started with a baseline buildout for a Ubunutu server? You can use our BNKops Server Build Script to roll out a configured server in about 20 mins!

  1. Docker Engine (24.0+)
# Install Docker\ncurl -fsSL https://get.docker.com | sudo sh\n\n# Add your user to docker group\nsudo usermod -aG docker $USER\n\n# Log out and back in for group changes to take effect\n
  1. Docker Compose (v2.20+)
# Verify Docker Compose v2 is installed\ndocker compose version\n
  1. Essential Tools
# Install required packages\nsudo apt update\nsudo apt install -y git curl jq openssl\n
"},{"location":"build/#installation","title":"Installation","text":""},{"location":"build/#1-clone-repository","title":"1. Clone Repository","text":"
git clone https://gitea.bnkops.com/admin/changemaker.lite\ncd changemaker.lite\n
"},{"location":"build/#2-run-configuration-wizard","title":"2. Run Configuration Wizard","text":"

The config.sh script will guide you through the initial setup:

./config.sh\n

This wizard will:

"},{"location":"build/#configuration-options","title":"Configuration Options","text":"

During setup, you'll be prompted for:

  1. Domain Name: Your primary domain (e.g., example.com)
  2. Cloudflare Settings (optional):
  3. API Token
  4. Zone ID
  5. Account ID
  6. Admin Credentials:
  7. Listmonk admin email and password
  8. n8n admin email and password
"},{"location":"build/#3-start-services","title":"3. Start Services","text":"

Launch all services with Docker Compose:

docker compose up -d\n

Wait for services to initialize (first run may take 5-10 minutes):

# Watch container status\ndocker compose ps\n\n# View logs\ndocker compose logs -f\n
"},{"location":"build/#4-verify-installation","title":"4. Verify Installation","text":"

Check that all services are running:

docker compose ps\n

Expected output should show all services as \"Up\":

"},{"location":"build/#local-access","title":"Local Access","text":"

Once services are running, access them locally:

"},{"location":"build/#homepage-dashboard","title":"\ud83c\udfe0 Homepage Dashboard","text":""},{"location":"build/#development-tools","title":"\ud83d\udcbb Development Tools","text":""},{"location":"build/#communication","title":"\ud83d\udce7 Communication","text":""},{"location":"build/#automation-data","title":"\ud83d\udd04 Automation & Data","text":""},{"location":"build/#interactive-tools","title":"\ud83d\udee0\ufe0f Interactive Tools","text":""},{"location":"build/#map","title":"Map","text":"

Map

Map is the canvassing application that is custom view of nocodb data. Map is best built after production deployment to reduce duplicate build efforts.

"},{"location":"build/#map-manual","title":"Map Manual","text":""},{"location":"build/#production-deployment","title":"Production Deployment","text":""},{"location":"build/#deploy-with-cloudflare-tunnels","title":"Deploy with Cloudflare Tunnels","text":"

For secure public access, use the production deployment script:

./start-production.sh\n

This script will:

  1. Install and configure cloudflared
  2. Create a Cloudflare tunnel
  3. Set up DNS records automatically
  4. Configure access policies
  5. Create a systemd service for persistence
"},{"location":"build/#what-happens-during-production-setup","title":"What Happens During Production Setup","text":"
  1. Cloudflare Authentication: Browser-based login to Cloudflare
  2. Tunnel Creation: Secure tunnel named changemaker-lite
  3. DNS Configuration: Automatic CNAME records for all services
  4. Access Policies: Email-based authentication for sensitive services
  5. Service Installation: Systemd service for automatic startup
"},{"location":"build/#production-urls","title":"Production URLs","text":"

After successful deployment, services will be available at:

Public Services:

Protected Services (require authentication):

"},{"location":"build/#configuration-management","title":"Configuration Management","text":""},{"location":"build/#environment-variables","title":"Environment Variables","text":"

Key settings in .env file:

# Domain Configuration\nDOMAIN=yourdomain.com\nBASE_DOMAIN=https://yourdomain.com\n\n# Service Ports (automatically assigned to avoid conflicts)\nHOMEPAGE_PORT=3010\nCODE_SERVER_PORT=8888\nLISTMONK_PORT=9000\nMKDOCS_PORT=4000\nMKDOCS_SITE_SERVER_PORT=4001\nN8N_PORT=5678\nNOCODB_PORT=8090\nGITEA_WEB_PORT=3030\nGITEA_SSH_PORT=2222\nMAP_PORT=3000\nMINI_QR_PORT=8089\n\n# Cloudflare (for production)\nCF_API_TOKEN=your_token\nCF_ZONE_ID=your_zone_id\nCF_ACCOUNT_ID=your_account_id\n
"},{"location":"build/#reconfigure-services","title":"Reconfigure Services","text":"

To update configuration:

# Re-run configuration wizard\n./config.sh\n\n# Restart services\ndocker compose down && docker compose up -d\n
"},{"location":"build/#common-tasks","title":"Common Tasks","text":""},{"location":"build/#service-management","title":"Service Management","text":"
# View all services\ndocker compose ps\n\n# View logs for specific service\ndocker compose logs -f [service-name]\n\n# Restart a service\ndocker compose restart [service-name]\n\n# Stop all services\ndocker compose down\n\n# Stop and remove all data (CAUTION!)\ndocker compose down -v\n
"},{"location":"build/#backup-data","title":"Backup Data","text":"
# Backup all volumes\ndocker run --rm -v changemaker_listmonk-data:/data -v $(pwd):/backup alpine tar czf /backup/listmonk-backup.tar.gz -C /data .\n\n# Backup configuration\ntar czf configs-backup.tar.gz configs/\n\n# Backup documentation\ntar czf docs-backup.tar.gz mkdocs/docs/\n
"},{"location":"build/#update-services","title":"Update Services","text":"
# Pull latest images\ndocker compose pull\n\n# Recreate containers with new images\ndocker compose up -d\n
"},{"location":"build/#troubleshooting","title":"Troubleshooting","text":""},{"location":"build/#port-conflicts","title":"Port Conflicts","text":"

If services fail to start due to port conflicts:

  1. Check which ports are in use:
sudo ss -tulpn | grep LISTEN\n
  1. Re-run configuration to get new ports:
./config.sh\n
  1. Or manually edit .env file and change conflicting ports
"},{"location":"build/#permission-issues","title":"Permission Issues","text":"

Fix permission problems:

# Get your user and group IDs\nid -u  # User ID\nid -g  # Group ID\n\n# Update .env file with correct IDs\nUSER_ID=1000\nGROUP_ID=1000\n\n# Restart services\ndocker compose down && docker compose up -d\n
"},{"location":"build/#service-wont-start","title":"Service Won't Start","text":"

Debug service issues:

# Check detailed logs\ndocker compose logs [service-name] --tail 50\n\n# Check container status\ndocker ps -a\n\n# Inspect container\ndocker inspect [container-name]\n
"},{"location":"build/#cloudflare-tunnel-issues","title":"Cloudflare Tunnel Issues","text":"
# Check tunnel service status\nsudo systemctl status cloudflared-changemaker\n\n# View tunnel logs\nsudo journalctl -u cloudflared-changemaker -f\n\n# Restart tunnel\nsudo systemctl restart cloudflared-changemaker\n
"},{"location":"build/#next-steps","title":"Next Steps","text":"

Now that your Changemaker Lite instance is running:

  1. Set up Listmonk - Configure SMTP and create your first campaign
  2. Create workflows - Build automations in n8n
  3. Import data - Set up your NocoDB databases
  4. Configure map - Add location data for the map viewer
  5. Write documentation - Start creating content in MkDocs
  6. Set up Git - Initialize repositories in Gitea
"},{"location":"build/#getting-help","title":"Getting Help","text":""},{"location":"build/map/","title":"Map Build Guide","text":"

Map is BNKops canvassing application built for community organizing and door-to-door canvassing.

Complete Configuration

For detailed configuration, usage instructions, and troubleshooting, see the Map Configuration Guide.

Clean NocoDB

Currently the way to get a good result is to ensure the target nocodb database is empty. You can do this by deleting all bases. The script should still work with other volumes however may insert tables into odd locations; still debugging. Again, see config if needing to do manually.

"},{"location":"build/map/#prerequisites","title":"Prerequisites","text":""},{"location":"build/map/#quick-build-process","title":"Quick Build Process","text":""},{"location":"build/map/#1-get-nocodb-api-token","title":"1. Get NocoDB API Token","text":"
  1. Login to your NocoDB instance
  2. Click user icon \u2192 Account Settings \u2192 API Tokens
  3. Create new token with read/write permissions
  4. Copy the token for the next step
"},{"location":"build/map/#2-configure-environment","title":"2. Configure Environment","text":"

Edit the .env file in the map/ directory:

cd map\n

Update your .env file with your NocoDB details, specifically the instance and api token:

# NocoDB API Configuration\nNOCODB_API_URL=https://your-nocodb-instance.com/api/v1\nNOCODB_API_TOKEN=your-api-token-here\n\n# These will be populated after running build-nocodb.sh\nNOCODB_VIEW_URL=\nNOCODB_LOGIN_SHEET=\nNOCODB_SETTINGS_SHEET=\n\n# Server Configuration\nPORT=3000\nNODE_ENV=production\nSESSION_SECRET=your-secure-random-string\n\n# Map Defaults (Edmonton, AB)\nDEFAULT_LAT=53.5461\nDEFAULT_LNG=-113.4938\nDEFAULT_ZOOM=11\n\n# Production Settings\nCOOKIE_DOMAIN=.yourdomain.com\nALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000\n
"},{"location":"build/map/#3-auto-create-database-structure","title":"3. Auto-Create Database Structure","text":"

Run the build script to create required tables:

chmod +x build-nocodb.sh\n./build-nocodb.sh\n

This creates three tables: - Locations - Main map data with geo-location, contact info, support levels - Login - User authentication (email, name, admin flag) - Settings - Admin configuration and QR codes

"},{"location":"build/map/#4-get-table-urls","title":"4. Get Table URLs","text":"

After the script completes:

  1. Login to your NocoDB instance
  2. Navigate to your project (\"Map Viewer Project\")
  3. Copy the view URLs for each table from your browser address bar
  4. URLs should look like: https://your-nocodb.com/dashboard/#/nc/project-id/table-id
"},{"location":"build/map/#5-update-environment-with-urls","title":"5. Update Environment with URLs","text":"

Edit your .env file and add the table URLs:

NOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id\nNOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id\nNOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id\n
"},{"location":"build/map/#6-build-and-deploy","title":"6. Build and Deploy","text":"

Build the Docker image and start the application:

# Build the Docker image\ndocker-compose build\n\n# Start the application\ndocker-compose up -d\n
"},{"location":"build/map/#verify-installation","title":"Verify Installation","text":"
  1. Check container status:

    docker-compose ps\n

  2. View logs:

    docker-compose logs -f map-viewer\n

  3. Access the application at http://localhost:3000

"},{"location":"build/map/#quick-start","title":"Quick Start","text":"
  1. Login: Use an email from your Login table
  2. Add Locations: Click on the map to add new locations
  3. Admin Panel: Admin users can access /admin.html for configuration
  4. Walk Sheets: Generate printable canvassing forms with QR codes
"},{"location":"build/map/#maintenance-commands","title":"Maintenance Commands","text":""},{"location":"build/map/#update-application","title":"Update Application","text":"
docker-compose down\ngit pull origin main\ndocker-compose build\ndocker-compose up -d\n
"},{"location":"build/map/#development-mode","title":"Development Mode","text":"
cd app\nnpm install\nnpm run dev\n
"},{"location":"build/map/#health-check","title":"Health Check","text":"
curl http://localhost:3000/health\n
"},{"location":"build/map/#support","title":"Support","text":"

For detailed configuration, troubleshooting, and usage instructions, see the Map Configuration Guide.

"},{"location":"build/server/","title":"BNKops Server Build","text":"

Purpose: a Ubuntu server build-out for general application

This documentation is a overview of the full build out for a server OS and baseline for running Changemaker-lite. It is a manual to re-install this server on any machine.

All of the following systems are free and the majority are open source.

"},{"location":"build/server/#ubuntu-os","title":"Ubuntu OS","text":"

Ubuntu is a Linux distribution derived from Debian and composed mostly of free and open-source software.

"},{"location":"build/server/#install-ubuntu","title":"Install Ubuntu","text":""},{"location":"build/server/#post-install","title":"Post Install","text":"

Post installation, run update:

sudo apt update\n

sudo apt upgrade\n
"},{"location":"build/server/#configuration","title":"Configuration","text":"

Further configurations:

"},{"location":"build/server/#vscode-insiders","title":"VSCode Insiders","text":"

Visual Studio Code is a new choice of tool that combines the simplicity of a code editor with what developers need for the core edit-build-debug cycle.

"},{"location":"build/server/#install-using-app-centre","title":"Install Using App Centre","text":""},{"location":"build/server/#obsidian","title":"Obsidian","text":"

The free and flexible app for your private\u00a0thoughts.

"},{"location":"build/server/#install-using-app-center","title":"Install Using App Center","text":""},{"location":"build/server/#curl","title":"Curl","text":"

command line tool and library for transferring data with URLs (since 1998)

"},{"location":"build/server/#install","title":"Install","text":"
sudo apt install curl \n
"},{"location":"build/server/#glances","title":"Glances","text":"

Glances an Eye on your system. A top/htop alternative for GNU/Linux, BSD, Mac OS and Windows operating systems.

"},{"location":"build/server/#install_1","title":"Install","text":"
sudo snap install glances \n
"},{"location":"build/server/#syncthing","title":"Syncthing","text":"

Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it\u2019s transmitted over the internet.

"},{"location":"build/server/#install_2","title":"Install","text":"
# Add the release PGP keys:\nsudo mkdir -p /etc/apt/keyrings\nsudo curl -L -o /etc/apt/keyrings/syncthing-archive-keyring.gpg https://syncthing.net/release-key.gpg\n
# Add the \"stable\" channel to your APT sources:\necho \"deb [signed-by=/etc/apt/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable\" | sudo tee /etc/apt/sources.list.d/syncthing.list\n
# Update and install syncthing:\nsudo apt-get update\nsudo apt-get install syncthing\n
"},{"location":"build/server/#post-install_1","title":"Post Install","text":"

Run syncthing as a system service.

sudo systemctl start syncthing@yourusername\n

sudo systemctl enable syncthing@yourusername\n
"},{"location":"build/server/#docker","title":"Docker","text":"

Docker helps developers build, share, run, and verify applications anywhere \u2014 without tedious environment configuration or management.

# Add Docker's official GPG key:\nsudo apt-get update\nsudo apt-get install ca-certificates curl\nsudo install -m 0755 -d /etc/apt/keyrings\nsudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc\nsudo chmod a+r /etc/apt/keyrings/docker.asc\n\n# Add the repository to Apt sources:\necho \\\n  \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \\\n  $(. /etc/os-release && echo \"${UBUNTU_CODENAME:-$VERSION_CODENAME}\") stable\" | \\\n  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null\nsudo apt-get update\n

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin\n
"},{"location":"build/server/#update-users","title":"Update Users","text":"
sudo groupadd docker\n
sudo usermod -aG docker $USER\n
newgrp docker\n
"},{"location":"build/server/#enable-on-boot","title":"Enable on Boot","text":"
sudo systemctl enable docker.service\nsudo systemctl enable containerd.service\n
"},{"location":"build/server/#cloudflared","title":"Cloudflared","text":"

Connect, protect, and build everywhere. We make websites, apps, and networks faster and more secure. Our developer platform is the best place to build modern apps and deliver AI initiatives.

sudo mkdir -p --mode=0755 /usr/share/keyrings\ncurl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null\n
echo \"deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main\" | sudo tee /etc/apt/sources.list.d/cloudflared.list\n
sudo apt-get update && sudo apt-get install cloudflared\n
"},{"location":"build/server/#post-install_2","title":"Post Install","text":"

Login to Cloudflare

cloudflared login\n

"},{"location":"build/server/#configuration_1","title":"Configuration","text":"

The ./config.sh and ./start-production.sh scripts will properly configure a Cloudflare tunnel and service to put your system online. More info in the Cloudflare Configuration.

"},{"location":"build/server/#pandoc","title":"Pandoc","text":"

If you need to convert files from one markup format into another, pandoc is your swiss-army knife.

sudo apt install pandoc\n
"},{"location":"build/site/","title":"Building the Site with MkDocs Material","text":"

Welcome! This guide will help you get started building and customizing your site using MkDocs Material.

"},{"location":"build/site/#reset-site","title":"Reset Site","text":"

You can read through all the BNKops cmlite documentation already in your docs folder or you can reset your docs folder to a baseline to start and read more manuals here. To reset docs folder to baseline, run the following:

./reset-site.sh\n
"},{"location":"build/site/#how-to-build-your-site-step-by-step","title":"\ud83d\ude80 How to Build Your Site (Step by Step)","text":"
  1. Open your Coder instance. For example: coder.yourdomain.com
  2. Go to the mkdocs folder: In the terminal (for a new terminal press Crtl - Shift - ~), type:
    cd mkdocs\n
  3. Build the site: Type:
    mkdocs build\n
    This creates the static website from your documents and places them in the mkdocs/site directory.

Preview your site locally: Visit localhost:4000 for local development or live.youdomain.com to see a public live load.

Material for MkDocs Documentation

Build vs Serve

Your website is built in stages. Any edits to documents in the mkdocs directory are instantly served and visible at localhost:4000 or if in production mode live.yourdomain.com. The live site is not meant as a public access point and will crash if too many requests are made to it.

Running mkdocs build pushes any changes to the site directory, which then a ngnix server pushes them to the production server for public access at your root domain (yourdomain.com).

You can think of it as serve/live = draft for personal review and build = save/push to production for the public.

This combination allows for rapid development of documentation while ensuring your live site does not get updated until your content is ready.

"},{"location":"build/site/#resetting-the-site","title":"\ud83e\uddf9 Resetting the Site","text":"

If you want to start fresh:

  1. Delete all folders EXCEPT these folders:

  2. Reset the landing page:

  3. Reset the mkdocs.yml

"},{"location":"build/site/#using-ai-to-help-build-your-site","title":"\ud83e\udd16 Using AI to Help Build Your Site","text":""},{"location":"build/site/#first-time-setup-tips","title":"\ud83d\udee0\ufe0f First-Time Setup Tips","text":"

Quick Start Guide

"},{"location":"build/site/#more-resources","title":"\ud83d\udcda More Resources","text":"

Happy building!

"},{"location":"config/","title":"Configuration","text":"

There are several configuration steps to building a production ready Changemaker-Lite.

In the order we suggest doing them:

"},{"location":"config/cloudflare-config/","title":"Configure Cloudflare","text":"

Cloudflare is the largest DNS routing service on the planet. We use their free service tier to provide Changemaker users with a fast, secure, and reliable way to get online that blocks 99% of surface level attacks and has built in user authenticaion (if you so choose to use it)

"},{"location":"config/cloudflare-config/#credentials","title":"Credentials","text":"

The config.sh and start-production.sh scripts require the following Cloudflare credentials to function properly:

"},{"location":"config/cloudflare-config/#1-cloudflare-api-token","title":"1. Cloudflare API Token","text":""},{"location":"config/cloudflare-config/#2-cloudflare-zone-id","title":"2. Cloudflare Zone ID","text":""},{"location":"config/cloudflare-config/#3-cloudflare-account-id","title":"3. Cloudflare Account ID","text":""},{"location":"config/cloudflare-config/#4-cloudflare-tunnel-id-optional-in-configsh-required-in-start-productionsh","title":"4. Cloudflare Tunnel ID (Optional in config.sh, Required in start-production.sh)","text":"

Automatic Configuration of Tunnel

The start-production.sh script will automatically create a tunnel and system service for Cloudflare.

"},{"location":"config/cloudflare-config/#summary-of-required-credentials","title":"Summary of Required Credentials:","text":"
# In .env file\nCF_API_TOKEN=your_cloudflare_api_token\nCF_ZONE_ID=your_cloudflare_zone_id\nCF_ACCOUNT_ID=your_cloudflare_account_id\nCF_TUNNEL_ID=will_be_set_by_start_production  # This will be set by start-production.sh\n
"},{"location":"config/cloudflare-config/#notes","title":"Notes:","text":""},{"location":"config/coder/","title":"Coder Server Configuration","text":"

This section describes the configuration and features of the code-server environment.

"},{"location":"config/coder/#accessing-code-server","title":"Accessing Code Server","text":""},{"location":"config/coder/#retrieving-the-code-server-password","title":"Retrieving the Code Server Password","text":"

After the first build, the code-server password is stored in:

configs/code-server/.config/code-server/config.yaml\n

Look for the password: field in that file. For example:

password: 0c0dca951a2d12eff1665817\n

Note: It is recommended not to change this password manually, as it is securely generated.

"},{"location":"config/coder/#main-configuration-options","title":"Main Configuration Options","text":""},{"location":"config/coder/#installed-tools-and-features","title":"Installed Tools and Features","text":"

The code-server environment includes:

"},{"location":"config/coder/#using-mkdocs","title":"Using MkDocs","text":"

The virtual environment for MkDocs is automatically added to your PATH. You can run MkDocs commands directly, or use the provided script. For example, to build the site, from a clean terminal we would rung:

cd mkdocs \nmkdocs build\n
"},{"location":"config/coder/#claude-code-integration","title":"Claude Code Integration","text":"

The code-server environment comes with Claude Code (@anthropic-ai/claude-code) globally installed via npm.

"},{"location":"config/coder/#what-is-claude-code","title":"What is Claude Code?","text":"

Claude Code is an AI-powered coding assistant by Anthropic, designed to help you write, refactor, and understand code directly within your development environment.

"},{"location":"config/coder/#usage","title":"Usage","text":"

Note: Claude Code requires an API key or account with Anthropic for full functionality. Refer to the extension settings for configuration.

"},{"location":"config/coder/#call-claude","title":"Call Claude","text":"

To use claude simply type claude into the terminal and follow instructions.

claude\n
"},{"location":"config/coder/#shell-environment","title":"Shell Environment","text":"

The .bashrc is configured to include the MkDocs virtual environment and user-local binaries in your PATH for convenience.

"},{"location":"config/coder/#code-navigation-and-editing-features","title":"Code Navigation and Editing Features","text":"

The code-server environment provides robust code navigation and editing features, including:

"},{"location":"config/coder/#collaboration-features","title":"Collaboration Features","text":"

Code-server includes features to support collaboration:

"},{"location":"config/coder/#security-considerations","title":"Security Considerations","text":"

When using code-server, consider the following security aspects:

"},{"location":"config/coder/#ollama-integration","title":"Ollama Integration","text":"

The code-server environment includes Ollama, a tool for running large language models locally on your machine.

"},{"location":"config/coder/#what-is-ollama","title":"What is Ollama?","text":"

Ollama is a lightweight, extensible framework for building and running language models locally. It provides a simple API for creating, running, and managing models, making it easy to integrate AI capabilities into your development workflow without relying on external services.

"},{"location":"config/coder/#getting-started-with-ollama","title":"Getting Started with Ollama","text":""},{"location":"config/coder/#staring-ollama","title":"Staring Ollama","text":"

For ollama to be available, you need to open a terminal and run:

ollama serve\n

This will start the ollama server and you can then proceed to pulling a model and chatting.

"},{"location":"config/coder/#pulling-a-model","title":"Pulling a Model","text":"

To get started, you'll need to pull a model. For development and testing, we recommend starting with a smaller model like Gemma 2B:

ollama pull gemma2:2b\n

For even lighter resource usage, you can use the 1B parameter version:

ollama pull gemma2:1b\n
"},{"location":"config/coder/#running-a-model","title":"Running a Model","text":"

Once you've pulled a model, you can start an interactive session:

ollama run gemma2:2b\n
"},{"location":"config/coder/#available-models","title":"Available Models","text":"

Popular models available through Ollama include:

"},{"location":"config/coder/#using-ollama-in-your-development-workflow","title":"Using Ollama in Your Development Workflow","text":""},{"location":"config/coder/#api-access","title":"API Access","text":"

Ollama provides a REST API that runs on http://localhost:11434 by default. You can integrate this into your applications:

curl http://localhost:11434/api/generate -d '{\n  \"model\": \"gemma2:2b\",\n  \"prompt\": \"Write a Python function to calculate fibonacci numbers\",\n  \"stream\": false\n}'\n
"},{"location":"config/coder/#model-management","title":"Model Management","text":"

List installed models:

ollama list\n

Remove a model:

ollama rm gemma2:2b\n

Show model information:

ollama show gemma2:2b\n

"},{"location":"config/coder/#resource-considerations","title":"Resource Considerations","text":""},{"location":"config/coder/#integration-with-development-tools","title":"Integration with Development Tools","text":"

Ollama can be integrated with various development tools and editors through its API, enabling features like:

For more information, visit the Ollama documentation.

For more detailed information on configuring and using code-server, refer to the official code-server documentation.

"},{"location":"config/map/","title":"Map Configuration","text":"

The Map system is a containerized web application that visualizes geographic data from NocoDB on an interactive map using Leaflet.js. It's designed for canvassing applications and community organizing.

"},{"location":"config/map/#features","title":"Features","text":""},{"location":"config/map/#setup-process-overview","title":"Setup Process Overview","text":"

The setup process involves several steps that must be completed in order:

  1. Get NocoDB API Token - Create an API token in your NocoDB instance
  2. Configure Environment - Update the .env file with your NocoDB details
  3. Auto-Create Database Structure - Run the build script to create required tables
  4. Get Table URLs - Find and copy the URLs for the newly created tables
  5. Update Environment with URLs - Add the table URLs to your .env file
  6. Build and Deploy - Build the Docker image and start the application
"},{"location":"config/map/#prerequisites","title":"Prerequisites","text":""},{"location":"config/map/#step-1-get-nocodb-api-token","title":"Step 1: Get NocoDB API Token","text":"
  1. Login to your NocoDB instance
  2. Click your user icon \u2192 Account Settings
  3. Go to the API Tokens tab
  4. Click Create new token
  5. Set the following permissions:
  6. Read: Yes
  7. Write: Yes
  8. Delete: Yes (optional, for admin functions)
  9. Copy the generated token - you'll need it for the next step

Token Security

Keep your API token secure and never commit it to version control. The token provides full access to your NocoDB data.

"},{"location":"config/map/#step-2-configure-environment","title":"Step 2: Configure Environment","text":"

Edit the .env file in the map/ directory:

# NocoDB API Configuration\nNOCODB_API_URL=https://your-nocodb-instance.com/api/v1\nNOCODB_API_TOKEN=your-api-token-here\n\n# These URLs will be populated after running build-nocodb.sh\nNOCODB_VIEW_URL=\nNOCODB_LOGIN_SHEET=\nNOCODB_SETTINGS_SHEET=\n\n# Server Configuration\nPORT=3000\nNODE_ENV=production\n\n# Session Secret (generate with: openssl rand -hex 32)\nSESSION_SECRET=your-secure-random-string\n\n# Map Defaults (Edmonton, Alberta, Canada)\nDEFAULT_LAT=53.5461\nDEFAULT_LNG=-113.4938\nDEFAULT_ZOOM=11\n\n# Optional: Map Boundaries (prevents users from adding points outside area)\n# BOUND_NORTH=53.7\n# BOUND_SOUTH=53.4\n# BOUND_EAST=-113.3\n# BOUND_WEST=-113.7\n\n# Production Settings\nTRUST_PROXY=true\nCOOKIE_DOMAIN=.yourdomain.com\nALLOWED_ORIGINS=https://map.yourdomain.com,http://localhost:3000\n
"},{"location":"config/map/#required-configuration","title":"Required Configuration","text":""},{"location":"config/map/#optional-configuration","title":"Optional Configuration","text":""},{"location":"config/map/#step-3-auto-create-database-structure","title":"Step 3: Auto-Create Database Structure","text":"

The build-nocodb.sh script will automatically create the required tables in your NocoDB instance.

cd map\nchmod +x build-nocodb.sh\n./build-nocodb.sh\n
"},{"location":"config/map/#what-the-script-creates","title":"What the Script Creates","text":"

The script creates three tables with the following structure:

"},{"location":"config/map/#1-locations-table","title":"1. Locations Table","text":"

Main table for storing map data:

"},{"location":"config/map/#2-login-table","title":"2. Login Table","text":"

User authentication table:

"},{"location":"config/map/#3-settings-table","title":"3. Settings Table","text":"

Admin configuration table:

"},{"location":"config/map/#default-data","title":"Default Data","text":"

The script also creates: - A default admin user (admin@example.com) - A default start location setting

"},{"location":"config/map/#step-4-get-table-urls","title":"Step 4: Get Table URLs","text":"

After the script completes successfully:

  1. Login to your NocoDB instance
  2. Navigate to your project (should be named \"Map Viewer Project\")
  3. For each table, get the view URL:
  4. Click on the table name
  5. Copy the URL from your browser's address bar
  6. The URL should look like: https://your-nocodb.com/dashboard/#/nc/project-id/table-id

You need URLs for: - Locations table \u2192 NOCODB_VIEW_URL - Login table \u2192 NOCODB_LOGIN_SHEET - Settings table \u2192 NOCODB_SETTINGS_SHEET

"},{"location":"config/map/#step-5-update-environment-with-urls","title":"Step 5: Update Environment with URLs","text":"

Edit your .env file and add the table URLs:

# Update these with the actual URLs from your NocoDB instance\nNOCODB_VIEW_URL=https://your-nocodb.com/dashboard/#/nc/project-id/locations-table-id\nNOCODB_LOGIN_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/login-table-id\nNOCODB_SETTINGS_SHEET=https://your-nocodb.com/dashboard/#/nc/project-id/settings-table-id\n

URL Format

Make sure to use the complete dashboard URLs, not the API URLs. The application will automatically extract the project and table IDs from these URLs.

"},{"location":"config/map/#step-6-build-and-deploy","title":"Step 6: Build and Deploy","text":"

Build the Docker image and start the application:

# Build the Docker image\ndocker-compose build\n\n# Start the application\ndocker-compose up -d\n
"},{"location":"config/map/#verify-deployment","title":"Verify Deployment","text":"
  1. Check that the container is running:

    docker-compose ps\n

  2. Check the logs:

    docker-compose logs -f map-viewer\n

  3. Access the application at http://localhost:3000 (or your configured domain)

"},{"location":"config/map/#using-the-map-system","title":"Using the Map System","text":""},{"location":"config/map/#user-interface","title":"User Interface","text":""},{"location":"config/map/#main-map-view","title":"Main Map View","text":""},{"location":"config/map/#location-markers","title":"Location Markers","text":""},{"location":"config/map/#adding-locations","title":"Adding Locations","text":"
  1. Click on the map where you want to add a location
  2. Fill out the form with contact information
  3. Select support level and sign information
  4. Add any relevant notes
  5. Click \"Save Location\"
"},{"location":"config/map/#authentication","title":"Authentication","text":""},{"location":"config/map/#user-login","title":"User Login","text":""},{"location":"config/map/#admin-access","title":"Admin Access","text":""},{"location":"config/map/#admin-panel-features","title":"Admin Panel Features","text":""},{"location":"config/map/#start-location-configuration","title":"Start Location Configuration","text":""},{"location":"config/map/#walk-sheet-generator","title":"Walk Sheet Generator","text":""},{"location":"config/map/#api-endpoints","title":"API Endpoints","text":""},{"location":"config/map/#public-endpoints","title":"Public Endpoints","text":""},{"location":"config/map/#authentication-endpoints","title":"Authentication Endpoints","text":""},{"location":"config/map/#admin-endpoints-requires-admin-privileges","title":"Admin Endpoints (requires admin privileges)","text":""},{"location":"config/map/#troubleshooting","title":"Troubleshooting","text":""},{"location":"config/map/#common-issues","title":"Common Issues","text":""},{"location":"config/map/#locations-not-showing","title":"Locations not showing","text":""},{"location":"config/map/#cannot-add-locations","title":"Cannot add locations","text":""},{"location":"config/map/#authentication-issues","title":"Authentication issues","text":""},{"location":"config/map/#build-script-failures","title":"Build script failures","text":""},{"location":"config/map/#development-mode","title":"Development Mode","text":"

For development and debugging:

cd map/app\nnpm install\nnpm run dev\n

This will start the application with hot reload and detailed logging.

"},{"location":"config/map/#logs-and-monitoring","title":"Logs and Monitoring","text":"

View application logs:

docker-compose logs -f map-viewer\n

Check health status:

curl http://localhost:3000/health\n

"},{"location":"config/map/#security-considerations","title":"Security Considerations","text":"
  1. API Token Security: Keep tokens secure and rotate regularly
  2. HTTPS: Use HTTPS in production
  3. CORS Configuration: Set appropriate ALLOWED_ORIGINS
  4. Cookie Security: Configure COOKIE_DOMAIN properly
  5. Input Validation: All inputs are validated server-side
  6. Rate Limiting: API endpoints have rate limiting
  7. Session Security: Use a strong SESSION_SECRET
"},{"location":"config/map/#maintenance","title":"Maintenance","text":""},{"location":"config/map/#regular-updates","title":"Regular Updates","text":"
# Stop the application\ndocker-compose down\n\n# Pull updates (if using git)\ngit pull origin main\n\n# Rebuild and restart\ndocker-compose build\ndocker-compose up -d\n
"},{"location":"config/map/#backup-considerations","title":"Backup Considerations","text":""},{"location":"config/map/#performance-tips","title":"Performance Tips","text":""},{"location":"config/map/#support","title":"Support","text":"

For issues or questions: 1. Check the troubleshooting section above 2. Review NocoDB documentation 3. Check Docker and Docker Compose documentation 4. Open an issue on GitHub

"},{"location":"config/mkdocs/","title":"MkDocs Customization & Features Overview","text":"

BNKops has been building our own features, widgets, and css styles for MKdocs material theme.

This document explains the custom styling, repository widgets, and key features enabled in this MkDocs site.

For more info on how to build your site see Site Build

"},{"location":"config/mkdocs/#using-the-repository-widget-in-documentation","title":"Using the Repository Widget in Documentation","text":"

You can embed repository widgets directly in your Markdown documentation to display live repository stats and metadata. To do this, add a div with the appropriate class and data-repo attribute for the repository you want to display.

Example (for a Gitea repository):

<div class=\"gitea-widget\" data-repo=\"admin/changemaker.lite\"></div>\n

This will render a styled card with information about the admin/changemaker.lite repository:

Options: You can control the widget display with additional data attributes: - data-show-description=\"false\" \u2014 Hide the description - data-show-language=\"false\" \u2014 Hide the language - data-show-last-update=\"false\" \u2014 Hide the last update date

Example with options:

<div class=\"gitea-widget\" data-repo=\"admin/changemaker.lite\" data-show-description=\"false\"></div>\n

For GitHub repositories, use the github-widget class:

<div class=\"github-widget\" data-repo=\"lyqht/mini-qr\"></div>\n

"},{"location":"config/mkdocs/#custom-css-styling-stylesheetsextracss","title":"Custom CSS Styling (stylesheets/extra.css)","text":"

The extra.css file provides extensive custom styling for the site, including:

"},{"location":"config/mkdocs/#repository-widgets","title":"Repository Widgets","text":""},{"location":"config/mkdocs/#data-generation-hooksrepo_widget_hookpy","title":"Data Generation (hooks/repo_widget_hook.py)","text":""},{"location":"config/mkdocs/#github-widget-javascriptsgithub-widgetjs","title":"GitHub Widget (javascripts/github-widget.js)","text":""},{"location":"config/mkdocs/#gitea-widget-javascriptsgitea-widgetjs","title":"Gitea Widget (javascripts/gitea-widget.js)","text":""},{"location":"config/mkdocs/#mkdocs-features-mkdocsyml","title":"MkDocs Features (mkdocs.yml)","text":"

Key features and plugins enabled:

"},{"location":"config/mkdocs/#summary","title":"Summary","text":"

This MkDocs site is highly customized for developer documentation, with visually rich repository widgets, improved code and table rendering, and a modern, responsive UI. All repository stats are fetched at build time for performance and reliability.

"},{"location":"how%20to/canvass/","title":"Canvas","text":"

This is BNKops canvassing how to! In the following document, you will find all sorts of tips and tricks for door knocking, canvassing, and using the BNKops canvassing app.

"},{"location":"manual/","title":"Manuals","text":"

The following are manuals, some accompanied by videos, on the use of the system.

"},{"location":"manual/map/","title":"Map Viewer Manual","text":"

This manual provides step-by-step instructions for using the NocoDB Map Viewer web application. Each section covers a major feature with direct instructions. (Insert screenshot - feature overview)

"},{"location":"manual/map/#1-logging-in","title":"1. Logging In","text":"
  1. Go to the map site URL (e.g., http://localhost:3000).
  2. Enter your email and password on the login page.
  3. Click Login.
  4. If you forget your password, contact an admin. (Insert screenshot - login page)
"},{"location":"manual/map/#2-viewing-the-map","title":"2. Viewing the Map","text":"
  1. After login, you will see the interactive map.
  2. Use your mouse or touch to pan and zoom.
  3. Your current location may be shown as a blue dot. (Insert screenshot - main map view)
"},{"location":"manual/map/#3-adding-a-new-location","title":"3. Adding a New Location","text":"
  1. Click the Add Location button (usually a plus icon on the map).
  2. Click on the map where you want to add the new location.
  3. Fill out the form:
  4. First Name, Last Name, Email, Phone, Unit Number, Support Level, Address, Sign, Sign Size, Notes.
  5. Click Save.
  6. The new location will appear as a marker on the map. (Insert screenshot - add location form)
"},{"location":"manual/map/#4-editing-or-deleting-a-location","title":"4. Editing or Deleting a Location","text":"
  1. Click on a location marker.
  2. In the popup, click Edit to update details, or Delete to remove the location.
  3. Confirm your changes. (Insert screenshot - location popup with edit/delete)
"},{"location":"manual/map/#5-auto-refresh","title":"5. Auto-Refresh","text":"

The map automatically refreshes every 30 seconds to show the latest data. (Insert screenshot - refresh indicator)

"},{"location":"manual/map/#6-map-start-location-boundaries","title":"6. Map Start Location & Boundaries","text":"
  1. The map opens to the default start location (Edmonton, Canada, unless changed by admin).
  2. Admins can set boundaries to restrict where points can be added. (Insert screenshot - map boundaries)
"},{"location":"manual/map/#7-walk-sheet-generator","title":"7. Walk Sheet Generator","text":"
  1. Go to the Walk Sheet section (usually in the admin panel).
  2. Enter the title, subtitle, footer, and QR code info.
  3. Click Generate to create a printable walk sheet.
  4. Download or print the sheet. (Insert screenshot - walk sheet generator)
"},{"location":"manual/map/#8-qr-code-integration","title":"8. QR Code Integration","text":"
  1. QR codes can be added to walk sheets for quick access to digital resources.
  2. Enter the URL and label for each QR code in the settings.
  3. QR codes will appear on the generated walk sheet. (Insert screenshot - QR code on walk sheet)
"},{"location":"manual/map/#9-volunteer-shift-management","title":"9. Volunteer Shift Management","text":""},{"location":"manual/map/#for-all-users","title":"For All Users","text":"
  1. Go to Shifts (http://localhost:3000/shifts.html).
  2. View shifts in Grid or Calendar view.
  3. Click a shift to see details.
  4. Click Sign Up to join a shift.
  5. Your signed-up shifts are shown at the top. (Insert screenshot - shifts grid and calendar)
"},{"location":"manual/map/#cancel-a-signup","title":"Cancel a Signup","text":"
  1. Click Cancel next to a shift you signed up for.
  2. Confirm cancellation. (Insert screenshot - cancel signup)
"},{"location":"manual/map/#calendar-color-codes","title":"Calendar Color Codes","text":""},{"location":"manual/map/#10-admin-features","title":"10. Admin Features","text":""},{"location":"manual/map/#shift-management","title":"Shift Management","text":"
  1. Go to Admin Panel (http://localhost:3000/admin.html).
  2. Create, edit, or cancel shifts.
  3. View all signups and manage volunteers. (Insert screenshot - admin shift management)
"},{"location":"manual/map/#user-management","title":"User Management","text":"
  1. In the admin panel, go to User Management.
  2. Add new users with email, password, and role (admin/user).
  3. Delete users as needed. (Insert screenshot - user management panel)
"},{"location":"manual/map/#map-start-location","title":"Map Start Location","text":"
  1. In the admin panel, go to Start Location.
  2. Select coordinates and zoom level.
  3. Save changes to update the map default. (Insert screenshot - start location config)
"},{"location":"manual/map/#walk-sheet-config","title":"Walk Sheet Config","text":"
  1. In the admin panel, go to Walk Sheet Config.
  2. Edit walk sheet fields and QR codes.
  3. Save to persist changes. (Insert screenshot - walk sheet config panel)
"},{"location":"manual/map/#11-troubleshooting","title":"11. Troubleshooting","text":""},{"location":"manual/map/#12-security-privacy","title":"12. Security & Privacy","text":""},{"location":"manual/map/#13-support","title":"13. Support","text":"

For help, check the troubleshooting section, review NocoDB docs, or contact your admin. (Insert screenshot - help section)

"},{"location":"phil/","title":"Philosophy: Your Secrets, Your Power, Your Movement","text":""},{"location":"phil/#the-question-that-changes-everything","title":"The Question That Changes Everything!","text":"

If you are a political actor, who do you trust with your secrets?

This isn't just a technical question\u2014it's the core political question of our time. Every email you send, every document you create, every contact list you build, every strategy you develop: where does it live? Who owns the servers? Who has the keys?

"},{"location":"phil/#the-corporate-extraction-machine","title":"The Corporate Extraction Machine","text":""},{"location":"phil/#how-they-hook-you","title":"How They Hook You","text":"

Corporate software companies have perfected the art of digital snake oil sales:

  1. Free Trials - They lure you in with \"free\" accounts
  2. Feature Creep - Essential features require paid tiers
  3. Data Lock-In - Your data becomes harder to export
  4. Price Escalation - $40/month becomes $750/month as you grow
  5. Surveillance Integration - Your organizing becomes their intelligence
"},{"location":"phil/#the-real-product","title":"The Real Product","text":"

You Are Not the Customer

If you're not paying for the product, you ARE the product. But even when you are paying, you're often still the product.

Corporate platforms don't make money from your subscription fees\u2014they make money from:

"},{"location":"phil/#the-bnkops-alternative","title":"The BNKops Alternative","text":""},{"location":"phil/#who-we-are","title":"Who We Are","text":"

BNKops is a cooperative based in amiskwaciy-w\u00e2skahikan (Edmonton, Alberta) on Treaty 6 territory. We're not a corporation\u2014we're a collective of skilled organizers, developers, and community builders who believe technology should serve liberation, not oppression.

"},{"location":"phil/#our-principles","title":"Our Principles","text":""},{"location":"phil/#liberation-first","title":"\ud83c\udff3\ufe0f\u200d\u26a7\ufe0f \ud83c\udff3\ufe0f\u200d\ud83c\udf08 \ud83c\uddf5\ud83c\uddf8 Liberation First","text":"

Technology that centers the most marginalized voices and fights for collective liberation. We believe strongly that the medium is the message; if you the use the medium of fascists, what does that say about your movement?

"},{"location":"phil/#community-over-profit","title":"\ud83e\udd1d Community Over Profit","text":"

We operate as a cooperative because we believe in shared ownership and democratic decision-making. No venture capitalists, no shareholders, no extraction.

"},{"location":"phil/#data-sovereignty","title":"\u26a1 Data Sovereignty","text":"

Your data belongs to you and your community. We build tools that let you own your digital infrastructure completely.

"},{"location":"phil/#security-culture","title":"\ud83d\udd12 Security Culture","text":"

Real security comes from community control, not corporate promises. We integrate security culture practices into our technology design.

"},{"location":"phil/#the-changemaker-difference","title":"The Changemaker Difference","text":""},{"location":"phil/#traditional-corporate-flow","title":"Traditional Corporate Flow","text":"
Your Data \u2192 Corporate Server \u2192 Surveillance \u2192 Profit \u2192 Your Oppression\n
"},{"location":"phil/#changemaker-flow","title":"Changemaker Flow","text":"
Your Data \u2192 Your Server \u2192 Your Community \u2192 Your Power \u2192 Liberation\n
"},{"location":"phil/#why-this-matters","title":"Why This Matters","text":"

When you control your technology infrastructure:

"},{"location":"phil/#the-philosophy-in-practice","title":"The Philosophy in Practice","text":""},{"location":"phil/#security-culture-meets-technology","title":"Security Culture Meets Technology","text":"

Traditional security culture asks: \"Who needs to know this information?\"

Digital security culture asks: \"Who controls the infrastructure where this information lives?\"

"},{"location":"phil/#community-technology","title":"Community Technology","text":"

We believe in community technology - tools that:

"},{"location":"phil/#prefigurative-politics","title":"Prefigurative Politics","text":"

The tools we use shape the movements we build. Corporate tools create corporate movements\u2014hierarchical, surveilled, and dependent. Community-controlled tools create community-controlled movements\u2014democratic, secure, and sovereign.

"},{"location":"phil/#common-questions","title":"Common Questions","text":""},{"location":"phil/#isnt-this-just-for-tech-people","title":"\"Isn't this just for tech people?\"","text":"

No. We specifically designed Changemaker Lite for organizers, activists, and movement builders who may not have technical backgrounds. Our philosophy is that everyone deserves digital sovereignty, not just people with computer science degrees.

This is not to say that you won't need to learn! These tools are just that; tools. They have no fancy or white-labeled marketing and are technical in nature. You will need to learn to use them, just as any worker needs to learn the power tools they use on the job.

"},{"location":"phil/#what-about-convenience","title":"\"What about convenience?\"","text":"

Corporate platforms are convenient because they've extracted billions of dollars from users to fund that convenience. When you own your tools, there's a learning curve\u2014but it's the same learning curve as learning to organize, learning to build power, learning to create change.

"},{"location":"phil/#cant-we-just-use-corporate-tools-carefully","title":"\"Can't we just use corporate tools carefully?\"","text":"

Would you hold your most sensitive organizing meetings in a room owned by your opposition? Would you store your membership lists in filing cabinets at a corporation that profits from surveillance? Digital tools are the same.

"},{"location":"phil/#what-about-security","title":"\"What about security?\"","text":"

Real security comes from community control, not corporate promises. When you control your infrastructure:

"},{"location":"phil/#historical-context","title":"Historical Context","text":""},{"location":"phil/#learning-from-past-struggles","title":"Learning from Past Struggles","text":"

Every liberation movement has had to solve the problem of secure communication and information sharing:

The internet should expand these traditions, not replace them with corporate surveillance.

"},{"location":"phil/#the-surveillance-capitalism-trap","title":"The Surveillance Capitalism Trap","text":"

As Shoshana Zuboff documents in \"The Age of Surveillance Capitalism,\" we're living through a new form of capitalism that extracts value from human experience itself. Political movements are particularly valuable targets because:

"},{"location":"phil/#taking-action","title":"Taking Action","text":""},{"location":"phil/#start-where-you-are","title":"Start Where You Are","text":"

You don't have to replace everything at once. Start with one tool, one campaign, one project. Learn the technology alongside your organizing.

"},{"location":"phil/#build-community-capacity","title":"Build Community Capacity","text":"

The goal isn't individual self-sufficiency\u2014it's community technological sovereignty. Share skills, pool resources, learn together.

"},{"location":"phil/#connect-with-others","title":"Connect with Others","text":"

You're not alone in this. The free and open source software community, the digital security community, and the appropriate technology movement are all working on similar problems.

"},{"location":"phil/#remember-why","title":"Remember Why","text":"

This isn't about technology for its own sake. It's about building the infrastructure for the world we want to see\u2014where communities have power, where people control their own data, where technology serves liberation.

"},{"location":"phil/#resources-for-deeper-learning","title":"Resources for Deeper Learning","text":""},{"location":"phil/#essential-reading","title":"Essential Reading","text":""},{"location":"phil/#community-resources","title":"Community Resources","text":""},{"location":"phil/#technical-learning","title":"Technical Learning","text":"

This philosophy document is a living document. Contribute your thoughts, experiences, and improvements through the BNKops documentation platform.

"},{"location":"phil/cost-comparison/","title":"Cost Comparison: Corporation vs. Community","text":""},{"location":"phil/cost-comparison/#the-true-cost-of-corporate-dependency","title":"The True Cost of Corporate Dependency","text":"

When movements choose corporate software, they're not just paying subscription fees\u2014they're paying with their power, their privacy, and their future. Let's break down the real costs.

"},{"location":"phil/cost-comparison/#monthly-cost-analysis","title":"Monthly Cost Analysis","text":""},{"location":"phil/cost-comparison/#small-campaign-50-supporters-5000-emailsmonth","title":"Small Campaign (50 supporters, 5,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $59/month Listmonk $0* Database & CRM Airtable Pro $240/month NocoDB $0* Website Hosting Squarespace $40/month Static Server $0* Documentation Notion Team $96/month MkDocs $0* Development GitHub Codespaces $87/month Code Server $0* Automation Zapier Professional $73/month n8n $0* File Storage Google Workspace $72/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $667/month $50/month

*Included in base Changemaker Lite hosting cost \u2020Privacy costs are incalculable but include surveillance, data sales, and community manipulation

"},{"location":"phil/cost-comparison/#medium-campaign-500-supporters-50000-emailsmonth","title":"Medium Campaign (500 supporters, 50,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $299/month Listmonk $0* Database & CRM Airtable Pro $600/month NocoDB $0* Website Hosting Squarespace $65/month Static Server $0* Documentation Notion Team $240/month MkDocs $0* Development GitHub Codespaces $174/month Code Server $0* Automation Zapier Professional $146/month n8n $0* File Storage Google Workspace $144/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $1,668/month $75/month"},{"location":"phil/cost-comparison/#large-campaign-5000-supporters-500000-emailsmonth","title":"Large Campaign (5,000 supporters, 500,000 emails/month)","text":"Service Category Corporate Solution Monthly Cost Changemaker Lite Monthly Cost Email Marketing Mailchimp $1,499/month Listmonk $0* Database & CRM Airtable Pro $1,200/month NocoDB $0* Website Hosting Squarespace + CDN $120/month Static Server $0* Documentation Notion Team $480/month MkDocs $0* Development GitHub Codespaces $348/month Code Server $0* Automation Zapier Professional $292/month n8n $0* File Storage Google Workspace $288/month PostgreSQL + Storage $0* Analytics Corporate tracking Privacy cost\u2020 Self-hosted $0* TOTAL $4,227/month $150/month"},{"location":"phil/cost-comparison/#annual-savings-breakdown","title":"Annual Savings Breakdown","text":""},{"location":"phil/cost-comparison/#3-year-cost-comparison","title":"3-Year Cost Comparison","text":"Campaign Size Corporate Total Changemaker Total Savings Small $24,012 $1,800 $22,212 Medium $60,048 $2,700 $57,348 Large $152,172 $5,400 $146,772"},{"location":"phil/cost-comparison/#hidden-costs-of-corporate-software","title":"Hidden Costs of Corporate Software","text":""},{"location":"phil/cost-comparison/#what-you-cant-put-a-price-on","title":"What You Can't Put a Price On","text":""},{"location":"phil/cost-comparison/#privacy-violations","title":"Privacy Violations","text":""},{"location":"phil/cost-comparison/#political-manipulation","title":"Political Manipulation","text":""},{"location":"phil/cost-comparison/#movement-disruption","title":"Movement Disruption","text":""},{"location":"phil/cost-comparison/#the-changemaker-advantage","title":"The Changemaker Advantage","text":""},{"location":"phil/cost-comparison/#what-you-get-for-50-150month","title":"What You Get for $50-150/month","text":""},{"location":"phil/cost-comparison/#complete-infrastructure","title":"Complete Infrastructure","text":""},{"location":"phil/cost-comparison/#true-ownership","title":"True Ownership","text":""},{"location":"phil/cost-comparison/#community-support","title":"Community Support","text":""},{"location":"phil/cost-comparison/#the-compound-effect","title":"The Compound Effect","text":""},{"location":"phil/cost-comparison/#year-over-year-savings","title":"Year Over Year Savings","text":"

Corporate software costs grow exponentially: - Year 1: \"Starter\" pricing to hook you - Year 2: Feature limits force tier upgrades - Year 3: Usage growth triggers premium pricing - Year 4: Platform changes force expensive migrations - Year 5: Lock-in enables arbitrary price increases

Changemaker Lite costs grow linearly with actual infrastructure needs: - Year 1: Base infrastructure costs - Year 2: Modest increases for storage/bandwidth only - Year 3: Scale only with actual technical requirements - Year 4: Community-driven improvements at no extra cost - Year 5: Established infrastructure with declining per-user costs

"},{"location":"phil/cost-comparison/#10-year-projection","title":"10-Year Projection","text":"Year Corporate (Medium Campaign) Changemaker Lite Annual Savings 1 $20,016 $900 $19,116 2 $22,017 $900 $21,117 3 $24,219 $1,080 $23,139 4 $26,641 $1,080 $25,561 5 $29,305 $1,260 $28,045 6 $32,235 $1,260 $30,975 7 $35,459 $1,440 $34,019 8 $39,005 $1,440 $37,565 9 $42,905 $1,620 $41,285 10 $47,196 $1,620 $45,576 TOTAL $318,998 $12,600 $306,398"},{"location":"phil/cost-comparison/#calculate-your-own-savings","title":"Calculate Your Own Savings","text":""},{"location":"phil/cost-comparison/#current-corporate-costs-worksheet","title":"Current Corporate Costs Worksheet","text":"

Email Marketing: $____/month Database/CRM: $____/month Website Hosting: $____/month Documentation: $____/month Development Tools: $____/month Automation: $____/month File Storage: $____/month Other SaaS: $____/month

Monthly Total: $____ Annual Total: $____

Changemaker Alternative: $50-150/month Your Annual Savings: $____

"},{"location":"phil/cost-comparison/#beyond-the-numbers","title":"Beyond the Numbers","text":""},{"location":"phil/cost-comparison/#what-movements-do-with-their-savings","title":"What Movements Do With Their Savings","text":"

The money saved by choosing community-controlled technology doesn't disappear\u2014it goes directly back into movement building:

"},{"location":"phil/cost-comparison/#making-the-switch","title":"Making the Switch","text":""},{"location":"phil/cost-comparison/#transition-strategy","title":"Transition Strategy","text":"

You don't have to switch everything at once:

  1. Start with documentation - Move your knowledge base to MkDocs
  2. Add email infrastructure - Set up Listmonk for newsletters
  3. Build your database - Move contact management to NocoDB
  4. Automate connections - Use n8n to integrate everything
  5. Phase out corporate tools - Cancel subscriptions as you replicate functionality
"},{"location":"phil/cost-comparison/#investment-timeline","title":"Investment Timeline","text":""},{"location":"phil/cost-comparison/#roi-calculation","title":"ROI Calculation","text":"

Most campaigns recover their entire first-year investment in 60-90 days through subscription savings alone.

Ready to stop feeding your budget to corporate surveillance? Get started with Changemaker Lite today and take control of your digital infrastructure.

"},{"location":"services/","title":"Services","text":"

Changemaker Lite includes several powerful services that work together to provide a complete documentation and development platform. Each service is containerized and can be accessed through its dedicated port.

"},{"location":"services/#available-services","title":"Available Services","text":""},{"location":"services/#code-server","title":"Code Server","text":"

Port: 8888 | Visual Studio Code in your browser for remote development

"},{"location":"services/#listmonk","title":"Listmonk","text":"

Port: 9000 | Self-hosted newsletter and mailing list manager

"},{"location":"services/#postgresql","title":"PostgreSQL","text":"

Port: 5432 | Reliable database backend - Data persistence for Listmonk - ACID compliance - High performance - Backup and restore capabilities

"},{"location":"services/#mkdocs-material","title":"MkDocs Material","text":"

Port: 4000 | Documentation site generator with live preview

"},{"location":"services/#static-site-server","title":"Static Site Server","text":"

Port: 4001 | Nginx-powered static site hosting - High-performance serving - Built documentation hosting - Caching and compression - Security headers

"},{"location":"services/#n8n","title":"n8n","text":"

Port: 5678 | Workflow automation tool

"},{"location":"services/#nocodb","title":"NocoDB","text":"

Port: 8090 | No-code database platform

"},{"location":"services/#homepage","title":"Homepage","text":"

Port: 3010 | Modern dashboard for all services

"},{"location":"services/#gitea","title":"Gitea","text":"

Port: 3030 | Self-hosted Git service

"},{"location":"services/#mini-qr","title":"Mini QR","text":"

Port: 8089 | Simple QR code generator service

"},{"location":"services/#map","title":"Map","text":"

Port: 3000 | Canvassing and community organizing application

"},{"location":"services/#service-architecture","title":"Service Architecture","text":"
\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502   Homepage      \u2502    \u2502   Code Server   \u2502    \u2502     MkDocs      \u2502\n\u2502     :3010       \u2502    \u2502     :8888       \u2502    \u2502     :4000       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Static Server   \u2502    \u2502    Listmonk     \u2502    \u2502      n8n        \u2502\n\u2502     :4001       \u2502    \u2502     :9000       \u2502    \u2502     :5678       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510    \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502     NocoDB      \u2502    \u2502 PostgreSQL      \u2502    \u2502 PostgreSQL      \u2502\n\u2502     :8090       \u2502    \u2502 (listmonk-db)   \u2502    \u2502 (root_db)       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2502     :5432       \u2502    \u2502     :5432       \u2502\n                      \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518    \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502      Map        \u2502\n\u2502     :3000       \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n
"},{"location":"services/code-server/","title":"Code Server","text":""},{"location":"services/code-server/#overview","title":"Overview","text":"

Code Server provides a full Visual Studio Code experience in your web browser, allowing you to develop from any device. It runs on your server and provides access to your development environment through a web interface.

"},{"location":"services/code-server/#features","title":"Features","text":""},{"location":"services/code-server/#access","title":"Access","text":""},{"location":"services/code-server/#configuration","title":"Configuration","text":""},{"location":"services/code-server/#environment-variables","title":"Environment Variables","text":""},{"location":"services/code-server/#volumes","title":"Volumes","text":""},{"location":"services/code-server/#usage","title":"Usage","text":"
  1. Access Code Server at http://localhost:8888
  2. Open the /home/coder/mkdocs/ workspace
  3. Start editing your documentation files
  4. Install extensions as needed
  5. Use the integrated terminal for commands
"},{"location":"services/code-server/#useful-extensions","title":"Useful Extensions","text":"

Consider installing these extensions for better documentation work:

"},{"location":"services/code-server/#official-documentation","title":"Official Documentation","text":"

For more detailed information, visit the official Code Server documentation.

"},{"location":"services/gitea/","title":"Gitea","text":"

Self-hosted Git service for collaborative development.

"},{"location":"services/gitea/#overview","title":"Overview","text":"

Gitea is a lightweight, self-hosted Git service similar to GitHub, GitLab, and Bitbucket. It provides a web interface for managing repositories, issues, pull requests, and more.

"},{"location":"services/gitea/#features","title":"Features","text":""},{"location":"services/gitea/#access","title":"Access","text":""},{"location":"services/gitea/#configuration","title":"Configuration","text":""},{"location":"services/gitea/#environment-variables","title":"Environment Variables","text":""},{"location":"services/gitea/#volumes","title":"Volumes","text":""},{"location":"services/gitea/#usage","title":"Usage","text":"
  1. Access Gitea at http://localhost:${GITEA_WEB_PORT:-3030}
  2. Register or log in as an admin user
  3. Create or import repositories
  4. Collaborate with your team
"},{"location":"services/gitea/#official-documentation","title":"Official Documentation","text":"

For more details, visit the official Gitea documentation.

"},{"location":"services/homepage/","title":"Homepage","text":"

Modern dashboard for accessing all your self-hosted services.

"},{"location":"services/homepage/#overview","title":"Overview","text":"

Homepage is a modern, fully static, fast, secure fully configurable application dashboard with integrations for over 100 services. It provides a beautiful and customizable interface to access all your Changemaker Lite services from a single location.

"},{"location":"services/homepage/#features","title":"Features","text":""},{"location":"services/homepage/#access","title":"Access","text":""},{"location":"services/homepage/#configuration","title":"Configuration","text":""},{"location":"services/homepage/#environment-variables","title":"Environment Variables","text":""},{"location":"services/homepage/#configuration-files","title":"Configuration Files","text":"

Homepage uses YAML configuration files located in ./configs/homepage/:

"},{"location":"services/homepage/#volumes","title":"Volumes","text":""},{"location":"services/homepage/#changemaker-lite-services","title":"Changemaker Lite Services","text":"

Homepage is pre-configured with all Changemaker Lite services:

"},{"location":"services/homepage/#essential-tools","title":"Essential Tools","text":""},{"location":"services/homepage/#content-documentation","title":"Content & Documentation","text":""},{"location":"services/homepage/#automation-data","title":"Automation & Data","text":""},{"location":"services/homepage/#customization","title":"Customization","text":""},{"location":"services/homepage/#adding-custom-services","title":"Adding Custom Services","text":"

Edit configs/homepage/services.yaml to add new services:

- Custom Category:\n    - My Service:\n        href: http://localhost:8080\n        description: Custom service description\n        icon: mdi-application\n        widget:\n          type: ping\n          url: http://localhost:8080\n
"},{"location":"services/homepage/#custom-icons","title":"Custom Icons","text":"

Add custom icons to ./assets/icons/ directory and reference them in services.yaml:

icon: /icons/my-custom-icon.png\n
"},{"location":"services/homepage/#themes-and-styling","title":"Themes and Styling","text":"

Modify configs/homepage/settings.yaml to customize appearance:

theme: dark  # or light\ncolor: purple  # slate, gray, zinc, neutral, stone, red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose\n
"},{"location":"services/homepage/#widgets","title":"Widgets","text":"

Enable live monitoring widgets in configs/homepage/services.yaml:

- Service Name:\n    widget:\n      type: docker\n      container: container-name\n      server: my-docker\n
"},{"location":"services/homepage/#service-monitoring","title":"Service Monitoring","text":"

Homepage can display real-time status information for your services:

"},{"location":"services/homepage/#docker-integration","title":"Docker Integration","text":"

Homepage monitors Docker containers automatically when configured:

  1. Ensure Docker socket is mounted (/var/run/docker.sock)
  2. Configure container mappings in docker.yaml
  3. Add widget configurations to services.yaml
"},{"location":"services/homepage/#security-considerations","title":"Security Considerations","text":""},{"location":"services/homepage/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/homepage/#common-issues","title":"Common Issues","text":"

Configuration not loading: Check YAML syntax in configuration files

docker logs homepage-changemaker\n

Icons not displaying: Verify icon paths and file permissions

ls -la ./assets/icons/\n

Services not reachable: Verify network connectivity between containers

docker exec homepage-changemaker ping service-name\n

Widget data not updating: Check Docker socket permissions and container access

docker exec homepage-changemaker ls -la /var/run/docker.sock\n
"},{"location":"services/homepage/#configuration-examples","title":"Configuration Examples","text":""},{"location":"services/homepage/#basic-service-widget","title":"Basic Service Widget","text":"
- Code Server:\n    href: http://localhost:8888\n    description: VS Code in the browser\n    icon: code-server\n    widget:\n      type: docker\n      container: code-server-changemaker\n
"},{"location":"services/homepage/#custom-dashboard-layout","title":"Custom Dashboard Layout","text":"
# settings.yaml\nlayout:\n  style: columns\n  columns: 3\n\n# Responsive breakpoints\nresponsive:\n  mobile: 1\n  tablet: 2\n  desktop: 3\n
"},{"location":"services/homepage/#official-documentation","title":"Official Documentation","text":"

For comprehensive configuration guides and advanced features:

"},{"location":"services/listmonk/","title":"Listmonk","text":"

Self-hosted newsletter and mailing list manager.

"},{"location":"services/listmonk/#overview","title":"Overview","text":"

Listmonk is a modern, feature-rich newsletter and mailing list manager designed for high performance and easy management. It provides a complete solution for email campaigns, subscriber management, and analytics.

"},{"location":"services/listmonk/#features","title":"Features","text":""},{"location":"services/listmonk/#access","title":"Access","text":""},{"location":"services/listmonk/#configuration","title":"Configuration","text":""},{"location":"services/listmonk/#environment-variables","title":"Environment Variables","text":""},{"location":"services/listmonk/#database","title":"Database","text":"

Listmonk uses PostgreSQL as its backend database. The database is automatically configured through the docker-compose setup.

"},{"location":"services/listmonk/#uploads","title":"Uploads","text":""},{"location":"services/listmonk/#getting-started","title":"Getting Started","text":"
  1. Access Listmonk at http://localhost:9000
  2. Log in with your admin credentials
  3. Set up your first mailing list
  4. Configure SMTP settings for sending emails
  5. Import subscribers or create subscription forms
  6. Create your first campaign
"},{"location":"services/listmonk/#important-notes","title":"Important Notes","text":""},{"location":"services/listmonk/#official-documentation","title":"Official Documentation","text":"

For comprehensive guides and API documentation, visit: - Listmonk Documentation - GitHub Repository

"},{"location":"services/map/","title":"Map","text":"

Interactive map service for geospatial data visualization, powered by NocoDB and Leaflet.js.

"},{"location":"services/map/#overview","title":"Overview","text":"

The Map service provides an interactive web-based map for displaying, searching, and analyzing geospatial data from a NocoDB backend. It supports real-time geolocation, adding new locations, and is optimized for both desktop and mobile use.

"},{"location":"services/map/#features","title":"Features","text":""},{"location":"services/map/#access","title":"Access","text":""},{"location":"services/map/#configuration","title":"Configuration","text":"

All configuration is done via environment variables:

Variable Description Default NOCODB_API_URL NocoDB API base URL Required NOCODB_API_TOKEN API authentication token Required NOCODB_VIEW_URL Full NocoDB view URL Required PORT Server port 3000 DEFAULT_LAT Default map latitude 53.5461 DEFAULT_LNG Default map longitude -113.4938 DEFAULT_ZOOM Default map zoom level 11"},{"location":"services/map/#volumes","title":"Volumes","text":""},{"location":"services/map/#usage","title":"Usage","text":"
  1. Access the map at http://localhost:${MAP_PORT:-3000}
  2. Search for locations or addresses
  3. Add or view custom markers
  4. Analyze geospatial data as needed
"},{"location":"services/map/#nocodb-table-setup","title":"NocoDB Table Setup","text":""},{"location":"services/map/#required-columns","title":"Required Columns","text":""},{"location":"services/map/#form-fields-as-seen-in-the-interface","title":"Form Fields (as seen in the interface)","text":""},{"location":"services/map/#api-endpoints","title":"API Endpoints","text":""},{"location":"services/map/#security-considerations","title":"Security Considerations","text":""},{"location":"services/map/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/mini-qr/","title":"Mini QR","text":"

Simple QR code generator service.

"},{"location":"services/mini-qr/#overview","title":"Overview","text":"

Mini QR is a lightweight service for generating QR codes for URLs, text, or other data. It provides a web interface for quick QR code creation and download.

"},{"location":"services/mini-qr/#features","title":"Features","text":""},{"location":"services/mini-qr/#access","title":"Access","text":""},{"location":"services/mini-qr/#configuration","title":"Configuration","text":""},{"location":"services/mini-qr/#environment-variables","title":"Environment Variables","text":""},{"location":"services/mini-qr/#volumes","title":"Volumes","text":""},{"location":"services/mini-qr/#usage","title":"Usage","text":"
  1. Access Mini QR at http://localhost:${MINI_QR_PORT:-8089}
  2. Enter the text or URL to encode
  3. Download or share the generated QR code
"},{"location":"services/mkdocs/","title":"MkDocs Material","text":"

Modern documentation site generator with live preview.

Looking for more info on BNKops code-server integration?

\u2192 Code Server Configuration

"},{"location":"services/mkdocs/#overview","title":"Overview","text":"

MkDocs Material is a powerful documentation framework built on top of MkDocs, providing a beautiful Material Design theme and advanced features for creating professional documentation sites.

"},{"location":"services/mkdocs/#features","title":"Features","text":""},{"location":"services/mkdocs/#access","title":"Access","text":""},{"location":"services/mkdocs/#configuration","title":"Configuration","text":""},{"location":"services/mkdocs/#main-configuration","title":"Main Configuration","text":"

Configuration is managed through mkdocs.yml in the project root.

"},{"location":"services/mkdocs/#volumes","title":"Volumes","text":""},{"location":"services/mkdocs/#environment-variables","title":"Environment Variables","text":""},{"location":"services/mkdocs/#directory-structure","title":"Directory Structure","text":"
mkdocs/\n\u251c\u2500\u2500 mkdocs.yml          # Configuration file\n\u251c\u2500\u2500 docs/               # Documentation source\n\u2502   \u251c\u2500\u2500 index.md       # Homepage\n\u2502   \u251c\u2500\u2500 services/      # Service documentation\n\u2502   \u251c\u2500\u2500 blog/          # Blog posts\n\u2502   \u2514\u2500\u2500 overrides/     # Template overrides\n\u2514\u2500\u2500 site/              # Built static site\n
"},{"location":"services/mkdocs/#writing-documentation","title":"Writing Documentation","text":""},{"location":"services/mkdocs/#markdown-basics","title":"Markdown Basics","text":""},{"location":"services/mkdocs/#example-page","title":"Example Page","text":"
# Page Title\n\nThis is a sample documentation page.\n\n## Section\n\nContent goes here with **bold** and *italic* text.\n\n### Code Example\n\n```python\ndef hello_world():\n    print(\"Hello, World!\")\n

Note

This is an informational note.

## Building and Deployment\n\n### Development\n\nThe development server runs automatically with live reload.\n\n### Building Static Site\n\n```bash\ndocker exec mkdocs-changemaker mkdocs build\n

The built site will be available in the mkdocs/site/ directory.

"},{"location":"services/mkdocs/#customization","title":"Customization","text":""},{"location":"services/mkdocs/#themes-and-colors","title":"Themes and Colors","text":"

Customize appearance in mkdocs.yml:

theme:\n  name: material\n  palette:\n    primary: blue\n    accent: indigo\n
"},{"location":"services/mkdocs/#custom-css","title":"Custom CSS","text":"

Add custom styles in docs/stylesheets/extra.css.

"},{"location":"services/mkdocs/#official-documentation","title":"Official Documentation","text":"

For comprehensive MkDocs Material documentation: - MkDocs Material - MkDocs Documentation - Markdown Guide

"},{"location":"services/n8n/","title":"n8n","text":"

Workflow automation tool for connecting services and automating tasks.

"},{"location":"services/n8n/#overview","title":"Overview","text":"

n8n is a powerful workflow automation tool that allows you to connect various apps and services together. It provides a visual interface for creating automated workflows, making it easy to integrate different systems and automate repetitive tasks.

"},{"location":"services/n8n/#features","title":"Features","text":""},{"location":"services/n8n/#access","title":"Access","text":""},{"location":"services/n8n/#configuration","title":"Configuration","text":""},{"location":"services/n8n/#environment-variables","title":"Environment Variables","text":""},{"location":"services/n8n/#volumes","title":"Volumes","text":""},{"location":"services/n8n/#getting-started","title":"Getting Started","text":"
  1. Access n8n at http://localhost:5678
  2. Log in with your admin credentials
  3. Create your first workflow
  4. Add nodes for different services
  5. Configure connections between nodes
  6. Test and activate your workflow
"},{"location":"services/n8n/#common-use-cases","title":"Common Use Cases","text":""},{"location":"services/n8n/#documentation-automation","title":"Documentation Automation","text":""},{"location":"services/n8n/#email-campaign-integration","title":"Email Campaign Integration","text":""},{"location":"services/n8n/#database-management-with-nocodb","title":"Database Management with NocoDB","text":""},{"location":"services/n8n/#development-workflows","title":"Development Workflows","text":""},{"location":"services/n8n/#data-processing","title":"Data Processing","text":""},{"location":"services/n8n/#example-workflows","title":"Example Workflows","text":""},{"location":"services/n8n/#simple-webhook-to-email","title":"Simple Webhook to Email","text":"
Webhook \u2192 Email\n
"},{"location":"services/n8n/#scheduled-documentation-backup","title":"Scheduled Documentation Backup","text":"
Schedule \u2192 Read Files \u2192 Compress \u2192 Upload to Storage\n
"},{"location":"services/n8n/#git-integration","title":"Git Integration","text":"
Git Webhook \u2192 Process Changes \u2192 Update Documentation \u2192 Notify Team\n
"},{"location":"services/n8n/#security-considerations","title":"Security Considerations","text":""},{"location":"services/n8n/#integration-with-other-services","title":"Integration with Other Services","text":"

n8n can integrate with all services in your Changemaker Lite setup:

"},{"location":"services/n8n/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/n8n/#common-issues","title":"Common Issues","text":""},{"location":"services/n8n/#debugging","title":"Debugging","text":"
# Check container logs\ndocker logs n8n-changemaker\n\n# Access container shell\ndocker exec -it n8n-changemaker sh\n\n# Check workflow executions in the UI\n# Visit http://localhost:5678 \u2192 Executions\n
"},{"location":"services/n8n/#official-documentation","title":"Official Documentation","text":"

For comprehensive n8n documentation:

"},{"location":"services/nocodb/","title":"NocoDB","text":"

No-code database platform that turns any database into a smart spreadsheet.

"},{"location":"services/nocodb/#overview","title":"Overview","text":"

NocoDB is an open-source no-code platform that transforms any database into a smart spreadsheet interface. It provides a user-friendly way to manage data, create forms, build APIs, and collaborate on database operations without requiring extensive technical knowledge.

"},{"location":"services/nocodb/#features","title":"Features","text":""},{"location":"services/nocodb/#access","title":"Access","text":""},{"location":"services/nocodb/#configuration","title":"Configuration","text":""},{"location":"services/nocodb/#environment-variables","title":"Environment Variables","text":""},{"location":"services/nocodb/#database-backend","title":"Database Backend","text":"

NocoDB uses a dedicated PostgreSQL instance (root_db) with the following configuration:

"},{"location":"services/nocodb/#volumes","title":"Volumes","text":""},{"location":"services/nocodb/#getting-started","title":"Getting Started","text":"
  1. Access NocoDB: Navigate to http://localhost:8090
  2. Initial Setup: Complete the onboarding process
  3. Create Project: Start with a new project or connect existing databases
  4. Add Tables: Import data or create new tables
  5. Configure Views: Set up different views (Grid, Form, Gallery, etc.)
  6. Set Permissions: Configure user access and sharing settings
"},{"location":"services/nocodb/#common-use-cases","title":"Common Use Cases","text":""},{"location":"services/nocodb/#content-management","title":"Content Management","text":""},{"location":"services/nocodb/#project-management","title":"Project Management","text":""},{"location":"services/nocodb/#data-collection","title":"Data Collection","text":""},{"location":"services/nocodb/#integration-with-other-services","title":"Integration with Other Services","text":"

NocoDB can integrate well with other Changemaker Lite services:

"},{"location":"services/nocodb/#api-usage","title":"API Usage","text":"

NocoDB automatically generates REST APIs for all your tables:

# Get all records from a table\nGET http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}\n\n# Create a new record\nPOST http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}\n\n# Update a record\nPATCH http://localhost:8090/api/v1/db/data/v1/{project}/table/{table}/{id}\n
"},{"location":"services/nocodb/#backup-and-data-management","title":"Backup and Data Management","text":""},{"location":"services/nocodb/#database-backup","title":"Database Backup","text":"

Since NocoDB uses PostgreSQL, you can backup the database:

# Backup NocoDB database\ndocker exec root_db pg_dump -U postgres root_db > nocodb_backup.sql\n\n# Restore from backup\ndocker exec -i root_db psql -U postgres root_db < nocodb_backup.sql\n
"},{"location":"services/nocodb/#application-data","title":"Application Data","text":"

Application settings and metadata are stored in the nc_data volume.

"},{"location":"services/nocodb/#security-considerations","title":"Security Considerations","text":""},{"location":"services/nocodb/#performance-tips","title":"Performance Tips","text":""},{"location":"services/nocodb/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/nocodb/#common-issues","title":"Common Issues","text":"

Service won't start: Check if the PostgreSQL database is healthy

docker logs root_db\n

Database connection errors: Verify database credentials and network connectivity

docker exec nocodb nc_data nc\n

Performance issues: Monitor resource usage and optimize queries

docker stats nocodb root_db\n
"},{"location":"services/nocodb/#official-documentation","title":"Official Documentation","text":"

For comprehensive guides and advanced features:

"},{"location":"services/postgresql/","title":"PostgreSQL Database","text":"

Reliable database backend for applications.

"},{"location":"services/postgresql/#overview","title":"Overview","text":"

PostgreSQL is a powerful, open-source relational database system. In Changemaker Lite, it serves as the backend database for Listmonk and can be used by other applications requiring persistent data storage.

"},{"location":"services/postgresql/#features","title":"Features","text":""},{"location":"services/postgresql/#access","title":"Access","text":""},{"location":"services/postgresql/#configuration","title":"Configuration","text":""},{"location":"services/postgresql/#environment-variables","title":"Environment Variables","text":""},{"location":"services/postgresql/#health-checks","title":"Health Checks","text":"

The PostgreSQL container includes health checks to ensure the database is ready before dependent services start.

"},{"location":"services/postgresql/#data-persistence","title":"Data Persistence","text":"

Database data is stored in a Docker volume (listmonk-data) to ensure persistence across container restarts.

"},{"location":"services/postgresql/#connecting-to-the-database","title":"Connecting to the Database","text":""},{"location":"services/postgresql/#from-host-machine","title":"From Host Machine","text":"

You can connect to PostgreSQL from your host machine using:

psql -h localhost -p 5432 -U [username] -d [database]\n
"},{"location":"services/postgresql/#from-other-containers","title":"From Other Containers","text":"

Other containers can connect using the internal hostname listmonk-db on port 5432.

"},{"location":"services/postgresql/#backup-and-restore","title":"Backup and Restore","text":""},{"location":"services/postgresql/#backup","title":"Backup","text":"
docker exec listmonk-db pg_dump -U [username] [database] > backup.sql\n
"},{"location":"services/postgresql/#restore","title":"Restore","text":"
docker exec -i listmonk-db psql -U [username] [database] < backup.sql\n
"},{"location":"services/postgresql/#monitoring","title":"Monitoring","text":"

Monitor database health and performance through: - Container logs: docker logs listmonk-db - Database metrics and queries - Connection monitoring

"},{"location":"services/postgresql/#security-considerations","title":"Security Considerations","text":""},{"location":"services/postgresql/#official-documentation","title":"Official Documentation","text":"

For comprehensive PostgreSQL documentation: - PostgreSQL Documentation - Docker PostgreSQL Image

"},{"location":"services/static-server/","title":"Static Site Server","text":"

Nginx-powered static site server for hosting built documentation and websites.

"},{"location":"services/static-server/#overview","title":"Overview","text":"

The Static Site Server uses Nginx to serve your built documentation and static websites. It's configured to serve the built MkDocs site and other static content with high performance and reliability.

"},{"location":"services/static-server/#features","title":"Features","text":""},{"location":"services/static-server/#access","title":"Access","text":""},{"location":"services/static-server/#configuration","title":"Configuration","text":""},{"location":"services/static-server/#environment-variables","title":"Environment Variables","text":""},{"location":"services/static-server/#volumes","title":"Volumes","text":""},{"location":"services/static-server/#usage","title":"Usage","text":"
  1. Build your MkDocs site: docker exec mkdocs-changemaker mkdocs build
  2. The built site is automatically available at http://localhost:4001
  3. Any files in ./mkdocs/site/ will be served statically
"},{"location":"services/static-server/#file-structure","title":"File Structure","text":"
mkdocs/site/           # Served at /\n\u251c\u2500\u2500 index.html         # Homepage\n\u251c\u2500\u2500 assets/           # CSS, JS, images\n\u251c\u2500\u2500 services/         # Service documentation\n\u2514\u2500\u2500 search/           # Search functionality\n
"},{"location":"services/static-server/#performance-features","title":"Performance Features","text":""},{"location":"services/static-server/#custom-configuration","title":"Custom Configuration","text":"

For advanced Nginx configuration, you can: 1. Create custom Nginx config files 2. Mount them as volumes 3. Restart the container

"},{"location":"services/static-server/#monitoring","title":"Monitoring","text":"

Monitor the static site server through: - Container logs: docker logs mkdocs-site-server-changemaker - Access logs for traffic analysis - Performance metrics

"},{"location":"services/static-server/#troubleshooting","title":"Troubleshooting","text":""},{"location":"services/static-server/#common-issues","title":"Common Issues","text":""},{"location":"services/static-server/#debugging","title":"Debugging","text":"
# Check container logs\ndocker logs mkdocs-site-server-changemaker\n\n# Verify files are present\ndocker exec mkdocs-site-server-changemaker ls -la /config/www\n\n# Test file serving\ncurl -I http://localhost:4001\n
"},{"location":"services/static-server/#official-documentation","title":"Official Documentation","text":"

For more information about the underlying Nginx server: - LinuxServer.io Nginx - Nginx Documentation

"},{"location":"blog/archive/2025/","title":"2025","text":""}]} \ No newline at end of file diff --git a/mkdocs/site/services/code-server/index.html b/mkdocs/site/services/code-server/index.html index 9853cda..e74cb21 100644 --- a/mkdocs/site/services/code-server/index.html +++ b/mkdocs/site/services/code-server/index.html @@ -1876,12 +1876,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/gitea/index.html b/mkdocs/site/services/gitea/index.html index 839ef78..0a05e1a 100644 --- a/mkdocs/site/services/gitea/index.html +++ b/mkdocs/site/services/gitea/index.html @@ -1854,12 +1854,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/homepage/index.html b/mkdocs/site/services/homepage/index.html index 483565a..1b3e07a 100644 --- a/mkdocs/site/services/homepage/index.html +++ b/mkdocs/site/services/homepage/index.html @@ -2320,12 +2320,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/index.html b/mkdocs/site/services/index.html index dd5ec43..0092371 100644 --- a/mkdocs/site/services/index.html +++ b/mkdocs/site/services/index.html @@ -1848,12 +1848,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/listmonk/index.html b/mkdocs/site/services/listmonk/index.html index d8061c1..62798ff 100644 --- a/mkdocs/site/services/listmonk/index.html +++ b/mkdocs/site/services/listmonk/index.html @@ -1900,12 +1900,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/map/index.html b/mkdocs/site/services/map/index.html index 1e1167a..9f3d0c1 100644 --- a/mkdocs/site/services/map/index.html +++ b/mkdocs/site/services/map/index.html @@ -2012,12 +2012,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/mini-qr/index.html b/mkdocs/site/services/mini-qr/index.html index 6c334e3..7ba9ec5 100644 --- a/mkdocs/site/services/mini-qr/index.html +++ b/mkdocs/site/services/mini-qr/index.html @@ -1820,12 +1820,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/mkdocs/index.html b/mkdocs/site/services/mkdocs/index.html index 5492787..eb371ed 100644 --- a/mkdocs/site/services/mkdocs/index.html +++ b/mkdocs/site/services/mkdocs/index.html @@ -2058,12 +2058,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/n8n/index.html b/mkdocs/site/services/n8n/index.html index 32bc304..dc6e55c 100644 --- a/mkdocs/site/services/n8n/index.html +++ b/mkdocs/site/services/n8n/index.html @@ -2246,12 +2246,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/nocodb/index.html b/mkdocs/site/services/nocodb/index.html index cc09e19..508b1ac 100644 --- a/mkdocs/site/services/nocodb/index.html +++ b/mkdocs/site/services/nocodb/index.html @@ -2225,12 +2225,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/postgresql/index.html b/mkdocs/site/services/postgresql/index.html index e877d7b..20dcc92 100644 --- a/mkdocs/site/services/postgresql/index.html +++ b/mkdocs/site/services/postgresql/index.html @@ -2036,12 +2036,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/services/static-server/index.html b/mkdocs/site/services/static-server/index.html index 9e41d60..0b33c4d 100644 --- a/mkdocs/site/services/static-server/index.html +++ b/mkdocs/site/services/static-server/index.html @@ -2025,12 +2025,10 @@ Changemaker Archive. Learn more
-
- - + diff --git a/mkdocs/site/sitemap.xml b/mkdocs/site/sitemap.xml index 881622b..e9b2836 100644 --- a/mkdocs/site/sitemap.xml +++ b/mkdocs/site/sitemap.xml @@ -2,142 +2,142 @@ https://cmlite.org/ - 2025-07-20 + 2025-07-27 https://cmlite.org/test/ - 2025-07-20 + 2025-07-27 https://cmlite.org/adv/ - 2025-07-20 + 2025-07-27 https://cmlite.org/adv/ansible/ - 2025-07-20 + 2025-07-27 https://cmlite.org/adv/vscode-ssh/ - 2025-07-20 + 2025-07-27 https://cmlite.org/blog/ - 2025-07-20 + 2025-07-27 https://cmlite.org/blog/2025/07/03/blog-1/ - 2025-07-20 + 2025-07-27 https://cmlite.org/blog/2025/07/10/2/ - 2025-07-20 + 2025-07-27 https://cmlite.org/build/ - 2025-07-20 + 2025-07-27 https://cmlite.org/build/map/ - 2025-07-20 + 2025-07-27 https://cmlite.org/build/server/ - 2025-07-20 + 2025-07-27 https://cmlite.org/build/site/ - 2025-07-20 + 2025-07-27 https://cmlite.org/config/ - 2025-07-20 + 2025-07-27 https://cmlite.org/config/cloudflare-config/ - 2025-07-20 + 2025-07-27 https://cmlite.org/config/coder/ - 2025-07-20 + 2025-07-27 https://cmlite.org/config/map/ - 2025-07-20 + 2025-07-27 https://cmlite.org/config/mkdocs/ - 2025-07-20 + 2025-07-27 https://cmlite.org/how%20to/canvass/ - 2025-07-20 + 2025-07-27 https://cmlite.org/manual/ - 2025-07-20 + 2025-07-27 https://cmlite.org/manual/map/ - 2025-07-20 + 2025-07-27 https://cmlite.org/phil/ - 2025-07-20 + 2025-07-27 https://cmlite.org/phil/cost-comparison/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/code-server/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/gitea/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/homepage/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/listmonk/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/map/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/mini-qr/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/mkdocs/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/n8n/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/nocodb/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/postgresql/ - 2025-07-20 + 2025-07-27 https://cmlite.org/services/static-server/ - 2025-07-20 + 2025-07-27 https://cmlite.org/blog/archive/2025/ - 2025-07-20 + 2025-07-27 \ No newline at end of file diff --git a/mkdocs/site/sitemap.xml.gz b/mkdocs/site/sitemap.xml.gz index 1157841..28ad4a5 100644 Binary files a/mkdocs/site/sitemap.xml.gz and b/mkdocs/site/sitemap.xml.gz differ diff --git a/mkdocs/site/test/index.html b/mkdocs/site/test/index.html index 8525d53..c5effea 100644 --- a/mkdocs/site/test/index.html +++ b/mkdocs/site/test/index.html @@ -728,12 +728,10 @@ Changemaker Archive. Learn more
-
- - +