Skip to content

Environment Variables

All configuration is loaded from environment variables with the APPMECCA_API_ prefix, using Pydantic Settings. The definitive list is the Settings class in src/appmecca_api/config.py.

get_settings() is not cached — every call constructs a fresh Settings() and re-reads the environment.

Required Variables

Variable Description Example
APPMECCA_API_DATABASE_URL PostgreSQL connection string, password embedded postgresql://dbadmin:pass@db.internal:5432/appmecca

The API never resolves the database password itself

The agent reads the RDS master password from Secrets Manager and bakes it into APPMECCA_API_DATABASE_URL before launching the container. _resolve_database_url raises if the URL is missing or carries no password — it will not fall back to its own Secrets Manager call.

Core Variables

Variable Description Default
APPMECCA_API_ENVIRONMENT Environment name — the env this container is bound to dev
APPMECCA_API_DOMAIN Domain used to build app URLs, e.g. dev.appmecca.net (empty)
APPMECCA_API_INTERNAL_DOMAIN Route53 private-zone domain for service discovery (empty → {environment}.appmecca.internal)
APPMECCA_API_URL_PATTERN Tenant URL pattern; mirrors conf/{env}.yaml routing.url_pattern single_label
APPMECCA_API_AWS_REGION Primary AWS region af-south-1
APPMECCA_API_AWS_ACCOUNT_ID AWS account ID (empty)
APPMECCA_API_DYNAMODB_REGION Region for the asset-index table us-east-2
APPMECCA_API_DB_MIN_POOL_SIZE Minimum asyncpg connections 1
APPMECCA_API_DB_MAX_POOL_SIZE Maximum asyncpg connections 10
APPMECCA_API_API_TITLE OpenAPI title AppMecca API
APPMECCA_API_API_VERSION Reported version installed package version
APPMECCA_API_CORS_ORIGINS Allowed CORS origins (JSON list) ["*"]
APPMECCA_API_DEBUG Enable debug mode false

DYNAMODB_REGION is vestigial

The API makes no DynamoDB calls at all. The setting survives from the retired App Registry table; the asset index is read only by the admin CLI and the agent.

Celery, Redis and Secrets

