Skip to main content

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:

ServicePort
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.

VariableDefaultRequired?What it doesUsed by
ENVdevNoDeployment environment: dev | staging | prod. Controls log formatting and enables dev-only routes.All subsystems
LOG_LEVELINFONostructlog log level: DEBUG | INFO | WARNING | ERROR.All subsystems
RATIBA_VERSIONdevNoBuild/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_KEYYesStatic 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.

VariableDefaultRequired?What it doesUsed by
DATABASE_URLYesPostgres connection URL — postgresql+asyncpg://user:pass@host:5434/db. The asyncpg pool factory strips the +asyncpg suffix internally.Identity & tenancy
DB_POOL_SIZE20NoMaximum size of the shared asyncpg pool. Tune down on memory-constrained VPS hosts.Identity & tenancy
DB_TENANT_OWNER_ROLENoneNoPostgres 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_SCOPEAlembic onlyRead by alembic/env.py at migration time — public or tenant. Not consumed by the running app.Schema migrations
RATIBA_MIGRATION_TENANT_SLUGAlembic onlyRead 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.

VariableDefaultRequired?What it doesUsed by
REDIS_URLredis://localhost:6381NoRedis 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.

VariableDefaultRequired?What it doesUsed by
KEYCLOAK_BASE_URLYesBase URL of the Keycloak server — no trailing slash, e.g. http://keycloak:8281.Identity & tenancy
KEYCLOAK_ADMIN_REALM_USERYesUsername for Keycloak master-realm admin API calls. Used during tenant provisioning to create the tenant realm + client.Identity & tenancy
KEYCLOAK_ADMIN_REALM_PASSWORDYesPassword 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.

VariableDefaultRequired?What it doesUsed by
WHATSAPP_APP_SECRETYesProject-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_TOKENYesWebhook 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_KEYYesFernet 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""NoProject-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""NoProject-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.

VariableDefaultRequired?What it doesUsed by
LIVEKIT_URLws://livekit:7890NoLiveKit 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_KEYdevkeyNoLiveKit API key. Default devkey matches docker/livekit.yaml dev config — must be overridden in production.Voice conversation
LIVEKIT_API_SECRETdevsecretmin32charslong0123456789NoLiveKit API secret — minimum 32 chars. Default is the dev value from docker/livekit.yamlmust 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.

VariableDefaultRequired?What it doesUsed by
ANTHROPIC_API_KEYYesAnthropic 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_KEYYesOpenAI 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_MODELclaude-3-5-sonnet-20241022NoAnthropic 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.

VariableDefaultRequired?What it doesUsed by
PAYMENT_ENVIRONMENTsandboxNoProject-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 in public.tenants and 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.

VariableDefaultRequired?What it doesUsed by
AFRICASTALKING_SMS_API_KEY""NoAfrica'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""NoAfrica's Talking account username. Required alongside AFRICASTALKING_SMS_API_KEY for SMS sending — AT's API requires both.Channel substrate
AFRICASTALKING_SMS_SENDER_ID""NoOptional registered Sender ID. When empty the AT client falls back to the default short-code (AFRICASTKNG).Channel substrate

Adding a new variable

  1. Add a field to Settings in app/config.py with a docstring explaining the var.
  2. Add a matching line to backend/.env.example (the developer-facing mirror).
  3. If the field is required at boot, add the uppercase name to _REQUIRED_FIELDS (and to _SECRET_FIELDS if it wraps a SecretStr).
  4. Update this page (docs/reference/configuration.md) — add a row in the right group table, bump last_verified to today.