- Python 71.6%
- HTML 26.7%
- Shell 1.5%
- Mako 0.2%
| .config | ||
| alembic | ||
| deploy | ||
| src/chaskiadmin | ||
| tests | ||
| .gitignore | ||
| alembic.ini | ||
| CLAUDE.md | ||
| install.sh | ||
| pyproject.toml | ||
| README.md | ||
ChaskiAdmin
Multi-tenant middleware for Stalwart Mail Server. Adds tenant isolation, OIDC authentication, and domain-scoped administration without forking Stalwart or requiring an Enterprise license.
Architecture
Tenant Admin --> ChaskiAdmin (FastAPI) --> Stalwart Admin API
|
PostgreSQL Zitadel (sso.pop.coop)
ChaskiAdmin sits between tenant admins and Stalwart's management API. It maintains a tenant registry in PostgreSQL and filters all Stalwart API responses to only show resources belonging to the authenticated tenant.
Quick Start
# Clone and set up virtualenv
git clone https://git.pop.coop/pop/chaski-manager.git
cd chaski-manager
uv venv .venv
uv pip install -e ".[dev]"
# Configure
cp .config/settings.env.example .config/settings.env
# Edit .config/settings.env with your credentials
# Run migrations
.venv/bin/python -m alembic upgrade head
# Start the server
.venv/bin/python -m chaskiadmin.main
The admin UI is served at the root URL (default: https://admin.autonomia.lat).
Configuration
All configuration via environment variables in .config/settings.env:
| Variable | Default | Description |
|---|---|---|
DATABASE_URL |
postgresql+asyncpg://... |
PostgreSQL connection string |
DB_POOL_SIZE |
10 |
Connection pool size |
DB_MAX_OVERFLOW |
20 |
Max overflow connections |
STALWART_API_URL |
https://mail.autonomia.lat/api |
Stalwart admin API URL |
STALWART_ADMIN_USER |
admin |
Stalwart admin username |
STALWART_ADMIN_PASSWORD |
Stalwart admin password | |
OIDC_ISSUER |
https://sso.pop.coop |
Zitadel OIDC issuer |
OIDC_CLIENT_ID |
OIDC client ID | |
OIDC_CLIENT_SECRET |
OIDC client secret | |
OIDC_REDIRECT_URI |
https://admin.autonomia.lat/auth/callback |
OAuth callback URL |
SUPER_ADMIN_DOMAINS |
pop.coop |
Comma-separated super-admin email domains |
APP_HOST |
:: |
Bind address (IPv6-first) |
APP_PORT |
8000 |
Bind port |
APP_BASE_URL |
https://admin.autonomia.lat |
Public URL |
SECRET_KEY |
(auto-generated) | Session signing key |
DOCS_ENABLED |
false |
Enable /docs and /redoc |
LOG_LEVEL |
INFO |
Logging level |
LOG_JSON |
true |
Structured JSON logging |
API
All endpoints prefixed with /api/v1/. Authentication required via Bearer token or session cookie.
Tenant Management (super-admin only)
POST /tenants-- Create tenantGET /tenants-- List tenants (paginated:?limit=100&offset=0)GET /tenants/{id}-- Get tenant detailsPATCH /tenants/{id}-- Update tenantDELETE /tenants/{id}-- Deactivate tenantPOST /tenants/{id}/domains-- Add domain (auto-generates DKIM keys)DELETE /tenants/{id}/domains/{domain}-- Remove domainPOST /tenants/{id}/admins-- Add tenant adminDELETE /tenants/{id}/admins/{email}-- Remove admin
Account Management (tenant-scoped)
GET /accounts-- List accounts (paginated)POST /accounts-- Create accountGET /accounts/{email}-- Get accountPATCH /accounts/{email}-- Update accountDELETE /accounts/{email}-- Delete account
Group Management (tenant-scoped)
GET /groups-- List groups (paginated)POST /groups-- Create groupPATCH /groups/{name}-- Update groupDELETE /groups/{name}-- Delete group
Domains (tenant-scoped)
GET /domains-- List tenant's domainsGET /domains/{domain}/dns-- Full DNS record check (MX, SPF, DKIM, DMARC, CNAME)PATCH /domains/{domain}/hosts-- Set custom webmail/imap/smtp hostnames
Stats & Monitoring (tenant-scoped)
GET /stats-- Usage stats (accounts, storage, quota)GET /stats/queue-- Mail queueGET /stats/rate-limit-- Rate limit status
Audit (tenant-scoped)
GET /audit-- Audit log (paginated)
Health
GET /health-- DB connectivity check (returns 503 if degraded)
Testing
# Unit tests (212 tests, 94% coverage)
.venv/bin/python -m pytest
# With coverage report
.venv/bin/python -m pytest --cov=src/chaskiadmin --cov-report=term-missing
# Integration tests against real Stalwart (requires env vars)
.venv/bin/python -m pytest -m integration
# Dependency security audit
.venv/bin/pip-audit
Deployment
systemd
sudo cp deploy/chaskiadmin.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now chaskiadmin
The service runs as the chaskiadmin user with systemd security hardening (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, etc).
Migrations
# Apply pending migrations
.venv/bin/python -m alembic upgrade head
# Check current state
.venv/bin/python -m alembic current
Security
- OIDC authentication via Zitadel
- Tenant isolation enforced at the middleware layer
- Security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options
- Trusted host validation
- Per-tenant rate limiting (HTTP 429)
- Session cookies: HttpOnly, Secure, SameSite=Lax
- API docs disabled by default in production
- All inputs validated via Pydantic v2
- SQL injection protected via SQLAlchemy ORM
Tech Stack
- Python 3.12+ / FastAPI / uvicorn
- SQLAlchemy 2.0 (async) + asyncpg + Alembic
- httpx (async Stalwart API client)
- authlib (OIDC/JWT)
- dnspython + cryptography (DNS checker, DKIM)
License
Internal -- POP Coop