CONFIGURATION.md
Rama main · réplica de lectura del repositorio original.
# Configuration Reference
All configuration is via environment variables, set in `docker-compose.yml` or your platform's equivalent. The backend runs as either `./server` (web) or `./server worker` (background analytics). Which mode each variable applies to is noted per section.
See [`backend/.env.example`](backend/.env.example) for a copy-paste-ready template.
---
## Core Settings
*Applies to: web server*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `PORT` | `8080` | No | HTTP server port |
| `BACKEND_URL` | `http://localhost:8080` | No | Public URL of the backend; used for CLI device-code authorization flow |
| `FRONTEND_URL` | `http://localhost:5173` | Yes | Public URL of the frontend; used for OAuth redirects, email links, CORS, and the absolute link-preview image URL |
| `ALLOWED_ORIGINS` | `http://localhost:5173` | Yes | Comma-separated list of allowed CORS origins |
| `STATIC_FILES_DIR` | `/app/static` | No | Directory for serving the built frontend |
| `CSRF_SECRET_KEY` | *(none)* | Yes | CSRF protection secret; must be at least 32 characters |
| `INSECURE_DEV_MODE` | `false` | No | Set to `true` to disable secure-cookie requirements (local dev without HTTPS) |
| `TRUSTED_PROXY_HEADERS` | *(all)* | No | Comma-separated list of proxy header names to honor for client-IP extraction (e.g. `Fly-Client-IP,CF-Connecting-IP`). When unset, all known proxy headers are trusted. Set this to only the header your edge proxy sets so attackers can't spoof other headers to bypass IP rate limits. |
## Database
*Applies to: web server and worker*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `DATABASE_URL` | *(none)* | Yes | PostgreSQL connection string (e.g. `postgres://user:pass@host:5432/confab?sslmode=disable`) |
| `MIGRATE_DATABASE_URL` | Falls back to `DATABASE_URL` | No | Separate connection string for running migrations with an elevated database user |
| `DB_CONN_MAX_IDLE_TIME` | *(unset — no limit)* | No | Maximum time a pooled connection may sit idle before being closed. Setting this (e.g. `1m`) lets managed Postgres providers with autosuspend (e.g. Neon) actually suspend compute between bursts of activity. Leave unset for self-hosted plain Postgres, which has no autosuspend to earn back. |
## Storage
*Applies to: web server and worker*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `S3_ENDPOINT` | *(none)* | Yes | S3/MinIO endpoint (e.g. `localhost:9000`) |
| `AWS_ACCESS_KEY_ID` | *(none)* | Yes | S3/MinIO access key |
| `AWS_SECRET_ACCESS_KEY` | *(none)* | Yes | S3/MinIO secret key |
| `BUCKET_NAME` | *(none)* | Yes | S3/MinIO bucket name |
| `S3_USE_SSL` | `true` | No | Use SSL for S3 connections; set to `false` for local MinIO |
## Authentication
*Applies to: web server*
At least one method must be enabled. All four can be used simultaneously.
### Password Auth
Recommended for self-hosted deployments.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `AUTH_PASSWORD_ENABLED` | `false` | No | Set to `true` to enable username/password login |
| `ADMIN_BOOTSTRAP_EMAIL` | *(none)* | If password auth enabled | Email for the initial admin user (created on first startup if no users exist) |
| `ADMIN_BOOTSTRAP_PASSWORD` | *(none)* | If password auth enabled | Password for the initial admin user, 8–72 bytes; remove after setup |
### GitHub OAuth
Create an OAuth app at [github.com/settings/developers](https://github.com/settings/developers).
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `GITHUB_CLIENT_ID` | *(none)* | If GitHub OAuth enabled | GitHub OAuth app client ID |
| `GITHUB_CLIENT_SECRET` | *(none)* | If GitHub OAuth enabled | GitHub OAuth app client secret |
| `GITHUB_REDIRECT_URL` | *(none)* | If GitHub OAuth enabled | OAuth callback URL (e.g. `https://your-domain/auth/github/callback`) |
### Google OAuth
Create OAuth credentials at [console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials).
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `GOOGLE_CLIENT_ID` | *(none)* | If Google OAuth enabled | Google OAuth client ID |
| `GOOGLE_CLIENT_SECRET` | *(none)* | If Google OAuth enabled | Google OAuth client secret |
| `GOOGLE_REDIRECT_URL` | *(none)* | If Google OAuth enabled | OAuth callback URL (e.g. `https://your-domain/auth/google/callback`) |
### Generic OIDC
Works with Okta, Auth0, Azure AD, Keycloak, etc. All four variables must be set.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `OIDC_ISSUER_URL` | *(none)* | If OIDC enabled | OIDC issuer URL (e.g. `https://dev-12345.okta.com`) |
| `OIDC_CLIENT_ID` | *(none)* | If OIDC enabled | OIDC client ID |
| `OIDC_CLIENT_SECRET` | *(none)* | If OIDC enabled | OIDC client secret |
| `OIDC_REDIRECT_URL` | *(none)* | If OIDC enabled | OAuth callback URL (e.g. `https://your-domain/auth/oidc/callback`) |
| `OIDC_DISPLAY_NAME` | `SSO` | No | Controls the login button text ("Continue with ...") |
### Shared Auth Settings
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `ALLOWED_EMAIL_DOMAINS` | *(all domains)* | No | Comma-separated list of allowed email domains; applies to all auth methods |
| `OAUTH_AUTO_LINK_EMAIL` | `false` | No | When `true`, a first-time OAuth login whose email matches an existing account (password or another provider) is automatically linked to it. **Default `false`** rejects the login (`/login?error=account_exists`) instead, preventing account takeover via an attacker-controlled IdP email. Only enable if you trust every configured IdP to strictly verify email ownership. Returning users and brand-new emails are unaffected. |
### Demo Mode (CF-483)
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `DEMO_IDENTITY_EMAIL` | *(none)* | No | When set, designates a single user as the **read-only demo identity**. On startup the named user is provisioned with `read_only=true`, name `"Demo"`, `is_admin=false`, and any password identity is stripped (login disabled). Anonymous web visitors on auth-required routes are auto-impersonated as the demo identity via a single shared session cookie (HMAC-derived from `CSRF_SECRET_KEY`). Mutating requests from the demo identity return `403 {"error":"read_only_user", ...}`. The login handler and all OAuth callbacks reject this email. Real users (with their own password / OAuth) continue to authenticate and write normally. **Unset = zero behavior change anywhere** — safe to leave off in regular deployments. |
## Email
*Applies to: web server*
Email is enabled when both `RESEND_API_KEY` and `EMAIL_FROM_ADDRESS` are set.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `RESEND_API_KEY` | *(none)* | If email enabled | Resend API key ([resend.com](https://resend.com)) |
| `EMAIL_FROM_ADDRESS` | *(none)* | If email enabled | Sender email address |
| `EMAIL_FROM_NAME` | `Confabulous Code` | No | Sender display name |
| `EMAIL_RATE_LIMIT_PER_HOUR` | `100` | No | Per-user email rate limit |
| `SUPPORT_EMAIL` | *(none)* | No | Support email shown in UI |
## Smart Recaps
*Applies to: web server and worker*
AI-powered session summaries, generated by Anthropic or OpenAI. Requires an [Anthropic API key](https://console.anthropic.com/) or an [OpenAI API key](https://platform.openai.com/api-keys) for the selected provider.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `SMART_RECAP_ENABLED` | `false` | No | Set to `true` to enable smart recaps |
| `SMART_RECAP_LLM_PROVIDER` | `anthropic` | No | LLM provider for recap generation: `anthropic` or `openai`. Any other value fails startup, even when smart recaps are disabled. |
| `ANTHROPIC_API_KEY` | *(none)* | If provider is `anthropic` | Anthropic API key |
| `OPENAI_API_KEY` | *(none)* | If provider is `openai` | OpenAI API key |
| `SMART_RECAP_MODEL` | *(none)* | If smart recaps enabled | Model for the selected provider (e.g. `claude-haiku-4-5-20251001` or `gpt-6-luna`) |
| `SMART_RECAP_QUOTA_LIMIT` | `0` (unlimited) | No | Per-user monthly generation cap. Positive integer enforces a limit; `0` or omitted means unlimited. |
| `SMART_RECAP_MAX_OUTPUT_TOKENS` | `1000` | No | Maximum LLM output tokens per recap |
| `SMART_RECAP_MAX_TRANSCRIPT_TOKENS` | `50000` | No | Maximum input tokens per transcript (~chars/4) |
If `SMART_RECAP_ENABLED=true` but the selected provider's API key or `SMART_RECAP_MODEL` is missing, smart recaps stay disabled and the server and worker log a startup warning naming the missing variable. Switching providers does not regenerate existing recaps.
With the `anthropic` provider, smart recaps use structured outputs with extended thinking disabled. Supported models are Claude Haiku 4.5, Sonnet 5, Opus 4.8, and Opus 5. Fable and Mythos models (thinking always on) are not supported for smart recaps.
## Admin & User Management
*Applies to: web server*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `SUPER_ADMIN_EMAILS` | *(none)* | No | Comma-separated email addresses with admin panel access. Admin authorization is the **union** of this list and the per-user `is_admin` column (5k4v) — env super-admins are always admins (recovery path); other admins can be granted/revoked at runtime from the admin UI without an env edit + restart. Validated at startup: malformed/duplicate entries are logged as warnings and the normalized list is logged. Keep any demo identity's email out of this list. |
| `MAX_USERS` | `50` | No | Maximum number of registered users; set to `0` to block new registrations |
| `SESSION_IDLE_TIMEOUT` | `48h` | No | Sliding idle timeout for web sessions (`time.ParseDuration` format, e.g. `48h`, `30m`). A session inactive longer than this is rejected even within the 7-day absolute cap. Invalid/empty/non-positive values fall back to the default. |
## Instance Customization
*Applies to: web server*
Sharing behavior and UI customization.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `SHARE_ALL_SESSIONS_TO_AUTHENTICATED` | `false` | No | Make every session visible to every authenticated user; useful for small teams that want full transparency |
| `ENABLE_SHARE_CREATION` | `false` | No | Enable share link creation |
| `SHARE_DAILY_QUOTA` | `100` | No | Per-user cap on shares created in a rolling 24h window; the creation endpoint returns 429 once a user exceeds it. Set to `0` to disable the cap. |
| `ENABLE_ORG_ANALYTICS` | `false` | No | Expose org-wide per-user analytics (`/admin/...`) to every authenticated user — same visibility model as `SHARE_ALL_SESSIONS_TO_AUTHENTICATED`. See [Organization Analytics in backend/API.md](backend/API.md#organization-analytics) for the privacy implications. |
| `ENABLE_SAAS_FOOTER` | `false` | No | Show the SaaS footer (GitHub, Discord, Help links, copyright) and serve the code.confabulous.dev legal pages (`/policies`, `/terms`, `/privacy`, `/cookies`, `/disclaimer`, `/acceptable-use`; they 404 when off); off by default for self-hosted |
| `DISABLE_UPDATE_CHECK` | `false` | No | Suppress the in-product "Update available" badge (skips the periodic GitHub release check). Useful for air-gapped deployments. Implicitly `true` when `ENABLE_SAAS_FOOTER=true`, since SaaS users can't self-upgrade. |
## Model Pricing
*Applies to: web server, worker*
Model token prices are no longer baked into the build — the backend pulls the latest price table from code.confabulous.dev so a self-hosted instance picks up new prices (and new models within an existing provider) without a redeploy. The fetch is best-effort: if the source is unreachable, invalid, or older than the build's embedded table, the embedded table is used. The canonical SaaS instance auto-disables this (it is the source).
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `PRICING_SOURCE_URL` | `https://code.confabulous.dev/api/v1/pricing` | No | Where to pull the latest model price table from. Set to empty (`PRICING_SOURCE_URL=`) to disable fetching and use the embedded table only (air-gapped deployments). |
| `PRICING_REFRESH_INTERVAL` | `2h` | No | How often to refresh the price table (Go duration, e.g. `2h`, `90m`). Failures are retried after 15 minutes. |
## Worker
*Applies to: worker only*
Precomputes analytics and smart recaps in the background.
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `WORKER_POLL_INTERVAL` | `30m` | No | How often to check for stale sessions |
| `WORKER_MAX_SESSIONS` | `20` | No | Maximum sessions to process per cycle |
| `WORKER_MAX_SEARCH_INDEX_SESSIONS` | `200` | No | Maximum sessions to (re)build the search index for per cycle (search indexing is cheap, so this is higher than `WORKER_MAX_SESSIONS`) |
| `WORKER_DRY_RUN` | `false` | No | Log what would be done without actually processing |
| `WORKER_SHARE_RETENTION` | `720h` | No | Each cycle, physically delete shares that have been expired longer than this. Use Go duration units (h/m/s only — `720h` = 30 days; `30d` is **not** accepted). Skipped in dry-run. |
### Staleness Thresholds (Advanced)
Controls when sessions need recomputation. `WORKER_REGULAR_*` = analytics cards, `WORKER_RECAP_*` = smart recaps.
| Variable | Default | Description |
|----------|---------|-------------|
| `WORKER_REGULAR_THRESHOLD_PCT` | `0.20` | Percentage change (0–1) to trigger analytics recompute |
| `WORKER_REGULAR_BASE_MIN_LINES` | `5` | Minimum new lines before recompute |
| `WORKER_REGULAR_BASE_MIN_TIME` | `3m` | Minimum age of new data |
| `WORKER_REGULAR_MIN_INITIAL_LINES` | `10` | Minimum lines for first computation |
| `WORKER_REGULAR_MIN_SESSION_AGE` | `10m` | Minimum session age |
| `WORKER_RECAP_THRESHOLD_PCT` | `0.20` | Percentage change (0–1) to trigger recap recompute |
| `WORKER_RECAP_BASE_MIN_LINES` | `150` | Minimum new lines before recompute |
| `WORKER_RECAP_BASE_MIN_TIME` | `30m` | Minimum age of new data |
| `WORKER_RECAP_MIN_INITIAL_LINES` | `25` | Minimum lines for first computation |
| `WORKER_RECAP_MIN_SESSION_AGE` | `10m` | Minimum session age |
## Observability
*Applies to: web server and worker*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `LOG_LEVEL` | `info` | No | Log level: `debug`, `info`, `warn`, `error` |
| `ENABLE_PPROF` | `false` | No | Enable pprof profiling server on `localhost:6060` |
## HTTP Tuning
*Applies to: web server*
| Variable | Default | Required | Description |
|----------|---------|----------|-------------|
| `HTTP_READ_TIMEOUT` | `30s` | No | HTTP read timeout |
| `HTTP_WRITE_TIMEOUT` | `30s` | No | HTTP write timeout |