Environment variables

Every variable in .env.example, grouped as the file groups them, with the comment that describes it and its placeholder value.

This page is generated from reference/environment-variables.mdx — do not edit it by hand.

Generated from .env.example at the repository root by apps/web/scripts/docs/generate.ts — do not edit by hand.

Use this page to find what each environment variable configures when you run Spectrace yourself.

.env.example is the source of truth for configuration; this page lists its 48 variables in the same 16 sections. The Required? column is filled only where the source comment says so explicitly — a blank cell means the file does not state it, not that the variable is optional. Values are the placeholders from the file, shown so you can recognise the expected shape.

Supabase

VariableRequired?DescriptionExample / default
NEXT_PUBLIC_SUPABASE_URLhttps://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEYyour-anon-key
SUPABASE_SERVICE_ROLE_KEYService role also provisions the private requirement-attachments storage bucket (auto-created on first attachment upload).your-service-role-key

Database (for Drizzle migrations)

VariableRequired?DescriptionExample / default
DATABASE_URLpostgresql://postgres:password@db.your-project.supabase.co:5432/postgres

AI Providers

VariableRequired?DescriptionExample / default
OPENAI_API_KEYChat provider is selectable; embeddings are always OpenAI (pgvector is fixed at 1536 dims), so OPENAI_API_KEY is required regardless of AI_PROVIDER.sk-...
AI_PROVIDERAI_PROVIDER selects the chat provider: openai (default) | anthropic | googleopenai
ANTHROPIC_API_KEYRequired only for the matching AI_PROVIDER:sk-ant-...
GOOGLE_GENERATIVE_AI_API_KEYRequired only for the matching AI_PROVIDER:...
AI_CHAT_MODELModel overrides — apply to every provider, OpenAI included. Unset → OpenAI keeps each agent's registry model; other providers use per-provider defaults. Commented out in .env.example.claude-3-7-sonnet-latest
AI_CHAT_MODEL_MINICommented out in .env.example.claude-3-5-haiku-latest

GitHub App

Register a SEPARATE app per environment: an app has exactly one webhook URL, so a shared app sends production events wherever it was last pointed. Setup guide: https://spectrace.io/docs/integrations/github-app

VariableRequired?DescriptionExample / default
GITHUB_APP_IDThe numeric App ID from the app's settings page — NOT GITHUB_CLIENT_ID.123456
GITHUB_APP_PRIVATE_KEYThe downloaded .pem. Real newlines, \n escapes, or base64 of the whole file all work; a key that cannot be parsed is reported by the diagnostic below rather than failing later as an opaque 401."-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
GITHUB_APP_WEBHOOK_SECRETThe App's own webhook secret. Distinct from GITHUB_WEBHOOK_SECRET below: that one is for the legacy per-repo webhooks the OAuth connect flow installs, and the two coexist while the App migration is in progress.generate-a-long-random-string

Check configuration once deployed: visit /api/admin/github-app-status in a

VariableRequired?DescriptionExample / default
STEALTH_OWNER_EMAILbrowser signed in as the platform owner (the user whose email matches STEALTH_OWNER_EMAIL). Reveals no secrets, 404s for everyone else, and is subject to the stealth/beta access gate like any other page. Commented out in .env.example.owner@example.com

GitHub OAuth (user-authored actions: PR creation from the extension)

VariableRequired?DescriptionExample / default
GITHUB_CLIENT_IDIv1.abc123
GITHUB_CLIENT_SECRETsecret
GITHUB_WEBHOOK_SECRETShared secret for the LEGACY per-repo webhook signature verification (issues + PR webhooks). Auto-registered on repos by the GitHub connect flow. The PR webhook route rejects unsigned deliveries with 401 when this is unset; the issues route returns 503 in production and skips verification outside it.your-webhook-secret

Linear

VariableRequired?DescriptionExample / default
LINEAR_CLIENT_IDclient-id
LINEAR_CLIENT_SECRETclient-secret
LINEAR_WEBHOOK_SECRETMust match the secret set in Linear Settings > API > Webhooks. The webhook route fails closed: if this is unset every delivery is rejected with a 401.lin_wh_...

Jira (Atlassian Cloud OAuth 2.0 / 3LO)

VariableRequired?DescriptionExample / default
JIRA_CLIENT_IDclient-id
JIRA_CLIENT_SECRETclient-secret
JIRA_REDIRECT_URIOptionalOptional — defaults to ${NEXT_PUBLIC_APP_URL}/api/auth/jira/callback(empty)

Slack

VariableRequired?DescriptionExample / default
SLACK_CLIENT_ID1234567890.1234567890
SLACK_CLIENT_SECRETsecret
SLACK_SIGNING_SECRETFrom the Slack app's Basic Information page. Used to verify every inbound events / slash-command / interaction request; unset means all are rejected.signing-secret

Integration token encryption

VariableRequired?DescriptionExample / default
GITHUB_TOKEN_ENCRYPTION_KEYIntegration token encryption (AES-256-GCM, applied to OAuth tokens at rest). Linear and Slack fall back to GITHUB_TOKEN_ENCRYPTION_KEY when their own key is unset; Jira does not — set JIRA_TOKEN_ENCRYPTION_KEY for the Jira integration. Rotating a key makes existing stored tokens undecryptable — reconnect the integration after any rotation.generate-a-long-random-string
JIRA_TOKEN_ENCRYPTION_KEY(empty)
LINEAR_TOKEN_ENCRYPTION_KEY(empty)
SLACK_TOKEN_ENCRYPTION_KEY(empty)
VSCODE_AUTH_CODE_ENCRYPTION_KEYOptionalOptional. Encrypts the short-lived session stored against a VS Code sign-in code. Left unset it is derived from SUPABASE_SERVICE_ROLE_KEY, so there is nothing to configure — set it only to rotate independently of Supabase.(empty)

Trigger.dev

VariableRequired?DescriptionExample / default
TRIGGER_SECRET_KEYtr_dev_...
TRIGGER_API_URLhttps://api.trigger.dev

App

VariableRequired?DescriptionExample / default
NEXT_PUBLIC_APP_URLhttp://localhost:3000

Launch stage

VariableRequired?DescriptionExample / default
NEXT_PUBLIC_LAUNCH_STAGELaunch stage — ONE var drives both the middleware access gate and the stealth/beta tag on the logo. Values: stealth | beta. Unset = public (no gate, no badge). NEXT_PUBLIC_* is inlined at build time, so changing it requires a redeploy.beta
BETA_ACCESS_CODEMaster access code the gate accepts while stealth/beta is on (either name). Commented out in .env.example.(empty)

Email

VariableRequired?DescriptionExample / default
EMAIL_PROVIDERSupported providers: brevo | resendbrevo
EMAIL_MODEproduction
FROM_EMAILSpectrace <team@spectrace.io>
BREVO_API_KEYxkeysib-...
RESEND_API_KEYCommented out in .env.example.re_...

Sentry (Error Monitoring)

VariableRequired?DescriptionExample / default
NEXT_PUBLIC_SENTRY_DSNGet these from https://sentry.io/settings/https://xxx@xxx.ingest.sentry.io/xxx
SENTRY_DSNhttps://xxx@xxx.ingest.sentry.io/xxx
SENTRY_ORGyour-org
SENTRY_PROJECTspectrace-web
SENTRY_AUTH_TOKENsntrys_xxx

Optional: Upstash Redis (for production rate limiting)

VariableRequired?DescriptionExample / default
UPSTASH_REDIS_REST_URLGet these from https://upstash.com/https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKENxxx