Skip to content

Environment Variables

Complete reference for all backend and frontend env vars. Only JWT_SECRET and one AI provider key are required to get started — everything else has sensible defaults.

Backend (backend/.env)

Telemetry (DIF-013)

Anonymous opt-out telemetry via PostHog. All variables are optional; telemetry is a no-op unless POSTHOG_API_KEY is set.

Posture: Sentri is self-hosted, so telemetry follows the posture of tools like Next.js / Vite / Playwright: effectively opt-in (no POSTHOG_API_KEY → zero network traffic, zero events, zero data/ cache writes) with an opt-out signal (SENTRI_TELEMETRY=0 or DO_NOT_TRACK=1) for operators who do configure a key but want to disable collection per-deployment. There is no in-product banner or first-run consent prompt — telemetry cannot start without the operator explicitly providing an API key, which is itself the consent signal. If you need a consent banner for end-user-facing deployments, file an issue against DIF-013; the telemetry module's opt-out branches are already exposed so a UI-level gate is additive work.

VariableDefaultDescription
POSTHOG_API_KEY(unset)PostHog project API key. When unset, trackTelemetry() is a no-op regardless of other settings.
POSTHOG_HOSThttps://us.i.posthog.comPostHog ingestion host. Override for self-hosted PostHog or EU region (https://eu.i.posthog.com).
SENTRI_TELEMETRY1Set to 0 to disable telemetry entirely (overrides POSTHOG_API_KEY).
DO_NOT_TRACK0Industry-standard opt-out signal. Set to 1 to disable telemetry. Equivalent to SENTRI_TELEMETRY=0.

The distinct ID sent to PostHog is sha256(hostname|cwd) — no usernames, emails, or full URLs are transmitted. URL properties are reduced to the domain hostname before send. All values are read at process start; restart the backend after changing them.

AI Provider

VariableDefaultDescription
AI_PROVIDERauto-detectForce: anthropic, openai, google, openrouter, or local
ANTHROPIC_API_KEYconsole.anthropic.com
ANTHROPIC_MODELclaude-sonnet-4-20250514Override Anthropic model
OPENAI_API_KEYplatform.openai.com
OPENAI_MODELgpt-4o-miniOverride OpenAI model
GOOGLE_API_KEYaistudio.google.com
GOOGLE_MODELgemini-2.5-flashOverride Google model
OPENROUTER_API_KEYopenrouter.ai/keys — unified gateway to 200+ models
OPENROUTER_MODELopenrouter/autoOpenRouter model slug (e.g. anthropic/claude-3.5-sonnet, meta-llama/llama-3.1-70b-instruct)
OPENROUTER_BASE_URLhttps://openrouter.ai/api/v1Override for self-hosted / proxy deployments
OPENROUTER_REFERERhttps://sentri.devHTTP-Referer header sent to OpenRouter for leaderboard attribution (optional)
OPENROUTER_APP_TITLESentriX-Title header sent to OpenRouter for leaderboard attribution (optional)
OLLAMA_BASE_URLhttp://localhost:11434Ollama server URL
OLLAMA_MODELmistral:7bModel name for local inference
OLLAMA_MAX_PREDICT4096Max output tokens for Ollama
OLLAMA_TIMEOUT_MS120000Timeout for Ollama calls (ms)
ALLOW_PRIVATE_URLSfalseWhen "true", allows compat (compat:<id>) provider baseUrl to point at loopback / RFC1918 / link-local addresses (self-hosted vLLM, LiteLLM, LocalAI, internal proxies). Scoped exclusively to compat-slot saves and the per-call SSRF-guarded fetch — trigger callbacks, preview URLs, and notification webhooks remain SSRF-protected. Do not enable in multi-tenant deployments. See AI Providers → Self-hosted endpoints.
COMPAT_CONFIG_CACHE_TTL_MS60000TTL (ms) for the in-memory compat-provider config cache that fronts SQLite + AES decryption on every AI call. When REDIS_URL is set, cache invalidations are broadcast over the sentri:compat-config:invalidate channel for cross-instance coherence.

Compat providers are DB-only

There is no COMPAT_<ID>_API_KEY env equivalent — OpenAI-compatible slots (DeepSeek, Groq, Mistral, xAI, vLLM, LiteLLM, …) must be configured via the Settings UI or POST /api/v1/settings. For pure env-driven setups, use OpenRouter (OPENROUTER_API_KEY + OPENROUTER_MODEL) instead. See AI Providers → OpenAI-Compatible Providers.

Demo Mode

