26 KiB
Frequently Asked Questions (FAQ)
Comprehensive answers to common questions about Changemaker Lite V2.
General Questions
What is Changemaker Lite?
Changemaker Lite is a self-hosted political campaign platform that consolidates:
- Advocacy email campaigns - Contact elected representatives
- Geographic mapping - Location management and visualization
- Canvassing system - Door-to-door volunteer coordination
- Volunteer management - Shift scheduling and tracking
- Landing pages - Custom campaign pages with GrapesJS editor
- Newsletter platform - Listmonk integration for marketing emails
- Media library - Video management and public galleries
- Admin dashboard - Comprehensive management interface
Key features:
- 100% self-hosted (no external services required except email)
- Docker Compose deployment (single command to start)
- Full TypeScript stack (type-safe development)
- Production-ready security (JWT auth, bcrypt passwords, rate limiting)
- Monitoring included (Prometheus + Grafana)
- Canadian electoral data support (NAR format)
V1 vs V2 Differences
| Aspect | V1 | V2 |
|---|---|---|
| Architecture | Two separate Node apps (Influence + Map) | Single unified Express API |
| Database | NocoDB REST API | PostgreSQL 16 + Prisma ORM |
| Authentication | Sessions (express-session) | JWT (access + refresh tokens) |
| Frontend | EJS templates | React + Vite + Ant Design |
| State | Server-side | Zustand (client-side) |
| Bull queues | BullMQ queues | |
| Monitoring | Basic logging | Prometheus + Grafana + Alertmanager |
| Security | Basic | Production-grade (audit completed) |
| Status | Legacy (reference only) | Current (active development) |
Migration path: V1 → V2 requires data export/import. See Migration Guide.
System Requirements
Minimum (Development):
- CPU: 2 cores
- RAM: 4GB
- Disk: 10GB
- OS: Linux, macOS, or Windows with WSL2
- Docker: 20.10+ and Docker Compose v2+
Recommended (Production):
- CPU: 4+ cores
- RAM: 8-16GB
- Disk: 50GB+ SSD
- OS: Ubuntu 22.04 LTS or similar
- Docker: Latest stable version
External services (optional):
- SMTP server (for emails) - can use Gmail, SendGrid, Mailgun, etc.
- Pangolin/Cloudflare tunnel (for HTTPS) - or use your own reverse proxy
Browser Compatibility
Supported browsers:
- ✅ Chrome 90+ (recommended)
- ✅ Firefox 88+
- ✅ Safari 14+
- ✅ Edge 90+
- ❌ Internet Explorer (not supported)
Mobile browsers:
- ✅ Chrome on Android
- ✅ Safari on iOS
- ⚠️ Some features desktop-only (GrapesJS editor, map drawing)
Required features:
- JavaScript enabled
- Local Storage enabled
- Cookies enabled (for Listmonk only)
- WebSockets supported (for real-time features)
Installation & Setup
How to Install?
Quick start:
# 1. Clone repository
git clone <repo-url> changemaker.lite
cd changemaker.lite
git checkout v2
# 2. Create environment file
cp .env.example .env
nano .env # Edit and set passwords/secrets
# 3. Start services
docker compose up -d v2-postgres redis api admin
# 4. Run migrations
docker compose exec api npx prisma migrate deploy
docker compose exec api npx prisma db seed
# 5. Access application
# Admin GUI: http://localhost:3000
# API: http://localhost:4000
# Login: admin@example.com / Admin123!
# 6. Change default password immediately
See Installation Guide for detailed instructions.
Default Credentials
Admin user (created by seed):
- Email: admin@example.com
- Password: Admin123!
- Role: SUPER_ADMIN
⚠️ IMPORTANT: Change this password immediately after first login!
Other services:
- Grafana: admin / admin
- NocoDB: Set via NC_ADMIN_EMAIL / NC_ADMIN_PASSWORD in .env
- Listmonk: Set via LISTMONK_WEB_ADMIN_USER / LISTMONK_WEB_ADMIN_PASSWORD in .env
How to Change Password?
Via Admin UI (recommended):
- Login to admin at http://localhost:3000
- Navigate to Users (/app/users)
- Click user row
- Click Edit
- Enter new password (12+ chars, uppercase, lowercase, digit)
- Save
Via database:
# Generate bcrypt hash
docker compose exec api node -e "
const bcrypt = require('bcryptjs');
console.log(bcrypt.hashSync('NewPassword123!', 10));
"
# Update password
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
-c "UPDATE \"User\" SET password = 'PASTE_HASH_HERE' WHERE email = 'admin@example.com';"
Password requirements:
- Minimum 12 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 digit
- No maximum length
How to Enable HTTPS?
Changemaker Lite doesn't include HTTPS natively. Use one of these options:
Option 1: Pangolin Tunnel (Recommended)
Built-in integration:
- Navigate to
/app/pangolin - Follow setup wizard
- Configure tunnel
- Access via HTTPS URL provided by Pangolin
See Pangolin Integration.
Option 2: Cloudflare Tunnel
- Install cloudflared
- Configure tunnel
- Point to localhost:3000 (admin) and localhost:4000 (API)
Option 3: Reverse Proxy
Add nginx/Caddy in front:
# docker-compose.yml
reverse-proxy:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx/ssl.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl # Your SSL certificates
Option 4: Hosting Provider
Deploy to provider with built-in HTTPS:
- DigitalOcean App Platform
- Heroku
- Railway
- Render
User Management
How to Create Users?
Via Admin UI (recommended):
- Navigate to
/app/users - Click "Create User"
- Fill in form:
- Email (required, unique)
- Password (required, 12+ chars)
- Name (required)
- Role (default: USER)
- Click "Create"
Via API:
curl -X POST http://localhost:4000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@example.com",
"password": "SecurePass123!",
"name": "New User"
}'
Via database:
# Generate password hash first
docker compose exec api node -e "
const bcrypt = require('bcryptjs');
console.log(bcrypt.hashSync('Password123!', 10));
"
# Create user
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
-c "INSERT INTO \"User\" (id, email, password, name, role)
VALUES (gen_random_uuid(), 'user@example.com', 'HASH_HERE', 'User Name', 'USER');"
How to Reset Passwords?
Current: V2 doesn't have password reset flow yet (planned for Phase 15).
Workaround: Reset manually via database (see "How to Change Password?" above).
Future: Will include:
- Forgot password form
- Email with reset link
- 24-hour expiration
- One-time use tokens
What are the User Roles?
| Role | Level | Capabilities |
|---|---|---|
| SUPER_ADMIN | 5 | Full access to everything (users, settings, all features) |
| INFLUENCE_ADMIN | 4 | Manage campaigns, responses, email queue |
| MAP_ADMIN | 3 | Manage locations, cuts, shifts, canvassing |
| USER | 2 | View public content, participate in canvassing (if assigned) |
| TEMP | 1 | Very limited - shift signup confirmation only |
Permission matrix:
| Feature | SUPER_ADMIN | INFLUENCE_ADMIN | MAP_ADMIN | USER | TEMP |
|---|---|---|---|---|---|
| User management | ✅ | ❌ | ❌ | ❌ | ❌ |
| Site settings | ✅ | ❌ | ❌ | ❌ | ❌ |
| Campaigns (admin) | ✅ | ✅ | ❌ | ❌ | ❌ |
| Responses (moderation) | ✅ | ✅ | ❌ | ❌ | ❌ |
| Email queue | ✅ | ✅ | ❌ | ❌ | ❌ |
| Locations (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
| Cuts (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
| Shifts (admin) | ✅ | ❌ | ✅ | ❌ | ❌ |
| Canvass dashboard | ✅ | ❌ | ✅ | ❌ | ❌ |
| View public campaigns | ✅ | ✅ | ✅ | ✅ | ❌ |
| View public map | ✅ | ✅ | ✅ | ✅ | ❌ |
| Sign up for shifts | ✅ | ✅ | ✅ | ✅ | ✅ |
| Canvass (volunteer) | ✅ | ✅ | ✅ | ✅ | ❌ |
Login redirects:
- SUPER_ADMIN / INFLUENCE_ADMIN / MAP_ADMIN →
/app(admin dashboard) - USER / TEMP →
/volunteer(volunteer portal)
How to Suspend Users?
Current: V2 doesn't have user suspension yet (planned for Phase 15).
Workaround: Delete user account or change role to TEMP (limited permissions).
Future: Will include:
- Suspended flag on User model
- Suspension reason tracking
- Auto-logout suspended users
- Reactivation workflow
Campaigns
How to Create Campaign?
- Navigate to
/app/influence/campaigns - Click "Create Campaign"
- Fill in form:
- Name (required) - Campaign title
- Slug (required, unique) - URL-friendly name
- Description (optional) - Campaign details
- Email Subject (optional) - Default email subject
- Email Body (optional) - Default email template
- Active (checkbox) - Show on public site
- Allow Custom Message (checkbox) - Let users edit message
- Click "Create"
- Campaign now appears in admin table and public listing (if active)
How to Publish Campaign?
- Navigate to
/app/influence/campaigns - Find campaign in table
- Click row to expand
- Toggle "Active" switch to ON
- Campaign now visible at
/campaigns(public)
How to Track Emails?
- Navigate to
/app/influence/campaigns - Click campaign row
- Click "View Emails" button
- Drawer shows:
- Total emails sent
- Email list with timestamps
- Recipient addresses
- Email status (sent/failed)
Via Email Queue Page:
- Navigate to
/app/influence/email-queue - View stats:
- Total emails processed
- Success/fail counts
- Queue depth
- View recent jobs
- Retry failed jobs if needed
How to Moderate Responses?
- Navigate to
/app/influence/responses - Table shows all responses with:
- Participant name/email
- Campaign
- Message excerpt
- Submission timestamp
- Verification status
- Filters:
- Campaign dropdown
- Verified/unverified toggle
- Click row to view full response
- Actions:
- Verify (if unverified)
- Delete (if inappropriate)
Response verification workflow:
- User submits response → marked unverified
- User receives verification email
- User clicks verification link → marked verified
- Only verified responses show on public response wall
Map & Canvassing
How to Import Locations?
Via CSV:
- Navigate to
/app/map/locations - Click "Import CSV"
- Prepare CSV with columns:
address,city,province,postalCode,notes 123 Main St,Toronto,ON,M5H 2N2,Corner house 456 Oak Ave,Toronto,ON,M5H 2N3,Blue door - Upload file
- Map columns (if headers don't match exactly)
- Click "Import"
- Locations imported, geocoding starts automatically
Via NAR (Canadian Electoral Data):
- Obtain NAR data files (Location + Address)
- Place in
/datadirectory (mapped volume) - Navigate to
/app/map/locations - Click "NAR Import" tab
- Select province
- Select dataset
- Apply filters (city, postal code, cut, residential only)
- Preview count
- Click "Import"
- Import processes in background (can take minutes for large files)
See NAR Import Guide.
How to Create Cuts?
Via Map Drawing:
- Navigate to
/app/map/cuts - Click "Map Drawing" tab
- Map shows with drawing controls
- Click "Draw Cut" button
- Click on map to place vertices
- Click first vertex again to close polygon (or click "Finish")
- Fill in form:
- Name (required)
- Description (optional)
- Color (pick color for map display)
- Click "Save"
Via GeoJSON Import:
- Prepare GeoJSON file:
{ "type": "Polygon", "coordinates": [[ [-79.38, 43.65], [-79.37, 43.65], [-79.37, 43.64], [-79.38, 43.64], [-79.38, 43.65] ]] } - Navigate to
/app/map/cuts - Click "Create Cut"
- Paste GeoJSON in geometry field
- Fill in name/description
- Click "Create"
How to Organize Shifts?
- Navigate to
/app/map/shifts - Click "Create Shift"
- Fill in form:
- Title (required) - Shift name
- Description (optional) - Shift details
- Start Time (required) - When shift starts
- End Time (required) - When shift ends
- Cut (optional) - Assign to specific cut
- Max Volunteers (optional) - Capacity limit
- Public (checkbox) - Show on public shifts page
- Click "Create"
- Shift appears in admin table and public listing (if public)
Manage signups:
- Click shift row in table
- Click "View Signups"
- Drawer shows:
- Signup count
- List of volunteers
- Email addresses
- Actions:
- "Email All" - Send message to all volunteers
- Remove individual signups if needed
How to Start Canvassing?
For volunteers:
- Login to volunteer portal
- Navigate to "My Assignments" (/volunteer/assignments)
- Find assigned shift
- Click "Start Canvassing"
- Full-screen map opens (/volunteer/canvass/:cutId)
- GPS tracks your location
- Map shows:
- Your current position (blue dot)
- Locations in cut (markers)
- Walking route (blue line)
- Legend (outcome colors)
- Click location marker to record visit:
- Select outcome (Home, Away, Refused, etc.)
- Add notes (optional)
- Save
- Continue until all locations visited
- Session auto-saves progress
For admins (monitoring):
- Navigate to
/app/canvass/dashboard - View:
- Active sessions count
- Total visits recorded
- Recent activity feed
- Cut progress (% complete)
- Leaderboard (top canvassers)
- Click activity item to see details
See Canvassing Guide.
Technical Questions
Which Database?
PostgreSQL 16 with two ORMs:
-
Prisma - Main API (Express)
- Schema:
api/prisma/schema.prisma - Migrations:
api/prisma/migrations/ - 30+ models (User, Campaign, Location, etc.)
- Schema:
-
Drizzle - Media API (Fastify)
- Schema:
api/src/modules/media/db/schema.ts - Tables:
media_videos,media_reactions, etc.
- Schema:
Connection:
- Host: v2-postgres (container) or localhost:5433 (host)
- Database: changemaker_v2
- User: changemaker
- Password: V2_POSTGRES_PASSWORD (from .env)
Shared database: Both ORMs use same PostgreSQL database, different tables.
Which ORM?
Prisma for main API:
- Type-safe queries
- Auto-generated client
- Migrations workflow
- Prisma Studio GUI
Drizzle for media API:
- Lightweight
- SQL-like API
- Schema-first approach
- No migration files (push to sync)
Why two ORMs?
Media API was added later as separate Fastify microservice. Using Drizzle allowed faster development without modifying main Prisma schema.
API Architecture?
Dual API architecture:
-
Express API (Main)
- Port: 4000
- Language: TypeScript
- ORM: Prisma
- Features: Auth, campaigns, locations, shifts, canvass, pages
- Endpoints:
/api/*
-
Fastify Media API (Microservice)
- Port: 4100
- Language: TypeScript
- ORM: Drizzle
- Features: Video library, uploads, reactions
- Endpoints:
/api/media/*
Shared:
- Same PostgreSQL database
- Same Redis instance
- Same Docker network
- Separate containerization (can scale independently)
Frontend:
- React SPA (Vite)
- Port: 3000
- State: Zustand
- UI: Ant Design
- Routing: React Router v6
Authentication Method?
JWT-based authentication:
Tokens:
-
Access Token
- Duration: 15 minutes
- Stored: Memory (localStorage)
- Contains: userId, email, role
- Used: All authenticated requests
-
Refresh Token
- Duration: 7 days
- Stored: Database + localStorage
- Used: Renew access token
- Rotation: New refresh token on each refresh
Flow:
- Login → Returns access + refresh tokens
- Store in localStorage (Zustand persist)
- Add access token to Authorization header
- Access token expires after 15min
- Frontend auto-refreshes using refresh token
- New access + refresh tokens returned
- Continue seamlessly
Security features:
- bcrypt password hashing (10 rounds)
- Token rotation prevents replay attacks
- Refresh tokens stored in database (can revoke)
- Rate limiting on auth endpoints (10/min)
- User enumeration prevention
- Redis authentication required
See Authentication Flow.
Performance
How Many Users Supported?
Concurrent users:
- Development: 10-50 users
- Production (default config): 100-500 users
- Production (optimized): 1000+ users
Factors:
- Database connection pool (default: 10 connections)
- API worker concurrency (default: 1 worker)
- Server resources (CPU/RAM)
- Network bandwidth
Scaling:
- Horizontal: Run multiple API instances
- Vertical: Increase server resources
- Database: Read replicas for read-heavy loads
- Caching: Redis caching for frequently accessed data
How to Scale?
Horizontal scaling (recommended):
# docker-compose.yml
api:
deploy:
replicas: 3 # Run 3 API instances
# Each instance:
# - Handles requests independently
# - Connects to same database
# - Processes queue jobs
# - Shares Redis cache
Add load balancer in front:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx/lb.conf:/etc/nginx/nginx.conf
# Distributes requests across API instances
Vertical scaling:
Increase resources:
api:
deploy:
resources:
limits:
cpus: '4.0' # More CPU
memory: 8G # More RAM
Database scaling:
- Add read replicas for read-heavy queries
- Use connection pooler (PgBouncer)
- Optimize queries and indexes
Caching:
- Redis caching for geocoding results
- Redis caching for representative lookups
- HTTP caching headers (Nginx)
- Static asset CDN
Database Size Limits?
PostgreSQL:
- Maximum database size: ~32 TB (theoretical)
- Practical limit: Depends on storage and backup strategy
Typical sizes (after 1 year):
- Small campaign: 100MB-500MB (1k locations, 10 campaigns)
- Medium campaign: 500MB-2GB (10k locations, 50 campaigns)
- Large campaign: 2GB-10GB (100k locations, 200 campaigns)
Storage requirements:
- Database: 1-10GB
- Uploads: 5-50GB (videos)
- Backups: 2× database size (keep multiple backups)
- Logs: 1-5GB/month
- Total: 20-100GB recommended
Optimization:
- Regular VACUUM (auto-enabled)
- Archive old campaigns
- Delete old logs
- Compress backups
Security
Is Data Encrypted?
At rest:
- Database: Not encrypted by default (enable PostgreSQL encryption if needed)
- Passwords: bcrypt hashed (cannot be decrypted)
- Sensitive fields: ENCRYPTION_KEY env var for encrypting secrets
In transit:
- HTTPS: Use Pangolin/Cloudflare tunnel (encrypts all traffic)
- Database: PostgreSQL connections within Docker network (isolated)
- Redis: Authenticated (password required)
Recommendations:
- Use HTTPS in production
- Rotate ENCRYPTION_KEY periodically
- Enable PostgreSQL SSL if database exposed
- Use strong passwords for all services
Password Requirements?
Enforced policy:
- Minimum 12 characters
- At least 1 uppercase letter (A-Z)
- At least 1 lowercase letter (a-z)
- At least 1 digit (0-9)
- No maximum length
Valid examples:
SecurePass123!MyPassword99Admin12345678
Invalid:
short(too short)nouppercase123(no uppercase)NOLOWERCASE123(no lowercase)NoDigitsHere(no digit)
Storage:
- bcrypt hashed with salt (10 rounds)
- Hash stored in database (not plaintext)
- Cannot be decrypted (one-way hash)
How to Backup?
Manual backup:
# Use provided script
./scripts/backup.sh
# Creates:
# - PostgreSQL dump
# - Listmonk dump (if enabled)
# - Uploads archive (videos, images)
# - Timestamped filename: backup_2026-02-13_100000.tar.gz
What's included:
- Complete database dump (all tables)
- Uploaded files (videos, images, documents)
- Listmonk database (if enabled)
What's NOT included:
- Docker images (rebuild from Dockerfile)
- .env file (keep separate, has secrets)
- Temporary files (logs, cache)
Automated backups:
Add cron job:
# Run daily at 2 AM
0 2 * * * cd /path/to/changemaker.lite && ./scripts/backup.sh
# With S3 upload (if configured)
0 2 * * * cd /path/to/changemaker.lite && ./scripts/backup.sh --upload-s3
Restore:
# Stop services
docker compose down
# Restore database
gunzip -c backup.sql.gz | docker compose exec -T v2-postgres psql -U changemaker -d changemaker_v2
# Restore uploads
tar -xzf uploads.tar.gz -C ./uploads
# Start services
docker compose up -d
See Backup Guide.
Troubleshooting
Where are Logs?
Docker logs:
# View API logs
docker compose logs api
# View all logs
docker compose logs
# Follow logs (real-time)
docker compose logs -f api
# Last 100 lines
docker compose logs api --tail=100
# Since timestamp
docker compose logs api --since="2026-02-13T10:00:00"
# Save to file
docker compose logs api > api-logs.txt
Log locations inside containers:
- API: Console output (docker logs)
- PostgreSQL: Console +
/var/lib/postgresql/data/log/(if logging enabled) - Nginx:
/var/log/nginx/access.log,/var/log/nginx/error.log
Log levels:
- ERROR: Errors requiring attention
- WARN: Warnings (not critical)
- INFO: Informational messages
- DEBUG: Debugging information (enable with LOG_LEVEL=debug)
How to Restart Services?
Restart specific service:
# Restart API
docker compose restart api
# Restart multiple services
docker compose restart api admin v2-postgres
Restart all services:
# Graceful restart (preserves data)
docker compose restart
# Stop and start (recreates containers)
docker compose down
docker compose up -d
Force recreate:
# Rebuild and recreate
docker compose up -d --build --force-recreate
# Recreate specific service
docker compose up -d --build --force-recreate api
Restart single container:
# Get container name
docker compose ps
# Restart by name
docker restart changemaker-lite-api-1
How to Reset Database?
⚠️ WARNING: This deletes ALL data!
Full reset:
# Stop services
docker compose down
# Delete database volume
docker volume rm changemaker-lite_postgres-data
# Start fresh
docker compose up -d v2-postgres
# Wait for database ready
sleep 10
# Run migrations
docker compose exec api npx prisma migrate deploy
# Seed initial data
docker compose exec api npx prisma db seed
# Default admin: admin@example.com / Admin123!
Reset specific tables:
# Delete all users (keeps schema)
docker compose exec v2-postgres psql -U changemaker -d changemaker_v2 \
-c 'TRUNCATE "User" CASCADE;'
# Re-seed
docker compose exec api npx prisma db seed
Reset without deleting volumes:
# Drop and recreate database
docker compose exec v2-postgres psql -U postgres \
-c 'DROP DATABASE changemaker_v2;'
docker compose exec v2-postgres psql -U postgres \
-c 'CREATE DATABASE changemaker_v2 OWNER changemaker;'
# Run migrations
docker compose exec api npx prisma migrate deploy
docker compose exec api npx prisma db seed
Getting Help
Documentation Links
User Guides:
Technical Documentation:
Troubleshooting:
- Common Errors
- Docker Issues
- Database Issues
- Auth Issues
- Email Issues
- Geocoding Issues
- Monitoring Issues
- Performance Optimization
GitHub Issues
Before creating issue:
- Check existing issues
- Search closed issues (may already be fixed)
- Check Troubleshooting guides
- Try latest version (
git pull origin v2)
Creating good issues:
Bug reports:
**Describe the bug**
Clear description of what's wrong.
**To Reproduce**
1. Go to '...'
2. Click on '...'
3. See error
**Expected behavior**
What should happen instead.
**Screenshots**
If applicable, add screenshots.
**Environment**
- OS: [e.g. Ubuntu 22.04]
- Docker version: [e.g. 20.10.21]
- Browser: [e.g. Chrome 120]
**Logs**
Paste relevant logs (sanitize sensitive data).
Feature requests:
**Is your feature request related to a problem?**
Description of problem.
**Describe the solution you'd like**
Clear description of feature.
**Describe alternatives you've considered**
Other solutions considered.
**Additional context**
Any other context or screenshots.
Community Support
Official channels:
- GitHub Issues (bugs and features)
- GitHub Discussions (questions and ideas)
- Documentation (this site)
Response time:
- Bug reports: 1-7 days
- Feature requests: Variable (depends on priority)
- Questions: 1-3 days
Contributing:
- Pull requests welcome
- Follow Contributing Guide
- Follow Code of Conduct
Last Updated: February 2026 Version: V2.0 Status: Complete