Configuration
Every setting is an environment variable. Local dev reads them from a root .env file (cp .env.example .env); Docker Compose passes them into the containers. The annotated .env.example is the canonical, always-current list — this page groups and explains it.
Core
| Variable | Used by | Default | Notes |
|---|---|---|---|
DATABASE_URL | api, mcp | postgres://kanban:kanban@localhost:5432/kanban | Postgres connection string. Compose builds it from the POSTGRES_* vars. |
REDIS_URL | api, mcp | redis://localhost:6379 | Event bus + rate-limit + session store. |
SESSION_SECRET | api | dev fallback | Signs the session cookie (HMAC). Required in production, ≥ 32 chars — the api fails boot if missing/weak unless dev ergonomics are opted in (NODE_ENV=development/test, or ALLOW_DEV_SECRETS=true). Generate: openssl rand -hex 32. |
ALLOW_DEV_SECRETS | api | unset | Set true to boot with the insecure fallback secret in local dev. Never in production. |
PORT | api / mcp | 3000 / 3001 | HTTP listen port. |
NODE_ENV | api, mcp | development | production | development | test. Governs secret-strength enforcement and the default registration policy. |
LOG_LEVEL | api, mcp | info | pino level: trace | debug | info | warn | error. |
PG_POOL_MAX | api | 10 | Max Postgres pool size (postgres-js max). Tune to your DB's connection budget × process count. |
TRUST_PROXY | api, mcp | false | Trust X-Forwarded-For so req.ip is the real client. true trusts the whole chain; an integer is a hop count. Must be set behind a load balancer, or per-IP rate limiting collapses every client into one bucket. |
Origins & CORS
| Variable | Default | Notes |
|---|---|---|
CORS_ORIGINS | http://localhost:5173,http://localhost:8080 | Comma-separated exact origins (scheme+host+port) the browser may call the api from with the session cookie. No wildcard (* is invalid with credentialed requests); an unlisted origin gets no Access-Control-Allow-Origin. |
PUBLIC_BASE_URL | http://localhost:3000 | Public origin of the deployment — the OAuth issuer base and the root of every URL advertised in /.well-known/oauth-authorization-server. |
OAUTH_ISSUER_URL | PUBLIC_BASE_URL | The OAuth 2.1 issuer identifier. Set explicitly if the issuer differs from PUBLIC_BASE_URL. |
MCP_PUBLIC_URL | http://localhost:3001/mcp | The MCP server's public /mcp URL — the RFC 9728 resource identifier. OAuth grants are bound to it (authorize requests must send resource=<MCP_PUBLIC_URL>). The api and the mcp service read the same variable so they can't disagree. |
Registration
| Variable | Default | Notes |
|---|---|---|
REGISTRATION_MODE | prod → closed; dev/test → invite if a code is set, else open | closed (register returns 403) | invite (requires the code below) | open. Fails closed on ambiguity. |
REGISTER_INVITE_CODE | — | The shared invite code when REGISTRATION_MODE=invite. ≥ 12 chars — the api refuses to boot if it is empty or shorter. Generate: openssl rand -hex 16. This is the feature that gates self-registration; the code itself is your secret. |
Sessions (human login cookie)
| Variable | Default | Notes |
|---|---|---|
SESSION_IDLE_TTL_MS | 604800000 (7 d) | Idle TTL — slides forward on each request. |
SESSION_ABSOLUTE_TTL_MS | 2592000000 (30 d) | Absolute cap regardless of activity. |
SESSION_STALE_MINUTES | 30 | An agent work session with no events for this long renders a "stale" badge (derived, never stored). |
Search
| Variable | Default | Notes |
|---|---|---|
SEARCH_TSVECTOR_CONFIG | english | Postgres text-search config used to parse queries (websearch_to_tsquery). MUST match the config baked into the stored cards.search_tsv generated column (english by default). Only set to simple if you rebuilt that column for a non-English instance. |
Webhooks
| Variable | Default | Notes |
|---|---|---|
WEBHOOK_MAX_PER_BOARD | 20 | Max webhook subscriptions per board (amplification bound). |
WEBHOOK_DELIVERY_SWEEP_INTERVAL_MS | 3600000 (1 h) | How often the background sweep retries/expires pending deliveries. |
WEBHOOK_ALLOW_PRIVATE_TARGETS | off | SSRF escape hatch — set 1 to let webhook/automation targets resolve to private/loopback/link-local IPs. For local dev only; leave off in production. |
Automations (rules engine)
| Variable | Default | Notes |
|---|---|---|
AUTOMATION_MAX_PER_BOARD | 32 | Max automation rules per board. |
AUTOMATION_RULE_RATE_LIMIT | 30 | Runs/minute before a rule auto-pauses to break feedback loops. |
Rate limiting
| Variable | Default | Notes |
|---|---|---|
RATE_LIMIT_AUTH_MAX | 10 | Pre-auth brute-force bucket for login/register, per client IP per minute. Raise it if many legitimate users share one egress IP. |
RATE_LIMIT_IP_MAX | 1000 | Outer per-IP request cap enforced in memory before any DB-backed auth lookup. |
MCP_RATE_LIMIT_TOKEN_MAX | 120 | Per-token authenticated MCP call limit (requests/window). Shared bucket with the REST api. |
MCP_RATE_LIMIT_WINDOW_MS | 60000 | The MCP rate-limit window. |
MCP_PREAUTH_IP_MAX / MCP_PREAUTH_WINDOW_MS | 300 / 60000 | Pre-auth per-IP flood cap on the MCP server. |
WebSocket gateway
All optional; defaults shown.
| Variable | Default | Notes |
|---|---|---|
WS_REVALIDATE_INTERVAL_MS | 60000 | How often each live connection re-checks board membership + token expiry. |
WS_MAX_LIFETIME_MS | 14400000 (4 h) | Max lifetime of one WS connection before it closes and the client reconnects. |
WS_TICKET_TTL_MS | 30000 | Lifetime of a single-use WS handshake ticket (POST /api/ws-ticket). |
WS_MAX_PAYLOAD_BYTES | 16384 | Max inbound frame size; oversized frames close the socket. |
WS_MSG_RATE_PER_SEC / WS_MSG_BURST | 20 / 40 | Per-socket message rate limit (token bucket). |
WS_MAX_SUBSCRIPTIONS | 32 | Max concurrent board subscriptions per connection. |
WS_MAX_CONNECTIONS | 2048 | Max concurrent connections per api instance (excess closed 1013). |
MCP
| Variable | Default | Notes |
|---|---|---|
MCP_PORT | 3001 | Compose host port for the mcp service. |
KANBAN_MCP_LEGACY_TOOLS | off | Set 1 to expose the deprecated flat MCP tool catalog alongside the v2 consolidated tools (one deprecation release). |
Integrations
| Variable | Default | Notes |
|---|---|---|
INTEGRATION_SECRET_KEY | — | 32-byte symmetric key (64 hex chars or base64) used to AES-GCM encrypt integration secrets at rest. Required only when integrations store secrets; resolving it fails fast if missing/short. Generate: openssl rand -hex 32. |
Attachment storage
| Variable | Default | Notes |
|---|---|---|
ATTACHMENT_STORAGE | local | local (filesystem, bytes proxied through the api) or s3 (presigned S3-compatible). |
ATTACHMENT_LOCAL_PATH | ./data/attachments | Where the local driver writes bytes (Compose persists it in a volume). |
ATTACHMENT_MAX_BYTES | 26214400 (25 MiB) | Max bytes per attachment. |
ATTACHMENT_MAX_PER_CARD | 20 | Max non-archived attachments per card. |
ATTACHMENT_CONTENT_TYPES | built-in allowlist | Comma-separated content-type allowlist. Unset uses the default (images, PDF, text/CSV, common office docs, zip). |
ATTACHMENT_MAX_BYTES_PER_BOARD / ATTACHMENT_MAX_BYTES_TOTAL | 1073741824 / 10737418240 | Aggregate byte quotas per board and global. |
ATTACHMENT_PENDING_TTL_SECONDS / ATTACHMENT_SWEEP_INTERVAL_MS | 3600 / 600000 | How long a pending (unconfirmed) upload lives before the sweep archives it, and how often the sweep runs. |
S3 / MinIO (only when ATTACHMENT_STORAGE=s3)
| Variable | Default | Notes |
|---|---|---|
S3_BUCKET | kanban-attachments | Target bucket. |
S3_REGION | us-east-1 | Region. |
S3_ENDPOINT | — | Set for MinIO / non-AWS backends (enables path-style addressing). |
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY | — | Credentials. Default to minioadmin/minioadmin for the bundled MinIO profile. |
local is a zero-config filesystem driver — ideal for a single host. s3 issues presigned PUT/GET URLs so bytes never transit the api; downloads 302-redirect to a short-TTL signed URL. Server-side limits (size, per-card count, content-type allowlist) are enforced on every surface regardless of driver. The repo ships an optional MinIO profile for exercising the s3 path locally: docker compose --profile storage-s3 up -d minio minio-setup.
Compose-only
These drive the local Docker Compose services (each has a default in docker-compose.yml).
| Variable | Default | Notes |
|---|---|---|
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB / POSTGRES_PORT | kanban / kanban / kanban / 5432 | Backing Postgres. |
REDIS_PORT | 6379 | Backing Redis. |
WEB_PORT | 8080 | Host port the SPA (nginx) is published on. |
VITE_API_PROXY_TARGET | http://localhost:3000 | Where the Vite dev server proxies /api + /ws. |