Configuration
Every setting is an environment variable, and they fall into three tiers. All of them are validated when the server starts, and a start that fails lists every problem rather than the first one.
To see what a running instance is configured for, call GET /api/config-report as a global
administrator. It reports state, never values.
On Cloudflare, every credential below is a Worker secret rather than a plain variable. The Cloudflare page lists which is which.
Tier one: required always
Section titled “Tier one: required always”The server refuses to serve without these.
| Variable | Purpose |
|---|---|
APP_URL |
The public base URL. Every link in every email, every OAuth redirect and every webhook registration is built from it |
OAUTH_ENCRYPTION_KEY |
A 32-byte secret. Encrypts stored OAuth access and refresh tokens at rest |
DATABASE_URL |
The Postgres connection string. On Cloudflare, pooling comes from the Hyperdrive binding, but this is still required: survey access links are signed from it |
There are deliberately only three, so a failure to start has a small blast radius.
Runtime settings
Section titled “Runtime settings”| Variable | Default | Purpose |
|---|---|---|
PORT |
3000 |
The port the server listens on |
HOST |
0.0.0.0 |
The interface it binds |
NODE_ENV |
development |
Set to production on any real deployment |
RELEASE_VERSION |
dev |
What this deployment calls itself, at GET /api/version and to every CLI that talks to it. dev reads as unknown and is never reported to a user |
LOG_LEVEL |
info |
Log verbosity. Silent during tests regardless |
DATABASE_URL_DIRECT |
– | A connection that bypasses a pooler. Set it when PgBouncer or an equivalent sits in front of Postgres |
Compose-only settings
Section titled “Compose-only settings”Read by the Docker Compose stack itself rather than by the server, so they do nothing on Kubernetes or Cloudflare.
| Variable | Default | Purpose |
|---|---|---|
SERVER_REPLICAS |
3 |
How many server instances to run behind the load balancer |
MINIO_CONSOLE_PORT |
9001 |
Host port for the bundled object store’s console. Change it if something else on the host already uses 9001 |
POSTGRES_PASSWORD |
guidemode |
The bundled database’s password. Unused once DATABASE_URL is set |
Tier two: groups, whole or absent
Section titled “Tier two: groups, whole or absent”Each group is either fully configured or entirely absent. Half of one stops the server at boot, naming what is missing, rather than failing in a callback a week later.
Object store
Section titled “Object store”| Variable | Notes |
|---|---|
S3_BUCKET |
Presence of this turns the group on |
S3_ENDPOINT |
Full origin, such as https://s3.eu-west-1.amazonaws.com or http://minio:9000 |
S3_ACCESS_KEY_ID |
|
S3_SECRET_ACCESS_KEY |
|
S3_REGION |
Optional. Defaults to auto |
S3_ADDRESSING |
Optional, path or virtual-host. Guessed from the endpoint, and worth stating: MinIO uses path, AWS uses virtual-host, and the wrong choice returns an error that reads like a permissions problem |
Without this group there is no transcript storage. Everything else still works.
GitHub App
Section titled “GitHub App”Repository, issue and pull request sync.
| Variable | Notes |
|---|---|
GITHUB_APP_ID |
Presence of this turns the group on |
GITHUB_APP_PRIVATE_KEY |
|
GITHUB_APP_WEBHOOK_SECRET |
Optional. Without it, webhooks are not accepted |
GITHUB_APP_NAME |
Optional |
Sign-in providers
Section titled “Sign-in providers”Each is a pair, or a trio where a redirect URI is required.
| Provider | Variables |
|---|---|
| GitHub | GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET |
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI |
|
| GitLab | GITLAB_CLIENT_ID, GITLAB_CLIENT_SECRET, GITLAB_REDIRECT_URI |
| Microsoft | MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRET |
Email sign-in works without any of these, provided RESEND_API_KEY is set.
Work tracking integrations
Section titled “Work tracking integrations”| Integration | Variables |
|---|---|
| Jira | JIRA_CLIENT_ID, JIRA_SECRET, JIRA_REDIRECT_URI |
| Linear | LINEAR_CLIENT_ID, LINEAR_CLIENT_SECRET |
| Notion | NOTION_CLIENT_ID, NOTION_CLIENT_SECRET |
| Slack | SLACK_CLIENT_ID, SLACK_CLIENT_SECRET |
Billing and tracing
Section titled “Billing and tracing”Both are switched on by a flag rather than by a credential, so a deployment carrying price identifiers with billing off is read correctly as off.
| Group | Variables |
|---|---|
| Paddle | PADDLE_ENABLED, PADDLE_API_KEY, PADDLE_WEBHOOK_SECRET, PADDLE_CLIENT_TOKEN, PADDLE_INDIVIDUAL_PRICE_ID, PADDLE_SMALL_TEAM_PRICE_ID, PADDLE_LARGE_TEAM_PRICE_ID, PADDLE_ENTERPRISE_PRICE_ID |
| Langfuse | LANGFUSE_ENABLED, LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, and optionally LANGFUSE_BASE_URL |
Most self-hosted deployments leave billing off.
Tier three: absent means off
Section titled “Tier three: absent means off”| Variable | Effect |
|---|---|
RESEND_API_KEY |
Email. Without it, invitations and magic links are not sent |
NOTION_WEBHOOK_SECRET |
Notion webhooks are rejected without it |
SLACK_SIGNING_SECRET |
Verifies inbound Slack requests |
GITHUB_PUBLIC_TOKEN |
Read-only token for the open-source benchmark corpus |
ADMIN_SECRET |
Enables global administrator access |
AI analysis
Section titled “AI analysis”Session summaries, quality assessment and the agentic notebook.
| Variable | Notes |
|---|---|
AI_PROVIDER |
Which provider to use by default |
AI_MODELS |
Comma-separated model list, tried in order |
GEMINI_API_KEY, OPENAI_API_KEY, CLAUDE_API_KEY |
Provider credentials |
ANTHROPIC_API_KEY |
Used by the agentic notebook, separately from the above |
AI_PROMPT_LOGGING |
Logs every prompt and response. Development only |
Organizations can also configure their own provider and key in the product, which takes precedence over these.
Background work
Section titled “Background work”Sensible on every deployment; worth tuning on a large one.
| Variable | Default | Notes |
|---|---|---|
JOBS_WORKER_ENABLED |
true |
Set false for a replica that serves traffic and never claims background work |
JOBS_POLL_INTERVAL_MS |
1000 |
How often a worker looks for work |
JOBS_QUEUE_CONCURRENCY |
2 |
Per replica, per queue |
JOBS_WORKFLOW_CONCURRENCY |
2 |
Per replica |
JOBS_RETENTION_DAYS |
7 |
How long finished job rows are kept |
JOBS_SHUTDOWN_GRACE_MS |
20000 |
How long a draining replica has to finish what it holds |
Development only
Section titled “Development only”| Variable | Notes |
|---|---|
LOCAL_OBJECT_STORE_DIR |
Where transcripts are written when no S3 bucket is configured and this is not production. Refused in production, where a missing bucket means no transcript storage |
