Configuration

The environment-variable reference for Cordy Gateway — database, Redis, provider keys, security, licensing, and runtime behavior.

Cordy Gateway is configured through environment variables. Start from deploy/env.template, which ships with placeholders and no real secrets. In the production Compose workflow, .env supplies interpolation values; the required override below passes settings that the base production file does not map into its containers.

Minimum required in production

These have no safe default and must be set for a production deployment:

  • DB_NAME, DB_USER, DB_PASSWORD
  • DJANGO_SECRET_KEY, DJANGO_ALLOWED_HOSTS, DJANGO_ADMIN_URL
  • CHANNEL_ENCRYPTION_KEYS (identical value for the Admin and Gateway services)

Generate secrets:

# DJANGO_SECRET_KEY
python -c "import secrets; print(secrets.token_urlsafe(50))"

# CHANNEL_ENCRYPTION_KEYS (a Fernet key)
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

Database

VariableDefaultNotes
DB_NAMEDatabase name. Read directly by the Gateway data plane. If unset, the Gateway falls back to a dev database name and cannot reach your production DB.
DB_USERDatabase user. Read directly by the Gateway.
DB_PASSWORDDatabase password. Read directly by the Gateway.
DB_HOSTpostgres (Admin) / pgbouncer (Gateway)The Gateway connects through PgBouncer; the Admin connects to Postgres directly.
DB_PORT5432
DATABASE_URLConsumed by the Admin (and migration/test tooling) only. The Gateway data plane does not parse DATABASE_URL; it builds its DSN from DB_*. Set both consistently.

Gotcha: the Gateway builds its Postgres DSN from DB_NAME / DB_USER / DB_PASSWORD, not from DATABASE_URL. If those are missing on the gateway service, it defaults to a development database name, cannot connect, and every request fails auth with 401. Mirror the Admin's DB_* values onto the Gateway.

Redis

VariableDefaultNotes
REDIS_URLredis://localhost:6379/0Redis connection URL. In the prod compose this is redis://redis:6379/0.
REDIS_POOL_SIZE20Connection pool size. 20 is the tuned default; the shipped template raises it to 500 for high concurrency.

Provider keys

Provider and channel credentials are normally managed as encrypted Channels in the Admin, not in the environment. The following environment keys exist because the underlying LiteLLM integration reads them automatically; they are optional and mainly useful for development seeding. Prefer per-channel encrypted credentials for production.

VariableProvider
OPENAI_API_KEYOpenAI
ANTHROPIC_API_KEYAnthropic
OPENROUTER_API_KEYOpenRouter
GEMINI_API_KEYGoogle Gemini
AZURE_API_KEY, AZURE_API_BASE, AZURE_API_VERSIONAzure OpenAI
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION_NAMEAWS Bedrock
COHERE_API_KEYCohere
GROQ_API_KEYGroq
TOGETHERAI_API_KEYTogether AI
DEEPINFRA_API_KEYDeepInfra
REPLICATE_API_TOKENReplicate
HUGGINGFACE_API_KEYHugging Face

See Providers for how channels and credentials work.

Security

