Enterprise-grade SaaS platform combining Jobs & Recruitment, Freelance Marketplace with Escrow, HR Management, and CRM functionality.
Zumodra is a comprehensive multi-tenant platform that combines:
- Jobs & Recruitment - Full hiring pipeline from job posting to offer
- Freelance Marketplace - Service listings with proposals and escrow payments
- HR Core - Employee management, time-off, onboarding, performance reviews
- Trust & Verification - KYC verification and trust scoring
- Real-time Messaging - WebSocket-powered chat system
- Co-op/Internship Management - Student, employer, and coordinator dashboards
# Clone repository
git clone https://github.com/rhematek/zumodra.git
cd zumodra
# Configure environment
cp .env.example .env
# Edit .env with your credentials (see "Database Configuration" below)
# Start all services
docker compose up -d
# The entrypoint automatically handles:
# - Waiting for database/redis/rabbitmq
# - Running migrations
# - Collecting static files
# Create superuser
docker compose exec web python manage.py createsuperuser
# (Optional) Create demo tenant with sample data
docker compose exec web python manage.py bootstrap_demo_tenant
# Access application
# Web: http://localhost:8002 (or http://localhost:8084 via nginx)
# API Docs: http://localhost:8002/api/docs/
# Admin: http://localhost:8002/admin-panel/# Configure for production
cp .env.example .env
# Edit .env with REAL production secrets (DEBUG=False, secure passwords, etc.)
# For production, use the production compose file if available
docker compose -f docker-compose.prod.yml up -d
# Or use the standard compose with production env vars
docker compose up -dThe entrypoint script and Django both use the same environment variables. Set these correctly and everything works.
| Variable | Description | Example |
|---|---|---|
DB_HOST |
PostgreSQL hostname | db (dev) / postgres-primary (prod) |
DB_PORT |
PostgreSQL port | 5432 |
DB_NAME |
Database name | zumodra |
DB_USER |
Database username | postgres |
DB_PASSWORD |
Database password | your-strong-password |
- Docker Compose passes these env vars to the
webcontainer - Entrypoint script logs them on startup for diagnosis:
[INFO] Database Configuration (from env): [INFO] DB_HOST = db [INFO] DB_PORT = 5432 [INFO] DB_NAME = zumodra [INFO] DB_USER = postgres [INFO] DB_PASSWORD = [SET] - Django reads the same vars in
settings.py
If you see this error:
- Check that
DB_PASSWORDin your.envmatchesPOSTGRES_PASSWORDin the Postgres container - Check that
DB_USERmatchesPOSTGRES_USER - Check the entrypoint logs to see what values are actually being used
| File | Purpose |
|---|---|
.env.example |
Environment template (copy to .env) |
docker/Dockerfile |
Docker build file |
docker/entrypoint.sh |
Container entrypoint script |
docker-compose.yml |
Main compose file |
docker-compose.prod.yml |
Production compose (optional) |
For testing and exploration, create a demo tenant with rich sample data:
# Manual creation
python manage.py bootstrap_demo_tenant
# Or enable auto-creation on Docker startup
CREATE_DEMO_TENANT=1 docker compose up -dDemo Login Credentials:
| Role | Password | |
|---|---|---|
| Admin | admin@demo.zumodra.local | Demo@2024! |
| HR Manager | hr@demo.zumodra.local | Demo@2024! |
| Recruiter | recruiter@demo.zumodra.local | Demo@2024! |
See TENANT_ONBOARDING.md for full demo tenant details.
# Install dependencies
pip install -r requirements.txt
# Configure environment
cp .env.example .env
# Run migrations
python manage.py migrate
# Start development server
python manage.py runserver
# Start Celery (separate terminals)
celery -A zumodra worker --loglevel=info
celery -A zumodra beat --loglevel=info| Module | Status | Description |
|---|---|---|
| ATS | Production | Complete recruitment: jobs (create/edit/duplicate), candidates, interviews (schedule/reschedule/cancel), offers, pipelines |
| Marketplace | Production | Services, proposals, contracts, escrow |
| HR Core | Production | Employees, time-off, onboarding |
| Finance | Production | Stripe payments, escrow, subscriptions |
| Messaging | Production | Real-time WebSocket chat |
| Notifications | Production | Multi-channel notifications |
| KYC/Verification | Beta | Identity and career verification |
| Trust Scores | Beta | Multi-dimensional trust scoring |
| Co-op Management | Beta | Student/employer/coordinator UIs |
| Multi-CV | Beta | CV management with AI scoring |
- Two-Factor Authentication (mandatory)
- JWT API authentication with token rotation
- Brute force protection (django-axes)
- Strict Content Security Policy - No external CDN dependencies
- Local-only assets - All CSS/JS/fonts/icons served from staticfiles
- Admin honeypot protection
- Comprehensive audit logging
- Rate limiting per user tier
- Input sanitization and XSS prevention
See docs/SECURITY.md for the complete security policy including CSP configuration.
Zumodra supports two distinct tenant types with different capabilities:
| Feature | COMPANY | FREELANCER |
|---|---|---|
| Create jobs (ATS) | ✅ Yes | ❌ No |
| Create services | ✅ Yes | ✅ Yes |
| Have employees (HR) | ✅ Yes | ❌ No (single-user only) |
| Career page | ✅ Yes | ❌ No |
| Publish to marketplace | ✅ Yes | ✅ Yes |
| Switch tenant type | ✅ Yes (if ≤1 member) | ✅ Yes (always) |
Key Concepts:
- Freelancers are tenants (not user states) - single-user organizations that provide services
- Tenants can switch types: Company ↔ Freelancer
- Hiring contexts: Users can hire services PERSONALLY or ON BEHALF of their tenant
- Verification levels:
- User: CV verification + KYC verification (global, not tenant-specific)
- Tenant: EIN/business number verification (via API)
Documentation:
- Tenant Type API Reference - Complete API guide
- Verification System - CV/KYC/EIN verification workflows
- UI Components - 8 reusable components for tenant types
- Multi-Tenancy Logic - Complete architecture documentation
- Python 3.11+
- Django 5.2.7 with GeoDjango
- Django REST Framework + SimpleJWT
- PostgreSQL 15 + PostGIS 3.4 (
postgis/postgis:15-3.4) - django-tenants for multi-schema SaaS isolation
- Redis 7 (cache, sessions, Celery)
- RabbitMQ 3.12 (Celery broker)
- Celery 5.x (async tasks)
- Django Channels (WebSockets)
- Django Templates with HTMX
- Alpine.js for interactivity (local, no CDN)
- Tailwind CSS (pre-compiled, local)
- Phosphor Icons / Icomoon (local icon fonts)
- Wagtail CMS
- Docker + Docker Compose
- Nginx reverse proxy
- Gunicorn + Uvicorn (ASGI)
- Prometheus + Grafana (monitoring)
The codebase is a large Django monorepo with ~40+ apps at the repository root (flat layout, not under apps/). The main functional groups are:
zumodra/
├── # Identity & tenants
├── accounts/ # User accounts, KYC, trust scores
├── accounting/ # Chart of accounts, ledger entries
├── core/ # Shared utilities + security middleware
├── core_identity/ # Identity & profile core
├── tenants/ # Multi-tenant (django-tenants) management
├── tenant_profiles/ # Per-tenant profile data
├── custom_account_u/ # Custom account extensions
├── admin_honeypot/ # Fake admin for attacker honeypot
├── security/ # Security controls, audit trail
│
├── # Jobs & ATS
├── jobs/ # Job postings (tenant-scoped)
├── jobs_public/ # Public careers pages
├── careers/ # Careers marketing
├── ats/ # Applicant Tracking System
├── interviews/ # Interview scheduling
│
├── # HR & payroll
├── hr_core/ # HR management, onboarding
├── payroll/ # Payroll processing
├── expenses/ # Expense reports
│
├── # Freelance marketplace
├── services/ # Freelance service listings (tenant-scoped)
├── services_public/ # Public marketplace
├── projects/ # Projects & milestones
├── projects_public/ # Public project listings
├── escrow/ # Escrow for project payments
│
├── # Finance, billing, payments
├── billing/ # Invoice + billing
├── finance/ # Payments, subscriptions
├── finance_webhooks/ # Payment webhook handlers
├── payments/ # Payment processing
├── stripe_connect/ # Stripe Connect marketplace payouts
├── subscriptions/ # SaaS subscription plans
├── tax/ # Tax calculation (VAT/GST/region)
│
├── # Communication & content
├── messages_sys/ # Real-time messaging
├── notifications/ # Notification system
├── blog/ # Blog / Wagtail CMS content
├── marketing/ # Marketing pages
├── marketing_campaigns/ # Campaign management
├── media/ # Media files
│
├── # AI, analytics, integrations
├── ai_matching/ # AI-powered candidate/job matching
├── agents/ # Agent workflows
├── analytics/ # Analytics & reporting
├── integrations/ # Third-party integrations
├── configurations/ # Runtime configuration
├── logs/ # Audit logs
│
├── # Infrastructure
├── api/ # REST API infrastructure
├── dashboard/ # Tenant dashboard
├── main/ # Main app (landing, shared views)
├── frontend/ # Frontend assets app
├── templates/ # Django templates (unified_base, auth, emails, errors)
├── staticfiles/ # Local static assets (Alpine, HTMX, Chart.js, fonts)
├── tests/ # Cross-app test suite
├── docker/ # Docker configurations
├── scripts/ # Operational scripts
├── docs_project/ # Documentation
└── zumodra/ # Django project settings (settings.py, urls.py, asgi.py, celery.py)
templates/
├── base/
│ ├── unified_base.html # Root base template (no CDN)
│ ├── base_auth.html # Auth pages base
│ ├── dashboard_base.html # Dashboard base
│ └── public_base.html # Public pages base
├── components/ # Reusable UI components
├── emails/ # HTML email templates
│ ├── base/base_email.html # Email base template
│ ├── auth/ # Auth emails
│ ├── ats/ # ATS notifications
│ └── marketplace/ # Marketplace emails
└── errors/ # Error pages (500, 503)
templates_auth/
├── account/ # Allauth templates
├── mfa/ # MFA/2FA templates
└── socialaccount/ # Social auth templates
staticfiles/
├── assets/
│ ├── js/vendor/ # Alpine.js, HTMX, Chart.js, SortableJS
│ ├── css/ # Icomoon icons, Leaflet styles
│ └── fonts/ # Local web fonts
└── dist/
└── output-tailwind.css # Pre-compiled Tailwind CSS
- Swagger UI: http://localhost:8002/api/docs/
- ReDoc: http://localhost:8002/api/redoc/
- OpenAPI Schema: http://localhost:8002/api/schema/
# Get JWT token
curl -X POST http://localhost:8002/api/token/ \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password"}'
# Use token
curl http://localhost:8002/api/v1/jobs/jobs/ \
-H "Authorization: Bearer <access_token>"See docs/API_DOCUMENTATION.md for complete API reference.
All API endpoints follow a consistent namespace pattern: api_v1:{app_name}:{endpoint_name}
Rules:
- Namespace matches Django app directory name exactly
- Use underscores for multi-word apps (e.g.,
messages_sys,hr_core) - No
-apior_apisuffixes - All under
api_v1:parent namespace
Examples:
api_v1:dashboard:overview(NOTapi_v1:dashboard-api:overview)api_v1:finance:payment-list(NOTapi_v1:finance_api:payment-list)api_v1:messages_sys:conversation-list(NOTapi_v1:messages_api:conversation-list)
Template References:
# In API reverse() calls
reverse('api_v1:finance:payment-list')
reverse('api_v1:messages_sys:conversation-detail', args=[uuid])Zumodra includes several custom management commands for common operations:
# Bootstrap a demo tenant with comprehensive sample data
python manage.py bootstrap_demo_tenant
python manage.py bootstrap_demo_tenant --reset # Delete and recreate
python manage.py bootstrap_demo_tenant --dry-run # Preview changes
# Create a beta tenant for early adopters
python manage.py setup_beta_tenant "Company Name" "owner@email.com"
python manage.py setup_beta_tenant "Acme Corp" "admin@acme.com" --plan beta_enterprise --trial-days 90
# Create a basic demo tenant with sample data
python manage.py setup_demo_data
python manage.py setup_demo_data --num-jobs 20 --num-candidates 100 --reset
# Create a standard tenant
python manage.py create_tenant
# Set up subscription plans
python manage.py setup_plans
# Clean up inactive tenants
python manage.py cleanup_inactive_tenants --days 90 --dry-run
# Migrate data between tenant schemas
python manage.py migrate_tenant_data# Check health of all services (database, cache, email, etc.)
python manage.py health_check
python manage.py health_check --full # Include external services
python manage.py health_check --json # Output as JSON
# Generate API documentation
python manage.py generate_api_docs
python manage.py generate_api_docs --format markdown
python manage.py generate_api_docs --format html --output docs/api# Run migrations on all schemas
python manage.py migrate_schemas
# Run migrations only on shared (public) schema
python manage.py migrate_schemas --shared
# Run migrations only on tenant schemas
python manage.py migrate_schemas --tenant# Run all tests
pytest
# Run with coverage
pytest --cov
# Run specific test file
pytest tests/test_ats_flows.py
# Run by marker
pytest -m workflow # End-to-end workflows
pytest -m security # Security tests
pytest -m integration # Integration tests
# Check deployment readiness
python manage.py check --deploy📖 Complete Documentation - Full documentation index
| Category | Documents |
|---|---|
| Getting Started | CONTRIBUTING.md - Contribution guidelines |
| CLAUDE.md - Claude Code instructions | |
| Deployment | Deployment Summary - Latest fixes & deployment |
| Migration Fix Guide - Migration troubleshooting | |
| Deployment Guide - General deployment | |
| Architecture | Multi-Tenancy Logic - Tenant architecture |
| Domain Model - Data model | |
| Features - Platform features | |
| Security | Security Policy - Security documentation |
| API | API Documentation - REST API reference |
| Operations | Tenant Onboarding - New tenant setup |
| QA Scenarios - Test scenarios | |
| Server Updates - Update procedures |
| Service | Internal Port | External Port | Description |
|---|---|---|---|
| web | 8000 | 8002 | Django application |
| channels | 8001 | 8003 | WebSocket server (Daphne) |
| nginx | 80 | 8084 | Reverse proxy |
| db | 5432 | 5434 | PostgreSQL + PostGIS |
| redis | 6379 | 6380 | Cache and sessions |
| rabbitmq | 5672 | 5673 | Message broker |
| celery-worker | - | - | Background tasks |
| celery-beat | - | - | Scheduled tasks |
| mailhog | 8025 | 8026 | Email testing (dev) |
| prometheus | 9090 | 9090 | Metrics (optional, --profile monitoring) |
| grafana | 3000 | 3001 | Dashboards (optional, --profile monitoring) |
Key configuration options (see .env.example for complete list):
# Django
SECRET_KEY=your-secret-key
DEBUG=False
ALLOWED_HOSTS=localhost,127.0.0.1
# Database
DB_DEFAULT_NAME=zumodra
DB_USER=postgres
DB_PASSWORD=your-password
DB_HOST=db
# Redis
REDIS_URL=redis://redis:6379/0
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_PUBLIC_KEY=pk_test_...
# Feature Flags
FEATURE_ENABLE_2FA=True
FEATURE_ENABLE_ESCROW=True
FEATURE_ENABLE_AI_MATCHING=FalseZumodra supports full multi-tenant architecture:
- Tenant Isolation - Each tenant has isolated data
- Custom Domains - Per-tenant domain support
- Role-Based Access - PDG, Supervisor, HR, Recruiter, Employee, Viewer
- Plan-Based Features - Feature flags per subscription tier
# Tenant roles
TENANT_ROLES = [
'pdg', # Full tenant access
'supervisor', # Circusale + subordinates
'hr_manager', # HR operations
'recruiter', # ATS access
'employee', # Self-service
'viewer', # Read-only
]- English (en)
- Spanish (es)
- French (fr)
- German (de)
- Italian (it)
- Portuguese (pt)
- Russian (ru)
- Simplified Chinese (zh-hans)
- Traditional Chinese (zh-hant)
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
pytest) - Commit (
git commit -m 'Add amazing feature') - Push (
git push origin feature/amazing-feature) - Open a Pull Request
- Documentation: https://docs.zumodra.com
- API Status: https://status.zumodra.com
- Support Email: support@zumodra.com
Proprietary - All Rights Reserved
Version: 1.0.0 Last Updated: January 2026