Skip to content

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

VariableUsed byDefaultNotes
DATABASE_URLapi, mcppostgres://kanban:kanban@localhost:5432/kanbanPostgres connection string. Compose builds it from the POSTGRES_* vars.
REDIS_URLapi, mcpredis://localhost:6379Event bus + rate-limit + session store.
SESSION_SECRETapidev fallbackSigns 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_SECRETSapiunsetSet true to boot with the insecure fallback secret in local dev. Never in production.
PORTapi / mcp3000 / 3001HTTP listen port.
NODE_ENVapi, mcpdevelopmentproduction | development | test. Governs secret-strength enforcement and the default registration policy.
LOG_LEVELapi, mcpinfopino level: trace | debug | info | warn | error.
PG_POOL_MAXapi10Max Postgres pool size (postgres-js max). Tune to your DB's connection budget × process count.
TRUST_PROXYapi, mcpfalseTrust 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

VariableDefaultNotes
CORS_ORIGINShttp://localhost:5173,http://localhost:8080Comma-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_URLhttp://localhost:3000Public origin of the deployment — the OAuth issuer base and the root of every URL advertised in /.well-known/oauth-authorization-server.
OAUTH_ISSUER_URLPUBLIC_BASE_URLThe OAuth 2.1 issuer identifier. Set explicitly if the issuer differs from PUBLIC_BASE_URL.
MCP_PUBLIC_URLhttp://localhost:3001/mcpThe 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

VariableDefaultNotes
REGISTRATION_MODEprod → closed; dev/test → invite if a code is set, else openclosed (register returns 403) | invite (requires the code below) | open. Fails closed on ambiguity.
REGISTER_INVITE_CODEThe 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.
VariableDefaultNotes
SESSION_IDLE_TTL_MS604800000 (7 d)Idle TTL — slides forward on each request.
SESSION_ABSOLUTE_TTL_MS2592000000 (30 d)Absolute cap regardless of activity.
SESSION_STALE_MINUTES30An agent work session with no events for this long renders a "stale" badge (derived, never stored).
VariableDefaultNotes
SEARCH_TSVECTOR_CONFIGenglishPostgres 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

VariableDefaultNotes
WEBHOOK_MAX_PER_BOARD20Max webhook subscriptions per board (amplification bound).
WEBHOOK_DELIVERY_SWEEP_INTERVAL_MS3600000 (1 h)How often the background sweep retries/expires pending deliveries.
WEBHOOK_ALLOW_PRIVATE_TARGETSoffSSRF 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)

VariableDefaultNotes
AUTOMATION_MAX_PER_BOARD32Max automation rules per board.
AUTOMATION_RULE_RATE_LIMIT30Runs/minute before a rule auto-pauses to break feedback loops.

Rate limiting

VariableDefaultNotes
RATE_LIMIT_AUTH_MAX10Pre-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_MAX1000Outer per-IP request cap enforced in memory before any DB-backed auth lookup.
MCP_RATE_LIMIT_TOKEN_MAX120Per-token authenticated MCP call limit (requests/window). Shared bucket with the REST api.
MCP_RATE_LIMIT_WINDOW_MS60000The MCP rate-limit window.
MCP_PREAUTH_IP_MAX / MCP_PREAUTH_WINDOW_MS300 / 60000Pre-auth per-IP flood cap on the MCP server.

WebSocket gateway

All optional; defaults shown.

VariableDefaultNotes
WS_REVALIDATE_INTERVAL_MS60000How often each live connection re-checks board membership + token expiry.
WS_MAX_LIFETIME_MS14400000 (4 h)Max lifetime of one WS connection before it closes and the client reconnects.
WS_TICKET_TTL_MS30000Lifetime of a single-use WS handshake ticket (POST /api/ws-ticket).
WS_MAX_PAYLOAD_BYTES16384Max inbound frame size; oversized frames close the socket.
WS_MSG_RATE_PER_SEC / WS_MSG_BURST20 / 40Per-socket message rate limit (token bucket).
WS_MAX_SUBSCRIPTIONS32Max concurrent board subscriptions per connection.
WS_MAX_CONNECTIONS2048Max concurrent connections per api instance (excess closed 1013).

MCP

VariableDefaultNotes
MCP_PORT3001Compose host port for the mcp service.
KANBAN_MCP_LEGACY_TOOLSoffSet 1 to expose the deprecated flat MCP tool catalog alongside the v2 consolidated tools (one deprecation release).

Integrations

VariableDefaultNotes
INTEGRATION_SECRET_KEY32-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

VariableDefaultNotes
ATTACHMENT_STORAGElocallocal (filesystem, bytes proxied through the api) or s3 (presigned S3-compatible).
ATTACHMENT_LOCAL_PATH./data/attachmentsWhere the local driver writes bytes (Compose persists it in a volume).
ATTACHMENT_MAX_BYTES26214400 (25 MiB)Max bytes per attachment.
ATTACHMENT_MAX_PER_CARD20Max non-archived attachments per card.
ATTACHMENT_CONTENT_TYPESbuilt-in allowlistComma-separated content-type allowlist. Unset uses the default (images, PDF, text/CSV, common office docs, zip).
ATTACHMENT_MAX_BYTES_PER_BOARD / ATTACHMENT_MAX_BYTES_TOTAL1073741824 / 10737418240Aggregate byte quotas per board and global.
ATTACHMENT_PENDING_TTL_SECONDS / ATTACHMENT_SWEEP_INTERVAL_MS3600 / 600000How long a pending (unconfirmed) upload lives before the sweep archives it, and how often the sweep runs.

S3 / MinIO (only when ATTACHMENT_STORAGE=s3)

VariableDefaultNotes
S3_BUCKETkanban-attachmentsTarget bucket.
S3_REGIONus-east-1Region.
S3_ENDPOINTSet for MinIO / non-AWS backends (enables path-style addressing).
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEYCredentials. 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).

VariableDefaultNotes
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB / POSTGRES_PORTkanban / kanban / kanban / 5432Backing Postgres.
REDIS_PORT6379Backing Redis.
WEB_PORT8080Host port the SPA (nginx) is published on.
VITE_API_PROXY_TARGEThttp://localhost:3000Where the Vite dev server proxies /api + /ws.

Released under the AGPL-3.0 license.