VariableDefaultNotes
CHANNEL_ENCRYPTION_KEYSComma-separated Fernet keys that encrypt channel upstream credentials at rest. The first is the active (encrypting) key; the rest allow rotation. Mandatory in production — the Admin and Gateway refuse to start without it, and both must use the same value.
CLIENT_CORS_ORIGINSemptyComma-separated Origins allowed to call the Gateway from a browser. Empty denies all cross-origin browser access (server-to-server callers do not use CORS). A literal * is refused at boot in production.
CLIENT_CORS_ALLOW_WILDCARD_IN_PRODUCTIONfalseUnsafe escape hatch to permit a * client CORS origin in production. Leave off.
TRUSTED_PROXY_CIDRSemptyCIDRs of trusted reverse proxies. X-Forwarded-For / X-Real-IP are honoured for the logged client IP only when the socket peer matches one of these; otherwise the socket peer is used. Prevents audit-IP spoofing.
GATEWAY_METRICS_TOKENemptyBearer token scrapers must present for /metrics/prometheus. Use this for a production endpoint reached through public nginx. With neither this nor an allowed CIDR set, production returns 403.
GATEWAY_METRICS_ALLOWED_CIDRSemptyCIDRs allowed to scrape without a token. The check uses the immediate socket peer and runs before token validation. Behind public nginx, allowing nginx or its network would also admit public requests proxied through it; leave this empty. Use CIDRs only for a direct, isolated trusted scrape path.
DJANGO_SECRET_KEYDjango cryptographic key (Admin). Required in production.
DJANGO_ALLOWED_HOSTSComma-separated hostnames the Admin serves. Required in production.
DJANGO_ADMIN_URLAdmin URL path prefix; must end with /. Pick a non-obvious value. Required in production.
DJANGO_CSRF_TRUSTED_ORIGINSemptyScheme-qualified origins allowed to POST to the Admin behind a TLS proxy (e.g. https://admin.example.com).
DJANGO_SECURE_SSL_REDIRECTTrueForce HTTP→HTTPS redirect. Set False only for a local no-TLS test.
ADMIN_HTTP_PORT8001Host port the Admin is published on.
GATEWAY_HTTP_PORT8002Host port the Gateway (via nginx) is published on.

Admin and member portal

VariableDefaultNotes
CORDY_GATEWAY_BASE_URLhttp://127.0.0.1:8002/v1Base URL shown in the /my/ member quickstart. Set it to the client-reachable HTTPS URL in production; it does not change Gateway binding.
ADMIN_LANGFUSE_TRACE_URL_TEMPLATEemptyOptional external trace link shown in Request logs and member usage. Must be an HTTP(S) URL containing the literal {trace_id}, for example https://langfuse.example.com/trace/{trace_id}.

Both are Admin-process variables and must be passed through by the deployment override below.

Required production Compose override

docker-compose.prod.yml does not load .env as a container env_file. The --env-file .env option only supplies ${...} interpolation, and the base file does not map ENVIRONMENT or the security and runtime variables below into gateway. With the base file alone, the Gateway therefore uses its development default: an empty CORS list falls back to *, metrics is open when neither metrics control is set, .env tuning values that are not referenced are ignored, and the credit-consume WAL remains disabled.

Create docker-compose.production-overrides.yml beside the base file. This corrects the effective deployment; it does not change the upstream docker-compose.prod.yml:

services:
  admin:
    environment:
      CORDY_GATEWAY_BASE_URL: ${CORDY_GATEWAY_BASE_URL:?set CORDY_GATEWAY_BASE_URL}
      ADMIN_LANGFUSE_TRACE_URL_TEMPLATE: ${ADMIN_LANGFUSE_TRACE_URL_TEMPLATE:-}
  gateway:
    environment:
      ENVIRONMENT: production
      CLIENT_CORS_ORIGINS: ${CLIENT_CORS_ORIGINS:-}
      CLIENT_CORS_ALLOW_WILDCARD_IN_PRODUCTION: ${CLIENT_CORS_ALLOW_WILDCARD_IN_PRODUCTION:-false}
      TRUSTED_PROXY_CIDRS: ${TRUSTED_PROXY_CIDRS:-}
      GATEWAY_METRICS_TOKEN: ${GATEWAY_METRICS_TOKEN:-}
      GATEWAY_METRICS_ALLOWED_CIDRS: ${GATEWAY_METRICS_ALLOWED_CIDRS:-}
      GATEWAY_DEFAULT_ROUTING_STRATEGY: ${GATEWAY_DEFAULT_ROUTING_STRATEGY:-weighted}
      GATEWAY_MAX_BODY_BYTES: ${GATEWAY_MAX_BODY_BYTES:-10485760}
      GATEWAY_MAX_INFLIGHT_PER_KEY: ${GATEWAY_MAX_INFLIGHT_PER_KEY:-50}
      GATEWAY_DEFAULT_MAX_COMPLETION_TOKENS: ${GATEWAY_DEFAULT_MAX_COMPLETION_TOKENS:-4096}
      GATEWAY_PRICE_TTL_SECONDS: ${GATEWAY_PRICE_TTL_SECONDS:-300}
      IDEMPOTENCY_TTL_SECONDS: ${IDEMPOTENCY_TTL_SECONDS:-600}
      GATEWAY_CREDIT_CONSUME_WAL: /var/lib/cordy/credit-consume.jsonl
      OTEL_TRACING_ENABLED: ${OTEL_TRACING_ENABLED:-false}
      OTEL_SERVICE_NAME: ${OTEL_SERVICE_NAME:-cordy-gateway}
      OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT:-}
      OTEL_EXPORTER_OTLP_HEADERS: ${OTEL_EXPORTER_OTLP_HEADERS:-}
      OTEL_SHUTDOWN_TIMEOUT_SECONDS: ${OTEL_SHUTDOWN_TIMEOUT_SECONDS:-2.0}
    volumes:
      - gateway_wal:/var/lib/cordy

volumes:
  gateway_wal:

Set CORDY_GATEWAY_BASE_URL=https://gateway.example.com/v1 and, when used, ADMIN_LANGFUSE_TRACE_URL_TEMPLATE=https://langfuse.example.com/trace/{trace_id} in .env. Set explicit browser origins and trusted proxy CIDRs when your topology needs them. For a public nginx deployment, set a strong GATEWAY_METRICS_TOKEN and leave GATEWAY_METRICS_ALLOWED_CIDRS empty; the CIDR check sees nginx as the peer, not the original public client.

Apply both files whenever starting, recreating, or scaling the stack:

docker compose \
  -f docker-compose.prod.yml \
  -f docker-compose.production-overrides.yml \
  --env-file .env up -d