Variable Description Default
APPMECCA_API_CELERY_BROKER_URL Redis broker, e.g. redis://host:6379/0 (empty)
APPMECCA_API_CELERY_RESULT_BACKEND Redis result backend, e.g. redis://host:6379/1 (empty)
APPMECCA_API_ROOT_KEY_SECRET_NAME Secrets Manager name for the root API key (empty)
APPMECCA_API_GATEWAY_INTERNAL_SECRET Shared secret the auth gateway sends on /api/internal/* (empty)
APPMECCA_API_OAUTH_TOKEN_KEY Base64 AES-256-GCM key sealing linked-git tokens (empty → repo linking disabled)

Authentication and Rate Limits

Variable Description Default
APPMECCA_API_INVITE_REQUIRED A GRANT, not a filter — see the ⚠ below. When on, an address may create an account if it holds a pending invite. Enforced at the account-CREATION seams (both the emailed-code path and the OAuth callback), not only on POST /api/auth/code/request. On the request route an uninvited address gets the same body and no mail (never a 403 — that would be an enumeration oracle); on the two post-proof doors it gets 403 signup_not_permitted. An EXISTING user is always let through: the gate governs who may create an account, not who may sign back into one. false
APPMECCA_API_AUTH_SIGNUP_SELFSERVE_DOMAINS A GRANT, not a filter. Comma-separated domains whose addresses may self-serve signup (e.g. oronomo.com,unomena.com). Empty = off. Matching is exact, case-insensitive equality on the label after the last @ — never a suffix match, so evil-unomena.com and unomena.com.evil.co are refused. (empty)
APPMECCA_API_AUTH_RATE_LIMIT_REDIS_URL Redis for cross-instance limit state (empty → per-process memory) (empty)
APPMECCA_API_AUTH_RATE_LIMIT_LOGIN_IP Per-IP login cap 20/minute
APPMECCA_API_AUTH_RATE_LIMIT_REGISTER_IP Per-IP register cap 5/minute
APPMECCA_API_AUTH_RATE_LIMIT_RESET_IP Per-IP password-reset cap 5/minute
APPMECCA_API_AUTH_RATE_LIMIT_APP_TICKET_IP Per-IP app-ticket mint cap 60/minute
APPMECCA_API_AUTH_RATE_LIMIT_LOGIN_EMAIL Per-email login cap 5/minute
APPMECCA_API_AUTH_RATE_LIMIT_REGISTER_EMAIL Per-email register cap 3/hour
APPMECCA_API_AUTH_RATE_LIMIT_RESET_EMAIL Per-email password-reset cap 3/hour

⚠ The two signup settings compose with OR, so configuring either one WIDENS admission — it never narrows it. An address is admitted if it passes any gate that is on; if neither is on, signup is open. Setting the domain list while invite-only is on does not add safety: it re-opens self-serve signup for those whole domains. The API logs the resolved policy in words at boot (signup_policy). See the operator runbook for the three-stage rollout and its mandatory OAuth acceptance probe.

Social-login credentials are read directly from the process environment by auth/oauth/providers.py, not through Settings:

APPMECCA_API_OAUTH_{GOOGLE|GITHUB|GITLAB}_CLIENT_ID
APPMECCA_API_OAUTH_{GOOGLE|GITHUB|GITLAB}_CLIENT_SECRET

On a real environment these are provisioned into Secrets Manager at appmecca/{env}/oauth/{provider} — infra setup seeds them pre-DAG, and appmecca config oauth set rotates them. The agent injects them on the next deploy.

Email

Variable Description Default
APPMECCA_API_EMAIL_PROVIDER ses, dev (stdout + ring buffer) or null (empty)
APPMECCA_API_EMAIL_SENDER Verified SES identity (empty)
APPMECCA_API_EMAIL_CONFIGURATION_SET SES configuration set (empty)
APPMECCA_API_EMAIL_LINK_BASE_URL Base URL for invite / reset links (empty → https://api.{domain})
APPMECCA_API_EMAIL_VERIFICATION_RESEND_COOLDOWN_SECONDS Anti-spam cooldown; set 0 on test envs 120
APPMECCA_API_NOTIFICATION_RECIPIENTS Comma-separated operational-notification recipients (empty → dispatch disabled)

Certificates

These are live-tunable — appmecca infra conf-update -e <env> pushes them from conf/{env}.yaml into the platform_config table, and the runtime-config service caches the row for 30 s. The values below are the fallbacks used when no row exists.

Variable Description Default
APPMECCA_API_CUSTOM_DOMAIN_PROVIDER letsencrypt or acm for new custom domains letsencrypt
APPMECCA_API_LETSENCRYPT_STAGING Use the LE staging endpoint false
APPMECCA_API_LETSENCRYPT_CONTACT_EMAIL ACME account contact (empty)
APPMECCA_API_RENEWAL_THRESHOLD_DAYS Renew this many days before expiry 30
APPMECCA_API_DNS_CHECK_INTERVAL_SECONDS DNS polling cadence 60

Deployed vs Local

On a deployed environment the agent injects these variables at docker run time from the environment's config and Secrets Manager — you never set them by hand.

When running locally (via make run), export them in your shell or use the local Docker stack at appmecca-api/dev/:

export APPMECCA_API_DATABASE_URL="postgresql://appmecca:password@localhost:5432/appmecca?sslmode=disable"
export APPMECCA_API_ENVIRONMENT="dev"

sslmode=disable for local PostgreSQL

get_pool() requires TLS by default. A local Postgres container has no TLS configured, so the URL needs sslmode=disable to opt out. Deployed URLs never carry that flag. Whether TLS is verified or merely encrypted is a separate, shared policy governed by APPMECCA_DB_TLS_VERIFY (note: no _API_ prefix — it is read by appmecca.db.tls, not by Settings).

Connection Pool Sizing

The defaults (DB_MIN_POOL_SIZE=1, DB_MAX_POOL_SIZE=10) are per worker process. The container runs uvicorn --workers 2 and the api service runs replicas: 3, so a fully-warm environment can hold up to 2 × 3 × 10 = 60 connections from the API alone — before the Celery worker and Beat containers.

That matters because the deploy-queue admission gate budgets tenant connections against a fraction of the RDS max_connections. Raise these values only alongside the RDS instance class.

For local development the defaults are fine.