Properties
category: reference tags: - robot-wtf - v1 - deployment last_updated: 2026-03-15
V1-3: Gunicorn Configuration, Systemd Units, and Deployment
Branch: feat/v1-3-gunicorn-systemd in robot.wtf repo
Commit: 5eb51c6
Deliverables
Entry Points (app/)
| File | Service | Port | Server | Description |
|---|---|---|---|---|
wsgi.py |
Otterwiki | 8000 | Gunicorn | Wraps Flask app in TenantResolver + ManagementMiddleware. Stub when otterwiki not installed. |
api_server.py |
Platform API | 8002 | Gunicorn | ManagementMiddleware + /api/internal/check-slug for Caddy on-demand TLS + static file serving. |
auth_server.py |
Auth | 8003 | Gunicorn | ATProto OAuth stubs (501). Serves client-metadata.json, AS metadata, JWKS. |
mcp_entry.py |
MCP Sidecar | 8001 | Uvicorn | FastMCP stub (ASGI). Serves /.well-known/oauth-protected-resource. |
Shared Configuration
app/gunicorn.conf.py— Bind fromGUNICORN_BINDenv var, workers = min(2*CPU+1, 4), timeout 30s, access log to stdout.
Systemd Units (ansible/roles/deploy/files/)
All services run as robot user, WorkingDirectory=/srv/app/src.
robot-otterwiki.service— Gunicorn, port 8000, MULTI_TENANT=truerobot-api.service— Gunicorn, port 8002robot-auth.service— Gunicorn, port 8003robot-mcp.service— Uvicorn, port 8001
Ansible Deploy Role
ansible/roles/deploy/ with tasks + handlers:
- Syncs
app/to/srv/app/src/app/via rsync - Installs
requirements.txtinto/srv/app/venv - Copies systemd units, enables and starts all four services
- Handlers restart services on code change
- TODO: otterwiki pip install from fork (placeholder comments)
Requirements Added
flask>=3.0.0— needed by api_server, auth_server, wsgi stubuvicorn>=0.29.0— needed by MCP sidecar
Environment Variables
| Variable | Used By | Default |
|---|---|---|
| MULTI_TENANT | wsgi.py | (unset = disabled) |
| ROBOT_DB_PATH | wsgi, api | /srv/data/robot.db |
| SIGNING_KEY_PATH | wsgi, api, auth | /srv/data/signing_key.pem |
| PLATFORM_DOMAIN | all | robot.wtf |
| WIKI_BASE | wsgi | /srv/data/wikis |
| GUNICORN_BIND | gunicorn.conf | 0.0.0.0:8000 |
| ROBOT_STATIC_DIR | api | /srv/static |
| MCP_PORT | mcp | 8001 |
Testing
- All 55 existing tests pass (no regressions)
- auth_server: stub routes return 501, metadata routes return 200 with valid JSON
- mcp_entry: ASGI stub serves oauth-protected-resource (200) and default (501)
- wsgi.py: loads stub Flask app when otterwiki not installed
- api_server: check-slug returns 404 for unknown domains/slugs
- Systemd units validated: correct User, WorkingDirectory, ExecStart, ports
- Ansible YAML validated: 7 tasks, 2 handlers