The named volume makes /var/lib/cordy/credit-consume.jsonl durable across container replacement. The shipped Gateway image declares no USER, so it runs as root and can write the mounted directory. If you redistribute the image under a custom UID, create and grant that UID write access to /var/lib/cordy; the application opens the file directly and does not create a missing parent directory.

When the Redis consume-buffer write fails, the Gateway appends a JSON-lines cost record to this WAL and lets the request finish. If the path is empty or the append also fails, it records a consume_drop_total under-charge instead. The repository has no automated WAL replay command. Preserve the file as billing evidence and reconcile it with request logs, the credit ledger, and upstream invoices before applying any manual adjustment; do not feed the file to an undocumented command.

Licensing

Cordy Gateway uses an offline, HMAC-signed license. Licensing defaults to audit-only (fail-open); it does not gate a normal deployment.

VariableDefaultNotes
LICENSE_KEYemptyHMAC-signed license payload. Blank for unlicensed/dev.
LICENSE_SIGNING_SECRETemptyShared secret used to verify the license, provided per customer.
LICENSE_CUSTOMER_IDemptyExpected customer id to match the payload; blank to skip.
LICENSE_NODE_IDnode-1Current node id for node-count checks.
LICENSE_ENFORCEMENT_MODEaudit_onlyaudit_only (fail-open, logs only) or strict (fail-closed).

Runtime behavior

VariableDefaultNotes
GATEWAY_DEFAULT_ROUTING_STRATEGYweightedDefault channel-selection strategy: weighted, cheapest, fastest, or region_aware. Overridable per request with X-Routing-Strategy.
GATEWAY_MAX_BODY_BYTES10485760 (10 MiB)Hard request-body cap. Larger bodies get 413. <= 0 disables the cap.
GATEWAY_MAX_INFLIGHT_PER_KEY50Max concurrent in-flight requests per API key. <= 0 disables.
GATEWAY_DEFAULT_MAX_COMPLETION_TOKENS4096Completion-token estimate used for quota admission when a request sets no max_tokens.
GATEWAY_PRICE_TTL_SECONDS300Per-process TTL for the effective-price cache used in cost snapshots.
GATEWAY_CREDIT_CONSUME_WALemptyAppend-only JSON-lines fallback for credit-consume costs when the Redis buffer write fails. The production override sets a durable path; an empty or unwritable path can leave a counted under-charge.
GRANIAN_WORKERS8Granian worker processes per Gateway container.
IDEMPOTENCY_TTL_SECONDS600Lifetime of a stored idempotent response / lock.

Observability (OpenTelemetry)

Tracing is off by default. The production override maps every supported OTEL_* field into gateway. Configure an operator-managed OTLP/HTTP collector that is reachable from the Gateway container:

OTEL_TRACING_ENABLED=true
OTEL_SERVICE_NAME=cordy-gateway
OTEL_EXPORTER_OTLP_ENDPOINT=https://telemetry.example.com/v1/traces
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <collector-token>"
OTEL_SHUTDOWN_TIMEOUT_SECONDS=2.0

Apply the updated environment to the Gateway:

docker compose \
  -f docker-compose.prod.yml \
  -f docker-compose.production-overrides.yml \
  --env-file .env up -d gateway

The supplied docker-compose.tracing.yml and docker-compose.observability.yml are development/reference profiles. They attach collectors on networks that the production Gateway does not join, and the observability profile ships CHANGEME defaults. Do not combine them with the production stack without separately engineering and validating networking, secrets, storage, and access controls.

VariableDefaultNotes
OTEL_TRACING_ENABLEDfalseEnable OpenTelemetry tracing for the Gateway.
OTEL_SERVICE_NAMEcordy-gatewayservice.name reported in traces.
OTEL_EXPORTER_OTLP_ENDPOINTemptyOTLP HTTP endpoint for trace export.
OTEL_EXPORTER_OTLP_HEADERSemptyComma-separated OTLP exporter headers.
OTEL_SHUTDOWN_TIMEOUT_SECONDS2.0Bounded flush timeout on shutdown.

Scaling and infrastructure

These tune the compose stack and horizontal scaling. See Deployment.

VariableDefaultNotes
SCALE_GRANIAN_WORKERS4Per-replica worker count when running multiple gateway replicas.
ADMIN_WORKERS3Gunicorn workers for the Admin service.
PGBOUNCER_POOL_SIZE100PgBouncer default pool size.
PGBOUNCER_MAX_CLIENT_CONN400PgBouncer max client connections.
REDIS_MAXMEMORY512mbRedis max memory (with volatile-lru eviction that never touches money keys).

Backups

VariableDefaultNotes
BACKUP_DIR./backups in scriptsWhere logical backups are written. The production template recommends /var/backups/cordy.
BACKUP_RETENTION7Backups to retain.
REQUIRE_PITR0Set to 1 once WAL archiving / point-in-time recovery is enabled to enforce it in acceptance checks.

Next steps

  • Deployment — apply these settings to a production topology.
  • Operations — verify health, backups, upgrades, and traces.
  • Security — the security-relevant variables in context.