Configuration reference
app/config.py is the single source of truth for every environment variable the backend reads. It uses pydantic-settings so all fields are validated at boot — a misconfigured deployment fails loudly at uvicorn startup, not on the first inbound message. Required fields default to "" and a model_validator raises ValidationError listing every missing variable.
The subsystems that use these vars are explained on their own pages — follow the "Used by" deep-links; this page is strictly a lookup table.
Port assignments
All three Soft4U projects can run concurrently with no host-port conflicts. Ratiba's ports:
| Service | Port |
|---|---|
| Frontend (Next.js) | :3010 |
| Backend (FastAPI) | :8010 |
| PostgreSQL | :5434 |
| Redis | :6381 |
| MinIO API | :9200 |
| MinIO Console | :9201 |
| Keycloak | :8281 |
| LiveKit (signal) | :7890 |
| LiveKit (TCP RTC) | :7891 |
| LiveKit (UDP RTC) | :52000-52050 |
| Docusaurus (dev) | :3020 |
Core / app
General application identity and logging. No subsystem page; these are consumed directly by app/main.py and app/logging.py.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
ENV | dev | No | Deployment environment: dev | staging | prod. Controls log formatting and enables dev-only routes. | All subsystems |
LOG_LEVEL | INFO | No | structlog log level: DEBUG | INFO | WARNING | ERROR. | All subsystems |
RATIBA_VERSION | dev | No | Build/version identifier exposed via GET /api/v1/health .version. CI/CD sets this to the build SHA at image-build time. | Health endpoint |
ADMIN_API_KEY | — | Yes | Static key for platform-admin endpoints (e.g. tenant provisioning). PoC artifact — rotated by redeploying with a new value. | Admin API |
Database (PostgreSQL)
Two pools per ADR-0002: a shared asyncpg pool for tenant-scoped queries and per-tenant psycopg micro-pools for synchronous Alembic operations.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
DATABASE_URL | — | Yes | Postgres connection URL — postgresql+asyncpg://user:pass@host:5434/db. The asyncpg pool factory strips the +asyncpg suffix internally. | Identity & tenancy |
DB_POOL_SIZE | 20 | No | Maximum size of the shared asyncpg pool. Tune down on memory-constrained VPS hosts. | Identity & tenancy |
DB_TENANT_OWNER_ROLE | None | No | Postgres role passed to CREATE SCHEMA … AUTHORIZATION <role>. When None (local dev), the AUTHORIZATION clause is omitted and Postgres defaults ownership to the connection user. Production sets this to ratiba. | Identity & tenancy |
RATIBA_MIGRATION_SCOPE | — | Alembic only | Read by alembic/env.py at migration time — public or tenant. Not consumed by the running app. | Schema migrations |
RATIBA_MIGRATION_TENANT_SLUG | — | Alembic only | Read by alembic/env.py at migration time — the slug of the tenant schema to migrate. Not consumed by the running app. | Schema migrations |
Redis
Redis is Ratiba's FSM hot-path store and webhook idempotency layer per ADR-0003.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
REDIS_URL | redis://localhost:6381 | No | Redis connection URL. Default targets Ratiba's pinned dev port :6381. Production overrides to point at the compose service name. | Conversation FSM |
Keycloak
Per ADR-0002 each tenant gets a Keycloak realm provisioned atomically during onboarding. Platform-admin endpoints use master-realm credentials.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
KEYCLOAK_BASE_URL | — | Yes | Base URL of the Keycloak server — no trailing slash, e.g. http://keycloak:8281. | Identity & tenancy |
KEYCLOAK_ADMIN_REALM_USER | — | Yes | Username for Keycloak master-realm admin API calls. Used during tenant provisioning to create the tenant realm + client. | Identity & tenancy |
KEYCLOAK_ADMIN_REALM_PASSWORD | — | Yes | Password for the Keycloak master-realm admin user. Treat as a secret — rotate by redeploying with a new value. | Identity & tenancy |
WhatsApp / Meta
Per ADR-0008 Ratiba uses the WhatsApp Cloud API directly (Meta first-party). Project-level HMAC secrets are set here; per-tenant credentials (whatsapp_phone_number_id, whatsapp_access_token, whatsapp_business_account_id) live in public.tenants and are encrypted at rest with CREDENTIAL_ENCRYPTION_KEY.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
WHATSAPP_APP_SECRET | — | Yes | Project-level Meta App Secret used as the HMAC-SHA256 key for X-Hub-Signature-256 verification across all tenants. Single key per ADR-0008 D2 — rotation = redeploy. | Channel substrate |
WHATSAPP_VERIFY_TOKEN | — | Yes | Webhook validation challenge token matched against Meta's hub.verify_token query param on the GET handshake. Configure the same value in Meta App Dashboard → WhatsApp → Configuration. | Channel substrate |
CREDENTIAL_ENCRYPTION_KEY | — | Yes | Fernet key for at-rest encryption of per-tenant credential columns. URL-safe base64, 32 bytes — generate with python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'. Rotation requires re-encrypting every credential row. | Identity & tenancy |
INSTAGRAM_APP_SECRET | "" | No | Project-level HMAC key for Instagram DM webhook verification. Empty default — Instagram (Tier-2) ships disabled. Deployment without this set still boots. | Channel substrate |
MESSENGER_APP_SECRET | "" | No | Project-level HMAC key for Messenger DM webhook verification. Same disabled-by-default contract as INSTAGRAM_APP_SECRET. | Channel substrate |
Voice (LiveKit / Deepgram / ElevenLabs)
The full-duplex voice channel per ADR-0006 and the M7 / voice-enhancement implementation. See Voice conversation for the architecture.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
LIVEKIT_URL | ws://livekit:7890 | No | LiveKit signal WebSocket URL. Default targets the docker-compose livekit service on Ratiba's pinned signal port. Production overrides to the deployed LiveKit server. | Voice conversation |
LIVEKIT_API_KEY | devkey | No | LiveKit API key. Default devkey matches docker/livekit.yaml dev config — must be overridden in production. | Voice conversation |
LIVEKIT_API_SECRET | devsecretmin32charslong0123456789 | No | LiveKit API secret — minimum 32 chars. Default is the dev value from docker/livekit.yaml — must be overridden in production. | Voice conversation |
DEEPGRAM_API_KEY | "" | No (required at runtime) | Deepgram API key for streaming STT (Nova-3, Swahili + English). Empty default — checked at DeepgramClient instantiation, not at boot. Required in any environment where voice calls are received. | Voice conversation |
ELEVENLABS_API_KEY | "" | No (required at runtime) | ElevenLabs API key for streaming TTS (Multilingual v2). Same lazy-check contract as DEEPGRAM_API_KEY. | Voice conversation |
LLM providers
Per ADR-0005 D5+D6 — two providers ship on day one: Anthropic (Claude Haiku for handoff summariser + reorientation) and OpenAI (GPT-4.1 mini for high-volume narrow-schema tasks). Both keys are required at boot so a misconfigured deployment fails loudly.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
ANTHROPIC_API_KEY | — | Yes | Anthropic API key. Used by AnthropicAdapter for handoff_summarizer + reorientation roles (Claude Haiku 4.5). Per-tenant role overrides in public.tenants.llm_role_overrides can route additional roles through Anthropic. | Conversation FSM |
OPENAI_API_KEY | — | Yes | OpenAI API key. Used by OpenAIAdapter for the narrow-task roles — intent classifier, slot extraction, booking-flow steps, cancel/reschedule (GPT-4.1 mini). | Conversation FSM |
ANTHROPIC_VISION_MODEL | claude-3-5-sonnet-20241022 | No | Anthropic Vision-capable Sonnet model used by VisionExtractor for catalog-photo OCR (ADR-0010 D7). Revisit if Anthropic ships vision-specific Haiku variants. | Catalog onboarding |
Payments (Daraja / PesaPal)
Per ADR-0007 — M-Pesa via Safaricom Daraja (STK push, primary), cards via PesaPal. Per-tenant Daraja and PesaPal credentials are stored encrypted in public.tenants (not here). This section covers project-level payment config.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
PAYMENT_ENVIRONMENT | sandbox | No | Project-level Daraja + PesaPal environment toggle: sandbox | production. Default sandbox so a fresh deploy never accidentally charges real money. Per-tenant tenants.payment_environment_override column can override this. Production cutover = set PAYMENT_ENVIRONMENT=production and redeploy. | Payments |
Per-tenant Daraja credentials (
mpesa_consumer_key,mpesa_consumer_secret,mpesa_shortcode,mpesa_passkey) and PesaPal credentials (pesapal_consumer_key,pesapal_consumer_secret) are stored encrypted inpublic.tenantsand are NOT environment variables. They are provisioned via the admin onboarding flow. See Identity & tenancy and Payments.
SMS (Africa's Talking)
Per ADR-0009 D9 — SMS is a NotificationSink, not a Channel. Used for free-WhatsApp-then-SMS reminder routing (~$0.003/msg via Africa's Talking). Ships disabled by default.
| Variable | Default | Required? | What it does | Used by |
|---|---|---|---|---|
AFRICASTALKING_SMS_API_KEY | "" | No | Africa's Talking SMS API key. When empty the SMS sink short-circuits with a structured error — the rest of Ratiba continues to function. | Channel substrate |
AFRICASTALKING_SMS_USERNAME | "" | No | Africa's Talking account username. Required alongside AFRICASTALKING_SMS_API_KEY for SMS sending — AT's API requires both. | Channel substrate |
AFRICASTALKING_SMS_SENDER_ID | "" | No | Optional registered Sender ID. When empty the AT client falls back to the default short-code (AFRICASTKNG). | Channel substrate |
Adding a new variable
- Add a field to
Settingsinapp/config.pywith a docstring explaining the var. - Add a matching line to
backend/.env.example(the developer-facing mirror). - If the field is required at boot, add the uppercase name to
_REQUIRED_FIELDS(and to_SECRET_FIELDSif it wraps aSecretStr). - Update this page (
docs/reference/configuration.md) — add a row in the right group table, bumplast_verifiedto today.