Complete reference for all environment variables consumed by the Go backend. The authoritative source is internal/app/env-setup.go (API server) and internal/app/env-setup-worker.go (eventsync worker).
Phase 0 note: per-variable descriptions, valid values, and environment-specific requirements to be filled in during baseline generation.
Environment groups
| Group | Env struct | Purpose |
|---|
| Core | — | Runtime environment label (local, dev, prod) |
| Cookie | CookieEnv | Session cookie signing and encryption keys, domain, Gin mode |
| Website | WebsiteEnv | Frontend base URL (used for redirect URIs) |
| LiveKit | LiveKitEnv | LiveKit Cloud API key/secret, server URL, project and region |
| WorkOS | WorkOSEnv | AuthKit API key, client ID, SSO/OAuth redirect URIs, webhook secret |
| WebSocket | WebSocketEnv | Office-router Worker base URL, JWT issuer/audience/private key |
| R2 Files | R2Env | Cloudflare R2 credentials and bucket for recording temp storage |
| R2 Avatars | R2AvatarEnv | Separate R2 bucket and public URL for user/org avatar storage |
| Cloudflare | CloudflareEnv | Cloudflare Worker URL (office-router) |
| Google | GoogleEnv | Google OAuth credentials (Calendar integration) |
| Avatar | AvatarEnv | Avatar processing config |
| Org Logo | OrgLogoEnv | Org logo processing config |
| Recording | RecordingEnv | Recording pipeline config |
| Grafana | GrafanaEnv | Grafana Mimir push endpoint and credentials |
| Supabase | SupabaseEnv | JWT secret (used to validate Supabase-issued tokens) |
| Webhook | WebhookEnv | Inbound webhook signing secret (WorkOS events) |
| CORS | CORSEnv | Per-environment browser origin allowlist for credentialed CORS |
| Sentry | SentryEnv | Error reporting DSN, release tag, and performance-tracing sample rate |
Worker-specific overrides
The eventsync worker binary reads the same env file as the API server but supports one override:
WORKER_POSTGRES_URL — if set, replaces POSTGRES_URL for the worker process. Used to point the worker at a dedicated database while the API server continues using the main one.
Notes
- All secrets (keys, signing secrets, credentials) are managed as org-level GitHub Actions secrets and injected at deploy time by Sevalla. No secrets are committed to the repository.
- Local development uses a
.env file loaded by godotenv. The file is gitignored; obtain values from the shared secrets store.
- The
GIN_MODE variable controls Gin's behaviour: debug locally, release in production. Setting debug in production degrades performance and enables verbose output.
Variable reference
Audited from internal/app/env-setup.go (API) and the worker loader against actual read sites. Req = required (boot fails if missing); Opt = optional, with the default shown. Two binaries load different subsets: the API (cmd/api) loads the full set via LoadEnvVariables(); the worker (cmd/worker) loads a minimal subset via LoadWorkerEnvVariables().
Loader invariant: LoadEnvVariables() contains only variables that are both required and actually used — no dead loads. Optional knobs with a safe built-in default are the one exception. Per-environment values (local / staging / prod) live with the run dev tooling, not here; this section documents meaning, location, and necessity.
App / runtime
| Variable | Req | Meaning | Read at |
|---|
APP_ENV | Opt (prod) | Environment name (local/development/staging/prod). Gates pilot-invite endpoints and prod-only logging sinks. | env-setup.go, cmd/api/main.go, internal/app/slog-setup.go |
GIN_MODE | Req | Gin mode (debug/release/test). release flips cookies to secure/prod behaviour. | pkg/token/manager.go |
APP_DOMAIN | Req | Cookie domain for the auth cookie. | pkg/token |
Cookies & token encryption
| Variable | Req | Meaning | Read at |
|---|
COOKIE_HASH_KEY | Req | HMAC key for signed cookies (securecookie). | pkg/token |
COOKIE_BLOCK_KEY | Req | AES key for encrypted cookies (securecookie). | pkg/token |
TOKEN_ENCRYPTION_KEY_V1 | Req | Hex-encoded AES-256 key (must decode to 32 bytes) for encrypting refresh tokens at rest in user_sessions / user_integrations. | pkg/token/manager.go, pkg/token/aead.go |
Frontend / WorkOS auth
| Variable | Req | Meaning | Read at |
|---|
WEBSITE_BASE_URL | Req | Public frontend base URL; used to build invite links. | internal/features/user/service/generate_pilot_invite.go, internal/features/membership/service/invitelink.go |
WORKOS_API_KEY | Req | WorkOS server API key. | setupWorkOS → WorkOS SDK |
WORKOS_CLIENT_ID | Req | WorkOS client id; also the expected JWT audience/issuer for access-token validation. | setupWorkOS, token validator |
WORKOS_SSO_REDIRECT_URI | Req | Sign-in SSO callback (enterprise/SSO flow). | internal/features/authentication/service/discoverauth.go, oauth.go |
WORKOS_REDIRECT_URI_WEB | Req | Sign-in callback for web social auth (Google/Microsoft via web client). Distinct flow from the SSO callback. | internal/features/authentication/service/oauth.go |
WORKOS_INTEGRATIONS_REDIRECT_URI | Req | OAuth callback for Google Calendar integration connect/finalize. | internal/features/calendar/google/service/connectcalendar.go, finalizecalendar.go |
WORKOS_FAKE_LOGOUT_REDIRECT_URI | Req | Post-logout ReturnTo: where WorkOS sends the browser after sign-out. Must be a user-facing frontend URL. | internal/features/session/service/logout.go |
Logout wiring note. WORKOS_FAKE_LOGOUT_REDIRECT_URI is the logout ReturnTo. It currently points at a placeholder GET /login stub (cmd/api/main.go); replace the stub (and consider renaming the variable to WORKOS_LOGOUT_REDIRECT_URI) once a real logged-out page exists.
LiveKit
| Variable | Req | Meaning | Read at |
|---|
LIVEKIT_API_KEY | Req | LiveKit Cloud API key. | setupLivekit → LiveKit SDK |
LIVEKIT_API_SECRET | Req | LiveKit Cloud API secret. | setupLivekit |
LIVEKIT_URL | Req | LiveKit Cloud websocket URL. | setupLivekit |
LIVEKIT_PROJECT | Opt (default) | Project label applied to Prometheus metrics and PromQL analytics queries. | pkg/metrics/livekit.go, internal/features/analytics/service/panels.go |
LIVEKIT_REGION | Opt (global) | Region label, same usage as project. | same as above |
CORS
| Variable | Req | Meaning | Read at |
|---|
CORS_ALLOWED_ORIGINS | Req | Comma-separated, exact-match browser origin allowlist (scheme+host+port, no trailing slash). Because auth uses credentialed (cookie) CORS, * is forbidden and an empty/unset list fails closed at boot. | internal/app/env-setup.go (getRequiredCSV) → pkg/middleware/cors.go |
Cloudflare R2 (storage)
| Variable | Req | Meaning | Read at |
|---|
R2_ACCESS_KEY | Req | R2 access key (S3-compatible). | setupR2Config |
R2_SECRET_KEY | Req | R2 secret key. | setupR2Config |
R2_REGION | Opt (auto) | Region passed to the AWS SDK config. | internal/platform/cloudflare/r2/client.go |
R2_ENDPOINT | Req | R2 S3 endpoint. | setupR2Config |
R2_BUCKET | Req | General storage bucket (recordings, etc.). | setupR2Config |
R2_PUBLIC_BASE_URL | Req | Public base URL used to build object URLs. | internal/platform/cloudflare/r2/client.go, internal/features/shared/imageupload/service.go |
R2_AVATAR_BUCKET | Req | Separate bucket for avatars/logos. | setupR2AvatarClient |
R2_AVATAR_PUBLIC_BASE_URL | Req | Public base URL for avatar/logo objects. | avatar client |
R2_STORAGE_RETENTION_DAYS | Opt (90) | Days an R2 recording file is retained before storage expiry. | recording service |
R2_FORCE_PATH_STYLE | Opt (false) | Path-style S3 addressing for the LiveKit egress upload. false (prod) = virtual-hosted; the dev stack sets true because MinIO on a localhost endpoint has no bucket-subdomain DNS. Egress-only. | setupR2Config → internal/platform/livekit/client.go |
Google
| Variable | Req | Meaning | Read at |
|---|
GOOGLE_CLIENT_ID | Req | Google OAuth client id (Calendar integration). | setupGoogle |
GOOGLE_CLIENT_SECRET | Req | Google OAuth client secret. | setupGoogle |
Grafana Mimir (customer-facing analytics)
| Variable | Req | Meaning | Read at |
|---|
GRAFANA_MIMIR_URL | Req | Mimir query endpoint. | internal/features/analytics/service/query_panel.go |
GRAFANA_MIMIR_INSTANCE_ID | Req | Mimir tenant/instance id. | internal/platform/grafana/mimir/client.go |
GRAFANA_MIMIR_API_TOKEN | Req | Mimir API token. | internal/platform/grafana/mimir/client.go |
Supabase / webhooks / uploads
| Variable | Req | Meaning | Read at |
|---|
SUPABASE_JWT_SECRET | Req | HS256 secret to sign Supabase Realtime JWTs. | internal/features/realtime/service/generatetoken.go |
WEBHOOK_SUPABASE_SECRET | Req | Shared secret verified (constant-time) on inbound webhook calls (X-Webhook-Secret). | internal/features/recording/api/reconcile_handler.go |
AVATAR_MAX_UPLOAD_TRIES | Opt (3) | Per-user avatar upload attempt cap. | internal/features/user/service/avatar.go |
ORG_LOGO_MAX_UPLOAD_TRIES | Opt (5) | Per-org logo upload attempt cap. | internal/features/organization/service/logo.go |
RECORDING_ACCESS_RETENTION_DAYS | Opt (20) | Days a recording is accessible in-app before access expiry. | recording service |
Sentry / error reporting
| Variable | Req | Meaning | Read at |
|---|
SENTRY_DSN | Opt ("") | Sentry project ingest URL. Empty installs a no-op transport (events dropped, no network) — the intended local-dev default. | internal/app/sentry-setup.go |
SENTRY_RELEASE | Opt ("") | Build/version identifier tagged on events. | internal/app/sentry-setup.go |
SENTRY_TRACES_SAMPLE_RATE | Opt (1.0) | Fraction [0.0–1.0] of transactions captured for performance tracing; tracing is enabled iff the rate is > 0 (so 0 disables it). | internal/app/env-setup.go (getFloat) → internal/app/sentry-setup.go |
Read directly (outside the loader)
Not in LoadEnvVariables() — read at their point of use.
| Variable | Req | Meaning | Read at |
|---|
POSTGRES_URL | Req | Postgres/Supabase connection string. | internal/database/database.go, internal/database/migrate.go |
OTEL_EXPORTER_OTLP_ENDPOINT | Opt | If set (and APP_ENV is staging/prod), enables OTLP log export. | internal/app/slog-setup.go |
BETTER_STACK_TOKEN | Opt | If set (staging/prod), enables Better Stack log shipping. | internal/app/slog-setup.go |
BETTER_STACK_ENDPOINT | Opt (https://in.logs.betterstack.com) | Better Stack ingest endpoint. | internal/app/slog-setup.go |
Worker binary — LoadWorkerEnvVariables() (cmd/worker)
| Variable | Req | Meaning |
|---|
APP_ENV | Opt (prod) | Environment name. |
WORKOS_API_KEY | Req | WorkOS API key (the worker only needs WorkOS). |
WORKOS_CLIENT_ID | Req | WorkOS client id. |
POSTGRES_URL | Req | DB connection (read directly via database.New). |
WORKER_POSTGRES_URL | Opt | If set, overrides POSTGRES_URL for the worker (separate pool). |
Test-only
| Variable | Meaning |
|---|
WORKOS_TEST_API_KEY | WorkOS key for the eventsync integration test only. |
WORKOS_TEST_CLIENT_ID | WorkOS client id for the same integration test. |
Removed / no longer read
Previously loaded at boot but removed once their only consumers were disabled or deleted (the loader admits no dead loads). Delete these from every environment.
| Variable(s) | What it was | Why removed |
|---|
CF_WORKER_URL, CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET | Cloudflare "Notifier" service | Constructed but never injected into any service and never called; package deleted. |
WORKOS_WEBHOOK_SECRET | Inbound WorkOS webhook verification | The POST /webhook/workos route was commented out; the handler was unreachable. |
JWT_ISSUER, JWT_AUDIENCE, JWT_PRIVATE_KEY | RSA signing material for the Cloudflare Durable-Object websocket access token | The DO route was commented out; realtime is now served by the live Supabase Realtime path (SUPABASE_JWT_SECRET). |