DOCS · ENVIRONMENT

Environment variables

Copy .env.example to apps/web/.env.local. For Cloudflare Pages, set secrets via wrangler pages secret put or the dashboard. Anything with KEY, TOKEN, PASSWORD, or SECRET in the name MUST be a secret, not a [vars] entry.

App

VariableRequiredDefaultDescription
APP_ENVYesdevelopmentdevelopment | production
APP_URLYeshttp://localhost:3000Public origin of the dashboard. Used for invite emails, OAuth/SSO redirects, deep links in alerts.
INGEST_HOSTYeshttp://localhost:3001Base URL of the ingest API. Shown in DSN snippets in the dashboard.
NODE_ENVNodevelopmentStandard Node.js env flag.

Database — Neon Postgres

VariableRequiredDescription
DATABASE_URLYesFull Postgres connection string. Production: Neon with ?sslmode=require.

Why Neon? The @neondatabase/serverless driver uses HTTP fetch — no TCP socket required, so it works on Cloudflare Workers.

Cache — Upstash Redis

VariableRequiredDescription
UPSTASH_REDIS_REST_URLYesREST API endpoint from the Upstash console.
UPSTASH_REDIS_REST_TOKENYesREST API token from the Upstash console.

Auth — JWT RS256

VariableRequiredDefaultDescription
JWT_PRIVATE_KEYYesBase64-encoded RSA-2048 private key, no newlines.
JWT_PUBLIC_KEYYesBase64-encoded RSA-2048 public key, no newlines.
JWT_ACCESS_EXPIRESNo15mAccess token TTL.
JWT_REFRESH_EXPIRESNo30dRefresh token TTL.
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem
base64 -i private.pem | tr -d '\n'   # → JWT_PRIVATE_KEY
base64 -i public.pem  | tr -d '\n'   # → JWT_PUBLIC_KEY

Encryption keys

VariableRequiredDescription
TOTP_ENCRYPTION_KEYYes (if 2FA used)32-byte hex (64 chars). AES-256-GCM key for TOTP secrets at rest.
ENCRYPTION_KEYYes (for SSO + trackers)32-byte hex. AES-256-GCM key for SSO client secrets and Linear/Jira tokens at rest.
openssl rand -hex 32

Email — Resend

VariableRequiredDescription
RESEND_API_KEYYes (for emails)From resend.com.
RESEND_FROMYes (for emails)TillPulse <noreply@mail.tillpulse.io> — sender domain must be verified in Resend.

AI — Anthropic / OpenRouter

VariableRequiredDescription
OPENROUTER_API_KEYYes (for Ask Pulse + TillMind + NLQ)From openrouter.ai/keys. Required by both web app and event-processor. Models: configured via NLQ_MODEL / TILLMIND_MODEL (any OpenRouter-hosted model).

Analytics — ClickHouse

VariableRequiredDefaultDescription
CLICKHOUSE_URLYes (for metrics)HTTPS URL, e.g. https://host:8443.
CLICKHOUSE_USERNodefaultClickHouse user.
CLICKHOUSE_PASSWORDYes (if auth)ClickHouse password.
CLICKHOUSE_DBNotillpulseDatabase name.

Search — Meilisearch

VariableRequiredDescription
MEILI_URLYes (for search)Meilisearch server URL.
MEILI_MASTER_KEYYes (if auth)Meilisearch master key.

If unset, full-text search degrades to status-tab listing — no error.

Slack

VariableRequiredDescription
SLACK_SIGNING_SECRETYes (for Slack)From your Slack app's Basic Information page.
SLACK_ACTION_SIGNING_SECRETYes (for Slack action buttons)Random 32-byte hex string. Same value in event-processor and web app.

Tracker webhook secrets

VariableRequiredDescription
LINEAR_WEBHOOK_SECRETYes (for Linear)Linear webhook HMAC secret (set in Linear dashboard).
JIRA_WEBHOOK_TOKENYes (for Jira)Shared token put on the Jira webhook URL as ?token=...; Jira REST webhooks are unsigned.

Ingest API (Fastify)

VariableRequiredDefaultDescription
INGEST_PORTNo3001Fastify port.
INGEST_RATE_LIMIT_PER_DSNNo10000Max events per DSN per window.
INGEST_RATE_LIMIT_WINDOW_SECONDSNo3600Sliding window in seconds.
SOURCEMAPS_DIRNo/tmp/tillpulse-sourcemapsFilesystem path for uploaded source maps.

Event processor

VariableRequiredDefaultDescription
PROCESSOR_REGIONNoaf-south-1Pin worker to a region's Redis stream + region-suffixed DLQ.
GEO_DB_PATHNoMaxMind GeoLite2-City mmdb path. If unset, geo enrichment is skipped.
RELEASE_HEALTH_INTERVAL_MSNo3600000Background release-health refresh cadence (default 1h).
AI_MAX_CONCURRENTNo4Max concurrent TillMind analyses across the worker pool.

CLI overrides

VariableRequiredDescription
TILLPULSE_API_URLNoOverride default API base URL for tillpulse CLI.
TILLPULSE_TOKENNoPersonal API key for non-interactive CLI use (CI).

Local development minimum

# apps/web/.env.local — bare minimum to boot the dashboard
DATABASE_URL=postgresql://tillpulse:tillpulse@localhost:5432/tillpulse
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=xxx
JWT_PRIVATE_KEY=<base64>
JWT_PUBLIC_KEY=<base64>
TOTP_ENCRYPTION_KEY=<64 hex>
APP_URL=http://localhost:3000

Everything else is optional. The app degrades gracefully — Ask Pulse is hidden without an Anthropic key, email actions are dropped (with a warning) without a Resend key, search disables silently without Meili, etc.