VariableDefaultDescription
DEMO_GOOGLE_API_KEYPlatform-owned Gemini API key for zero-config trial. When set, users without their own AI key can try Sentri immediately using the shared key, subject to per-user daily quotas
DEMO_DAILY_CRAWLS2Max crawls per user per day in demo mode
DEMO_DAILY_RUNS3Max test runs per user per day in demo mode
DEMO_DAILY_GENERATIONS5Max AI test generations per user per day in demo mode

LLM Retry & Tokens

VariableDefaultDescription
LLM_MAX_RETRIES3Retry count for rate-limited AI calls
LLM_BASE_DELAY_MS2000Base delay for exponential backoff (ms)
LLM_MAX_BACKOFF_MS30000Max backoff delay (ms)
LLM_MAX_TOKENS16384Max output tokens per AI call

Server

VariableDefaultDescription
PORT3001Backend server port
NODE_ENVSet to production for production deployments
DB_PATHdata/sentri.dbSQLite database file path (ignored when DATABASE_URL is set)
CORS_ORIGIN*Frontend origin(s) for CORS, comma-separated. Required in production
SHUTDOWN_DRAIN_MS10000Max time (ms) to wait for in-flight runs during graceful shutdown
SPA_INDEX_PATHauto-detectPath to the Vite-built index.html for CSP nonce injection (SEC-002). Only needed when the frontend dist is not at the default location relative to the backend source. In Docker multi-container deployments, set to the shared volume path (e.g. /usr/share/frontend/index.html)

Database & Infrastructure

