Skip to main content
All configuration is supplied through environment variables. Copy .env.example to .env and fill in the values before starting the server. When DATABASE_URL is not set, the app uses an in-memory store — no database is required for local development or demos.
The following variables are production-critical. The application starts with the development defaults, but using them in production is a security risk.
  • JWT_SECRET — change from dev-secret-change-me before any real traffic
  • CAUSELOOP_MASTER_KEY — must be set when DATABASE_URL is set; encrypts all per-workspace data keys

Core server

VariableDefaultRequiredDescription
PORT4000NoHTTP listen port. Caddy and docker-compose.yml proxy to this port.
JWT_SECRETdev-secret-change-meYes (prod)HS256 signing key for JWTs issued at /auth/token. Generate with python3 -c "import secrets; print(secrets.token_urlsafe(48))".
CORS_ORIGINShttp://localhost:3000Yes (prod)Comma-separated list of allowed CORS origins. Set to your frontend domain in production, e.g. https://app.causeloop.ai.

Authentication — Clerk

These variables are only required if you use the /auth/exchange endpoint to verify Clerk session JWTs issued by your frontend.
VariableDefaultRequiredDescription
CLERK_JWKS_URLhttps://regular-mosquito-10.clerk.accounts.dev/.well-known/jwks.jsonNo (required for Clerk auth)Your Clerk instance’s JWKS endpoint. Find it in the Clerk dashboard under API Keys → Advanced. Replace the example value with your own instance URL.
CLERK_AUDIENCE(empty)NoOptional expected aud claim in Clerk JWTs. Leave empty to skip audience validation.
CLERK_AUTHORIZED_PARTIEShttp://localhost:3000NoComma-separated list of authorized frontend origins. Clerk uses this to validate the azp claim.
If CLERK_JWKS_URL is set to the placeholder value from .env.example, the app still starts but /auth/exchange will verify JWTs against Anthropic’s demo Clerk instance — which will reject tokens from your own frontend. Always replace it with your own JWKS URL.

LLM providers

Leave both API key variables empty to run in offline mock mode — the mock provider returns plausible stub responses and is suitable for local development and tests.
VariableDefaultRequiredDescription
ANTHROPIC_API_KEY(empty)NoAnthropic API key. When set, Anthropic Claude is available as an LLM provider.
OPENAI_API_KEY(empty)NoOpenAI API key. When set, GPT-4o models are available.
LLM_PROVIDER_ORDERanthropic,openai,mockNoComma-separated resolution order. The first provider with a usable key is selected.
USE_MOCK_LLMfalseNoForce the offline mock regardless of which API keys are set. Useful for CI or air-gapped environments.
OPENAI_MODEL_REASONINGgpt-4oNoOpenAI model used for multi-step reasoning tasks (pattern analysis, predictions).
OPENAI_MODEL_FASTgpt-4o-miniNoOpenAI model used for quick completions (summaries, labels).

Database

VariableDefaultRequiredDescription
DATABASE_URL(unset)No (prod: Yes)PostgreSQL connection string. When unset, the app uses an in-memory store. For Neon: postgresql://<user>:<pass>@<endpoint>.neon.tech/<db>?sslmode=require.
POSTGRES_PASSWORD(unset)Only with docker-composeUsed by docker-compose.yml to set the Postgres superuser password and inject DATABASE_URL into the API container. Do not set this if you are using an external managed database.
For local Postgres: DATABASE_URL=postgresql://localhost:5432/causeloopFor Neon (hosted, SSL required): DATABASE_URL=postgresql://<user>:<pass>@<endpoint>.neon.tech/<db>?sslmode=require

Encryption

VariableDefaultRequiredDescription
CAUSELOOP_MASTER_KEY(empty)Yes when DATABASE_URL is setBase64-encoded 32-byte AES-256 key (the Key Encryption Key). Wraps per-workspace data keys stored in workspace_keys. Never commit this value. Generate with: python3 -c "import os,base64;print(base64.b64encode(os.urandom(32)).decode())"
If DATABASE_URL is set but CAUSELOOP_MASTER_KEY is empty, the application will raise a KeyError_ on the first request that touches an encrypted column. Generate and store this secret securely before pointing the app at a real database.

Frontend integration

Add these to your Next.js (or any frontend) .env.local:
NEXT_PUBLIC_USE_MOCK=0
NEXT_PUBLIC_API_BASE_URL=https://api.causeloop.ai/v1
NEXT_PUBLIC_WS_URL=wss://api.causeloop.ai/v1/stream
For local development:
NEXT_PUBLIC_API_BASE_URL=http://localhost:4000/v1
NEXT_PUBLIC_WS_URL=ws://localhost:4000/v1/stream
The API client reads the JWT from localStorage['causeloop_token'].

Full .env.example

PORT=4000
JWT_SECRET=dev-secret-change-me
CORS_ORIGINS=http://localhost:3000

# Leave LLM keys empty to use the offline mock provider:
ANTHROPIC_API_KEY=
OPENAI_API_KEY=
LLM_PROVIDER_ORDER=anthropic,openai,mock
USE_MOCK_LLM=false
OPENAI_MODEL_REASONING=gpt-4o
OPENAI_MODEL_FAST=gpt-4o-mini

# Clerk — required for POST /auth/exchange to verify Clerk session JWTs (RS256)
CLERK_JWKS_URL=https://regular-mosquito-10.clerk.accounts.dev/.well-known/jwks.json
CLERK_AUDIENCE=
CLERK_AUTHORIZED_PARTIES=http://localhost:3000

# Postgres (when set, the app uses the Postgres repo instead of in-memory)
# DATABASE_URL=postgresql://localhost:5432/causeloop

# Envelope-encryption master key (base64 of 32 bytes). REQUIRED when DATABASE_URL is set.
# Generate: python3 -c "import os,base64;print(base64.b64encode(os.urandom(32)).decode())"
CAUSELOOP_MASTER_KEY=