VariableDefaultDescription
DATABASE_URLPostgreSQL connection string (e.g. postgres://user:pass@host:5432/db). When set, uses PostgreSQL instead of SQLite. Requires pg + pg-native (or deasync as fallback)
PG_POOL_SIZE10Max PostgreSQL connection pool size (ignored for SQLite)
REDIS_URLRedis connection URL (e.g. redis://localhost:6379). When set, enables shared rate limiting, cross-instance token revocation, SSE pub/sub, and BullMQ job queue. Requires ioredis. For Redis-backed rate limiting also install rate-limit-redis
MAX_WORKERS2Global concurrency limit for BullMQ run execution (INF-003). Each slot processes one crawl or test run at a time. Ignored when Redis/BullMQ is not available. Superseded by WORKER_CONCURRENCY — kept as a fallback for backward compatibility
WORKER_CONCURRENCY2Per-container concurrency for the BullMQ run worker (AUTO-008). Used by both the in-process worker started by the backend and the standalone worker Compose service (node src/worker.js). Falls back to MAX_WORKERS when unset

Local Redis setup

Redis is optional for local development — without it, Sentri uses in-memory stores for rate limiting, token revocation, and SSE. To enable Redis locally:

bash
# macOS (Homebrew)
brew install redis && redis-server

# Or via Docker (any platform)
docker run -d --name sentri-redis -p 6379:6379 redis:7-alpine

Then in backend/.env:

bash
REDIS_URL=redis://localhost:6379

Install the required npm packages:

bash
cd backend
npm install ioredis rate-limit-redis

Local BullMQ setup

BullMQ provides durable job queue execution for crawls and test runs (INF-003). Without it, runs execute in-process — which is fine for local development but means runs are lost if the server crashes mid-execution.

To enable BullMQ locally, ensure Redis is running (see above), then:

bash
cd backend
npm install bullmq

BullMQ is detected automatically when both REDIS_URL is set and the bullmq package is installed. Set MAX_WORKERS to control how many runs can execute concurrently (default: 2).

Deployment Webhooks (AUTO-015)

Optional HMAC secrets for Vercel and Netlify deployment-event webhooks. When set, the corresponding POST /api/v1/projects/:id/trigger/<provider> endpoint accepts signed deployment payloads and launches a diff-aware preview crawl. Both endpoints additionally require a project-scoped Bearer trigger token (dual-auth) — see POST /api/v1/projects/:id/trigger-tokens.

VariableDefaultDescription
VERCEL_WEBHOOK_SECRETHMAC-SHA1 secret used to verify the X-Vercel-Signature header on Vercel deployment-event webhooks. When unset, the /trigger/vercel endpoint rejects all requests with 401.
NETLIFY_WEBHOOK_SECRETHMAC-SHA256 secret used to verify the X-Netlify-Token header on Netlify deployment-event webhooks. When unset, the /trigger/netlify endpoint rejects all requests with 401.

GitHub PR Check Runs (INT-002)

GitHub App credentials for posting native Check Runs on PRs. The feature is opt-in per project via Settings → Integrations; the env vars below carry the App-level secrets Sentri uses to mint installation tokens. See backend/src/integrations/githubChecks.js for the lifecycle (createPendingmarkInProgressconclude) and the bounded-retry policy (3 attempts, ≤4s, honours Retry-After).

VariableDefaultDescription
GITHUB_APP_IDNumeric App ID from the GitHub App settings page. Required when any project opts into PR checks.
GITHUB_APP_PRIVATE_KEYPEM RSA private key for the GitHub App. For multi-line .env values, escape newlines as \n — the module unescapes them at runtime. Used to sign RS256 App JWTs that exchange for installation tokens.
GITHUB_WEBHOOK_SECRETHMAC-SHA256 secret for X-Hub-Signature-256 verification on POST /api/v1/projects/:id/trigger/github and the App-level webhook POST /api/v1/integrations/github/app-webhook. When unset, both endpoints reject all requests with 401.
GITHUB_APP_SLUGURL-safe slug of the GitHub App (the value at the end of github.com/apps/<slug>). Required for the OAuth-style install flow (GET /api/v1/integrations/github/install/start/:projectId); without it, /install/start returns 503. GITHUB_APP_NAME is accepted as a fallback alias.
GITHUB_CHECK_NAMESentri QADisplay name shown on the GitHub PR check.
GITHUB_API_BASEhttps://api.github.comOverride for GitHub Enterprise Server (e.g. https://github.acme.corp/api/v3).

Email (Transactional)

VariableDefaultDescription
RESEND_API_KEYResend API key for transactional email (recommended)
SMTP_HOSTSMTP server host (alternative to Resend)
SMTP_PORT587SMTP server port
SMTP_SECUREfalseUse TLS for SMTP connection
SMTP_USERSMTP username
SMTP_PASSSMTP password
EMAIL_FROMSentri <noreply@sentri.dev>Sender address for all transactional emails
SKIP_EMAIL_VERIFICATIONfalseWhen "true", new users are auto-verified on registration. Dev/CI only — never set in production

Auth & Security

VariableDefaultDescription
JWT_SECRETrandom (dev)Required in production. 32+ char secret for signing JWTs
CREDENTIAL_SECRETfalls back to JWT_SECRETEncryption secret for project credentials
ARTIFACT_SECRETrandom (dev)Required in production. Signs artifact URLs (screenshots, videos)
ARTIFACT_TOKEN_TTL_MS3600000Artifact URL token TTL (ms)
ENABLE_DEV_RESET_TOKENSfalseWhen "true", forgot-password response includes the reset token (dev/test only — never in production)
APP_URLhttp://localhost:3000Frontend base URL (used for OAuth redirects, email verification links, and notification deep links). Falls back to CORS_ORIGIN
APP_BASE_PATH/Frontend base path prefix (e.g. /sentri for GitHub Pages)
BACKEND_URLauto-detectBackend URL override for cross-origin cookie detection

Compliance Audit Log (SEC-007)

The activities table is the workspace's compliance audit log. SOC 2 / ISO 27001 / PCI-DSS require it to be immutable, retained, tamper-evident, and its own reads to be audited. Every variable below is off / safe by default. See Compliance Audit Log for the full operator guide.

VariableDefaultDescription
DANGER_ALLOW_AUDIT_PURGEfalseWhen "true", DELETE /api/v1/data/activities is permitted (admin-gated). Default returns 403 AUDIT_PURGE_DISABLED. Only flip in dev / CI or under explicit incident-response process.
AUDIT_HASH_CHAINfalseWhen "true", every audit row's prevHash is computed as sha256(prev.prevHash + JSON.stringify(rowMinusHash(row))) inside the INSERT transaction. GET /api/v1/audit/verify walks the chain. Serialises INSERTs under contention — enable only on low-volume, compliance-sensitive deployments. Mutually exclusive with AUDIT_RETENTION_DAYS > 0 (boot fails if both are set).
AUDIT_RETENTION_DAYS365Daily 03:30 UTC sweep deletes activity rows older than this. 0 disables retention entirely. Values 189 are rejected at boot (SOC 2 / ISO 27001 minimum is 90 days).
AUDIT_EXPORT_RATE_LIMIT10Per (workspace × admin) CSV/NDJSON export budget per 15-min window. JSON browsing is exempt. Tripped exports return 429 AUDIT_EXPORT_RATE_LIMITED.
AUDIT_DEDUP_WINDOW_SEC60Industry-standard audit-log event dedup window (Splunk / CloudTrail / Auth0 / Datadog convention). Consecutive identical read-shaped events (audit.read, audit.export, auth.login.failed) collapse into a single row with count++ and lastAt = now if they fire within this window. 0 disables dedup entirely. Automatically disabled when AUDIT_HASH_CHAIN=true (mutating a persisted row's count/lastAt would invalidate its prevHash). PCI-DSS 10.5.3 permits this provided attribution is preserved.

Object Storage (MNT-006)

Sentri stores test artifacts (screenshots, videos, traces, visual-diff PNGs) on the local artifacts/ directory by default. Set STORAGE_BACKEND=s3 to upload supported artifacts to an S3-compatible object store (AWS S3, Cloudflare R2, MinIO).

VariableDefaultDescription
STORAGE_BACKENDlocallocal (default) or s3. Anything other than s3 keeps local-disk behaviour.
S3_BUCKETRequired when STORAGE_BACKEND=s3. Bucket name. For custom-endpoint providers (R2/MinIO) it is included path-style in upload + presigned URLs.
S3_REGIONus-east-1AWS region used for SigV4 signing. For Cloudflare R2 use auto.
S3_ACCESS_KEY_IDRequired when STORAGE_BACKEND=s3. Access key ID.
S3_SECRET_ACCESS_KEYRequired when STORAGE_BACKEND=s3. Secret access key.
S3_ENDPOINTOptional custom endpoint for S3-compatible providers. Leave unset for AWS S3 (virtual-hosted style). When set, path-style addressing is used.

Scope: Screenshots, visual-diff baselines, visual-diff PNGs, Playwright videos, and trace zips all route through writeArtifactBuffer() in s3 mode, which uploads to S3 and dual-writes to local disk so baseline acceptance and other code paths that still read from the filesystem continue to work. When STORAGE_BACKEND=s3 is active, signArtifactUrl() emits S3 pre-signed GET URLs for every /artifacts/* path. Video and trace uploads are best-effort: if S3 upload fails the run still reports the local artifact path so the run isn't flipped to failed by a transient storage outage.

Provider examples:

bash
# AWS S3
STORAGE_BACKEND=s3
S3_BUCKET=my-sentri-artifacts
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=AKIA…
S3_SECRET_ACCESS_KEY=

# Cloudflare R2
STORAGE_BACKEND=s3
S3_BUCKET=sentri-artifacts
S3_REGION=auto
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com

# MinIO (self-hosted)
STORAGE_BACKEND=s3
S3_BUCKET=sentri
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_ENDPOINT=https://minio.internal:9000

Test Execution

VariableDefaultDescription
BROWSER_HEADLESStrueSet false to see the browser window
VIEWPORT_WIDTH1280Browser viewport width (px)
VIEWPORT_HEIGHT720Browser viewport height (px)
NAVIGATION_TIMEOUT30000Timeout for page.goto() calls (ms)
API_TEST_TIMEOUT30000Per-API-test timeout (ms)
BROWSER_TEST_TIMEOUT120000Per-browser-test timeout guard (ms)
PARALLEL_WORKERS1Concurrent browser contexts (1–10). Override per-run from UI
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATHCustom Chromium executable path

Crawler

VariableDefaultDescription
CRAWL_MAX_PAGES30Maximum pages to visit per crawl
CRAWL_MAX_DEPTH3Maximum link-follow depth from the start URL
CRAWL_NETWORKIDLE_TIMEOUT5000Timeout (ms) for networkidle wait after page load

Self-Healing

VariableDefaultDescription
HEALING_ELEMENT_TIMEOUT5000Element finding timeout per strategy (ms)
HEALING_RETRY_COUNT3Retries per interaction before giving up
HEALING_RETRY_DELAY400Pause between retries (ms)
HEALING_HINT_MAX_FAILS3Skip healing hints that have failed this many consecutive times
HEALING_VISIBLE_WAIT_CAP1200Max waitFor timeout per strategy in firstVisible (ms)

AI Chat

VariableDefaultDescription
MAX_CONVERSATION_TURNS20Max turn pairs kept in chat context
AI_CLASSIFY_THRESHOLD40Confidence threshold for AI-assisted intent classification (0–100)

Logging

VariableDefaultDescription
LOG_LEVELinfodebug, info, warn, or error
LOG_DATE_FORMATisoiso, utc, local, or epoch
LOG_TIMEZONEsystemIANA timezone for local format
LOG_JSONfalseEmit structured JSON logs

OAuth

VariableDescription
GITHUB_CLIENT_IDGitHub OAuth app client ID
GITHUB_CLIENT_SECRETGitHub OAuth app client secret
GOOGLE_CLIENT_IDGoogle OAuth client ID
GOOGLE_CLIENT_SECRETGoogle OAuth client secret
GOOGLE_REDIRECT_URIOverride Google OAuth redirect URI

Frontend (build-time)

VariableDefaultDescription
VITE_API_URL"" (same origin)Backend URL for cross-origin deploys
GITHUB_PAGESSet to true to use /sentri/ base path
VITE_GITHUB_CLIENT_IDGitHub OAuth client ID (passed to frontend)
VITE_GOOGLE_CLIENT_IDGoogle OAuth client ID (passed to frontend)

Released under the MIT License.