Environment Variables
Full reference for every chmonitor environment variable, grouped by category with build-time vs runtime notes.
This is the complete list of every supported environment variable, grouped by category. Start with the two rules that govern how variables are named and when they take effect, then jump to the category you need.
One canonical name per setting
A setting the browser needs (auth provider, cloud mode, feature flags) is inlined
into the client bundle at build time as a VITE_* var. You do not set it
twice. Set the canonical CHM_* name once and vite.config.ts derives the
matching client VITE_*:
| Set this (canonical) | Derives this (client, build-time) |
|---|---|
CHM_AUTH_PROVIDER | VITE_AUTH_PROVIDER |
CHM_CLERK_PUBLISHABLE_KEY | VITE_CLERK_PUBLISHABLE_KEY |
CHM_CLOUD_MODE | VITE_CLOUD_MODE |
CHM_FEATURE_CONVERSATION_DB | VITE_FEATURE_CONVERSATION_DB |
CHM_FEATURE_USER_CONNECTIONS_DB | VITE_FEATURE_USER_CONNECTIONS_DB |
CHM_EDITION | VITE_EDITION |
Precedence per var: explicit VITE_* → canonical CHM_* → legacy
NEXT_PUBLIC_* → built-in default. The explicit VITE_* (and legacy
NEXT_PUBLIC_*) forms still work as overrides.
A dual-surface setting must still be present at both build time (so vite
inlines its VITE_*) and runtime (so the server reads it) — but it is the same
single name in both places.
Cloud mode is a build-time contract. CHM_CLOUD_MODE /
CHM_DEPLOYMENT_MODE=cloud must be set before the build so VITE_CLOUD_MODE
is inlined into the client bundle. Setting it only at runtime on a prebuilt
OSS image (e.g. the published Docker image) splits the product: the server
enforces cloud behaviour (demo-host guard) while the client still renders OSS
UI. /api/healthz detects and warns on this mismatch and reports a cloudMode
object. The reverse — a cloud build with the runtime var unset — is safe
(fail-closed: both halves degrade to OSS).
Client variable prefix by app
Browser-exposed variables are build-time inlined. The prefix depends on which app you run:
| App | Client prefix | Example |
|---|---|---|
apps/dashboard (TanStack Start, current) | VITE_* | VITE_AUTH_PROVIDER |
| Legacy Next.js (v0.2 and earlier) | NEXT_PUBLIC_* | NEXT_PUBLIC_AUTH_PROVIDER |
Tables below show the explicit VITE_* form for clarity, but you usually only set
the canonical CHM_* name (above) and let the VITE_* derive. If you are
migrating from v0.2, substitute NEXT_PUBLIC_ wherever you see VITE_.
Server-side variables (CLICKHOUSE_*, CHM_*, LLM_*, CLERK_SECRET_KEY, etc.)
are the same.
Authoritative example: apps/dashboard/.env.example.
Build-time vs runtime
There are two categories of environment variables:
| Category | When resolved | How to set | Example |
|---|---|---|---|
| Build-time | When pnpm run build runs | CI environment / shell before the build | VITE_FEATURE_CONVERSATION_DB, VITE_AUTH_PROVIDER, VITE_CLERK_PUBLISHABLE_KEY |
| Runtime | When the server process starts per request | wrangler.toml [vars], wrangler secret put, Docker -e, k8s ConfigMap/Secret | CLICKHOUSE_HOST, CONVERSATION_STORE_BACKEND |
VITE_* variables are build-time only
Setting a VITE_* var in wrangler.toml [vars] or as a Docker -e flag has no
effect on the running app — the bundle already has the value from when it was
built. Always rebuild and redeploy when changing these variables.
All other vars (unless noted) are runtime — read from process.env when the
Worker or Node process handles a request.
ClickHouse connection
Required. Set at least CLICKHOUSE_HOST.
| Variable | Required | Default | Purpose |
|---|---|---|---|
CLICKHOUSE_HOST | Yes | — | Comma-separated ClickHouse host URLs, e.g. http://ch-1:8123,http://ch-2:8123. |
CLICKHOUSE_USER | No | default | Comma-separated usernames, one per host. |
CLICKHOUSE_PASSWORD | No | "" | Comma-separated passwords, one per host. |
CLICKHOUSE_NAME | No | — | Comma-separated display labels shown in the host selector. |
CLICKHOUSE_HOST defines the host count. CLICKHOUSE_USER and
CLICKHOUSE_PASSWORD may be either a single value (applied to all hosts) or one
value per host position. CLICKHOUSE_NAME is optional. Position N maps to host
index N.
See Multiple hosts and Custom name.
Query execution
| Variable | Default | Purpose |
|---|---|---|
CLICKHOUSE_MAX_EXECUTION_TIME | 60 | Query timeout in seconds. |
CLICKHOUSE_TZ | server default | Time zone for date/time display. |
CLICKHOUSE_DATABASE | system | Database used for app-owned tables (self-tracking events, dashboards). |
EVENTS_TABLE_NAME | system.monitoring_events | Full table name override for self-tracking events. |
Connection pool
| Variable | Default | Purpose |
|---|---|---|
CLICKHOUSE_POOL_SIZE | 10 | Max concurrent ClickHouse clients per host. |
CLICKHOUSE_POOL_TIMEOUT | 300000 | Idle client timeout in milliseconds (5 min). |
CLICKHOUSE_POOL_CLEANUP_INTERVAL | 60000 | Stale-client cleanup interval in milliseconds (1 min). |
Health alerting
The cron sweep (GET /api/cron/health-sweep, Cloudflare Cron Trigger every 5
minutes) runs health checks over all hosts and can post webhook alerts without a
browser tab open.
| Variable | Default | Purpose |
|---|---|---|
CRON_SECRET | — | Required shared secret guarding the cron endpoints /api/cron/health-sweep and /api/cron/retention-prune. Send as Authorization: Bearer <secret> (or ?secret=<secret>). When unset/empty the endpoints fail closed and return HTTP 503 (they do not run) — retention-prune is destructive, so it must not be reachable unauthenticated. |
HEALTH_ALERT_ENABLED | false | Set true to POST webhook alerts. Checks run regardless; alerts only fire when true. |
HEALTH_ALERT_WEBHOOK_URL | — | Slack- or Discord-compatible webhook URL. Required for alerts to be dispatched. |
HEALTH_ALERT_MIN_SEVERITY | warning | Minimum severity to alert: warning (warning + critical) or critical (critical only). |
# Alert Slack on warning-or-worse
CRON_SECRET=a-long-random-string
HEALTH_ALERT_ENABLED=true
HEALTH_ALERT_WEBHOOK_URL=https://hooks.slack.com/services/T000/B000/XXXX
HEALTH_ALERT_MIN_SEVERITY=warningWeekly health report
A second cron job (GET /api/cron/weekly-report) builds a proactive weekly
HTML report per opted-in host — a 7-day narrative from the AI insights engine,
adaptive statistical baselines, and the disk-capacity forecaster. Opt-in, never
opt-out: reports are generated only for hosts listed in
CHM_WEEKLY_REPORT_HOSTS.
| Variable | Default | Purpose |
|---|---|---|
CHM_WEEKLY_REPORT_HOSTS | unset (no reports) | Comma-separated host indices to opt in, e.g. 0,2. Uses the same CRON_SECRET guard as health-sweep. |
# Opt host 0 and host 2 into the Monday 08:00 UTC weekly report
CRON_SECRET=a-long-random-string
CHM_WEEKLY_REPORT_HOSTS=0,2Persisted regardless of delivery outcome; best-effort delivered to
HEALTH_ALERT_WEBHOOK_URL (above) when configured. See
Health — Weekly health report
for the report contents and how to view a persisted report.
Prometheus metrics exporter
GET /api/v1/metrics serves system.metrics + system.asynchronous_metrics
for every configured host (labeled host="<id>"), plus a
chmonitor_alerts_firing gauge from the in-memory alert-dedup state, as
Prometheus text exposition format. Cached ~30s; concurrent scrapes share one
query batch.
| Variable | Default | Purpose |
|---|---|---|
CHM_FEATURE_PROMETHEUS_ENABLED | mode (self-hosted→true, cloud→false) | Canonical, server-only (no client bundle exposure needed). Set explicitly to override either default. Never gated on Clerk/billing — disabled returns 404 rather than 403 so cloud never advertises the surface. |
# Scrape this instance from Prometheus
CHM_FEATURE_PROMETHEUS_ENABLED=true# prometheus.yml
scrape_configs:
- job_name: chmonitor
scrape_interval: 30s
metrics_path: /api/v1/metrics
static_configs:
- targets: ['dash.example.com']OTel trace export
Exports chmonitor's OWN query traces — a dashboard-request span per request,
with a nested clickhouse-query span per ClickHouse call carrying
query_id/read_bytes/host attributes when available — to an external OTLP
collector for correlation with the rest of your tracing stack. This is
DISTINCT from the built-in OTel span viewer, which reads spans already
stored in system.opentelemetry_span_log.
| Variable | Default | Purpose |
|---|---|---|
CHM_OTEL_EXPORTER_URL | unset (disabled) | Collector's OTLP/HTTP traces endpoint. Must be an absolute http(s) URL, including the /v1/traces path. Unset or invalid → trace export stays off: zero spans, zero network calls, zero added latency (fail-open). Server-only, never gated on Clerk/billing. |
# Export to a local Jaeger instance (OTLP/HTTP receiver)
CHM_OTEL_EXPORTER_URL=http://jaeger:4318/v1/tracesRuns on Cloudflare Workers: the exporter uses OTLP/HTTP (not gRPC, which
Workers cannot run), and the batch export is flushed inline before each
response returns rather than deferred with waitUntil (Workers request
middleware doesn't expose it here) — so export only adds latency when
explicitly enabled.
Feature permissions
All features are public and enabled by default. Override only what needs different behavior.
| Variable | Default | Purpose |
|---|---|---|
CHM_CONFIG_FILE | — | Path to TOML or YAML config file for feature overrides. |
CHM_DISABLED_FEATURES | — | Comma-separated feature ids to disable entirely. |
CHM_AUTH_REQUIRED_FEATURES | — | Comma-separated feature ids that require authentication. |
CHM_FEATURE_{ID}_ACCESS | public | Per-feature access: public, guest (alias for public), or authenticated. |
CHM_FEATURE_{ID}_ENABLED | true | Per-feature enabled flag: true or false. |
Replace {ID} with an uppercase feature id: OVERVIEW, AGENT, INSIGHTS,
HEALTH, QUERIES, TABLES, METRICS, DASHBOARD, SECURITY, LOGS,
SETTINGS, BILLING, CLUSTER, OPERATIONS, ACTIONS, MCP, DOCS,
ABOUT, PEERDB.
CHM_FEATURE_AGENT_ACCESS=authenticated
CHM_FEATURE_METRICS_ENABLED=false
CHM_DISABLED_FEATURES=settings,insightsSee Feature permissions for config-file examples and precedence details.
Rate limiting
Per-IP (and, for the agent, per-signed-in-identity) request throttling on the
SQL-executing API surfaces. All three share one implementation
(lib/api/rate-limiter.ts, checkRateLimitDurable): an in-memory token
bucket everywhere, upgraded to a fleet-wide Cloudflare Rate Limiting binding
on Workers deploys (CHM_RATE_LIMIT_API / CHM_RATE_LIMIT_AGENT /
CHM_RATE_LIMIT_MCP in wrangler.toml) so the limit holds across isolates,
not just per-isolate. Requests over the limit get 429 with a Retry-After
header. None of these need to be set for the defaults to apply.
| Variable | Default | Purpose |
|---|---|---|
RATE_LIMIT_API_PER_MIN | 100 | GET data routes (e.g. /api/v1/charts/$name), per IP. |
RATE_LIMIT_AGENT_PER_MIN | 10 | POST /api/v1/agent, per IP then tightened per signed-in identity. |
RATE_LIMIT_MCP_PER_MIN | 30 | /api/mcp (POST/GET/DELETE), per IP. Same class of capability as the agent route — arbitrary read-only SQL via the query tool — over the MCP transport. |
Authentication
The active server-side auth provider is set by CHM_AUTH_PROVIDER. See
Authentication for the full model.
| Variable | Default | Purpose |
|---|---|---|
CHM_AUTH_PROVIDER | none | Server auth provider: none, clerk, proxy, or trusted. Canonical — the client VITE_AUTH_PROVIDER is derived from it at build time. |
VITE_AUTH_PROVIDER | (from CHM_AUTH_PROVIDER) | Client-side build-time value. Auto-derived from CHM_AUTH_PROVIDER; only set explicitly to override. |
CHM_API_KEY_SECRET | — | Shared secret for chm_ API keys. When set, API-key auth is always on alongside the active provider. Issue keys at /api/v1/auth/api-key. |
Keep secrets server-side
CLERK_SECRET_KEY, CHM_API_KEY_SECRET, and every trust-gate secret below are
runtime secrets. Never prefix them with VITE_ or NEXT_PUBLIC_, and never
expose them to the browser.
Clerk (CHM_AUTH_PROVIDER=clerk)
| Variable | Default | Purpose |
|---|---|---|
CHM_CLERK_PUBLISHABLE_KEY | — | Clerk publishable key (pk_..., public). Canonical — the client VITE_CLERK_PUBLISHABLE_KEY is derived from it at build time. |
VITE_CLERK_PUBLISHABLE_KEY | (from CHM_CLERK_PUBLISHABLE_KEY) | Client-side build-time value. Auto-derived; only set explicitly to override. Must be present before pnpm run build. |
CLERK_SECRET_KEY | — | Clerk secret key (sk_...). Runtime only, secret. Never expose to the browser. |
CHM_CLERK_PUBLIC_READ | false | When true, anonymous visitors can view read-only monitoring content; writes (AI agent, control actions, arbitrary SQL) still require sign-in. Runtime only. See Clerk → public read-only mode. |
Reverse proxy (CHM_AUTH_PROVIDER=proxy)
Trust a reverse proxy that already authenticated the user. Either mechanism below can authenticate a request.
Cloudflare Access JWT:
| Variable | Default | Purpose |
|---|---|---|
CHM_CF_ACCESS_TEAM_DOMAIN | — | Cloudflare Access team URL (https://<team>.cloudflareaccess.com). Enables Cf-Access-Jwt-Assertion JWT verification. |
CHM_CF_ACCESS_AUD | — | Access application AUD tag the JWT must carry. |
Trusted header (bare subject):
| Variable | Default | Purpose |
|---|---|---|
CHM_PROXY_AUTH_SECRET | — | Shared secret. When set, the identity header is honored only if this secret is also present (constant-time compare). Without it, the trusted-header mechanism is disabled. |
CHM_PROXY_SHARED_SECRET_HEADER | X-Chm-Proxy-Secret | Header the proxy sends containing the shared secret. |
CHM_PROXY_AUTH_HEADER | X-Forwarded-User | Header the proxy sends containing the authenticated user identity. |
Trusted-header gate
When CHM_PROXY_AUTH_SECRET is set, the identity header (X-Forwarded-User by
default) is honored only when the request also carries a matching shared-secret
header (constant-time compared). Without CHM_PROXY_AUTH_SECRET, the entire
trusted-header mechanism is disabled and identity headers are ignored.
Trusted reverse proxy (CHM_AUTH_PROVIDER=trusted)
Use when an upstream proxy (oauth2-proxy, Authelia, Traefik ForwardAuth, nginx auth-url) forwards the user's full identity as HTTP headers. Extracts a complete principal — name, email, avatar, groups, custom claims.
Trust gate — configure exactly one:
| Variable | Default | Purpose |
|---|---|---|
CHM_TRUSTED_AUTH_SECRET | — | Shared secret (runtime secret). The proxy must send it in CHM_TRUSTED_SHARED_SECRET_HEADER. Constant-time compared. Recommended. |
CHM_TRUSTED_ALLOW_INSECURE | false | When true, trusts headers with no secret check. Only safe when the service is unreachable except via the proxy (e.g. k8s ClusterIP). |
CHM_TRUSTED_SHARED_SECRET_HEADER | X-Chm-Proxy-Secret | Header name carrying the shared secret. |
Identity headers — all optional; defaults match common proxy conventions:
| Variable | Default | Populates |
|---|---|---|
CHM_TRUSTED_USER_HEADER | X-Forwarded-User | Subject / user id. Falls back to the email header. |
CHM_TRUSTED_EMAIL_HEADER | X-Forwarded-Email | Email address. |
CHM_TRUSTED_NAME_HEADER | X-Forwarded-Preferred-Username | Display name. |
CHM_TRUSTED_AVATAR_HEADER | X-Forwarded-Avatar | Avatar URL. |
CHM_TRUSTED_GROUPS_HEADER | X-Forwarded-Groups | Comma- or space-separated groups. |
CHM_TRUSTED_ROLE_HEADER | X-Forwarded-Role | Single role; merged into the groups list. |
CHM_TRUSTED_CUSTOM_HEADERS | — | Extra claims. Comma-separated field:Header-Name pairs, e.g. team:X-Forwarded-Team. |
Access control:
| Variable | Default | Purpose |
|---|---|---|
CHM_TRUSTED_ALLOWED_GROUPS | — | Comma-separated group names. When set, users whose forwarded groups do not intersect are denied 403. Case-insensitive. |
See Trusted proxy for setup examples (oauth2-proxy + Dex + Traefik, nginx).
AI agent / LLM provider
The agent uses an OpenAI-compatible API. Set LLM_API_KEY to enable it. Keep all
LLM keys server-side — never prefix them with VITE_.
| Variable | Default | Purpose |
|---|---|---|
LLM_API_KEY | — | Provider API key (fallback when no provider-specific key is set). |
LLM_API_BASE | https://openrouter.ai/api/v1 | OpenAI-compatible API base URL. |
LLM_MODEL | openrouter:openrouter/free | Model identifier in provider:modelId form. |
LLM_EXTRA_MODELS | — | Additional model picker entries. Format: provider:modelId[|contextLength][|description], comma-separated. Providers: openrouter, nvidia, anyrouter. |
Provider-specific keys
Provider-specific keys take priority over the generic LLM_API_KEY /
LLM_API_BASE.
| Variable | Purpose |
|---|---|
OPENROUTER_API_KEY | OpenRouter API key. |
OPENROUTER_API_BASE | OpenRouter base URL. |
OPENROUTER_REFERER | HTTP referer sent to OpenRouter (for rankings). |
OPENROUTER_APP_NAME | App name sent to OpenRouter (for rankings). |
OPENROUTER_MODELS_API | OpenRouter models list endpoint (default https://openrouter.ai/api/v1/models). |
NVIDIA_API_KEY | NVIDIA NIM API key. |
NVIDIA_API_BASE | NVIDIA NIM base URL. |
ANYROUTER_API_KEY | AnyRouter API key. |
ANYROUTER_API_BASE | AnyRouter base URL. |
Agent behavior
| Variable | Default | Purpose |
|---|---|---|
AGENT_API_TOKEN | — | Shared Bearer token for the agent API (POST /api/v1/agent). Accepted when CHM_FEATURE_AGENT_ACCESS=authenticated. |
AGENT_ENABLE_CONTROL_TOOLS | false | Enable kill-query, optimize, and other write actions. Keep false unless users are trusted. |
See AI agent — configuration for full details.
Conversation store
Agent conversations default to browser localStorage. Enable server-side persistence with two steps:
Enable the feature (build-time)
Set CHM_FEATURE_CONVERSATION_DB=true (the client VITE_FEATURE_CONVERSATION_DB
is derived from it at build time — must be present before pnpm run build). Also
requires Clerk auth (CHM_AUTH_PROVIDER=clerk).
Choose a backend (runtime)
Configure a backend at runtime via CONVERSATION_STORE_BACKEND.
| Variable | Default | Purpose |
|---|---|---|
CHM_FEATURE_CONVERSATION_DB | false | Canonical. Set true to enable server-side conversation persistence. Requires Clerk. Drives the build-time VITE_FEATURE_CONVERSATION_DB. |
VITE_FEATURE_CONVERSATION_DB | (from CHM_FEATURE_CONVERSATION_DB) | Client-side build-time value. Auto-derived; only set explicitly to override. Must be present before pnpm run build. |
CONVERSATION_STORE_BACKEND | (auto) | Runtime. Backend: agentstate, d1, postgres, or memory. When unset, the server auto-selects the first available backend (AgentState if key present → D1 binding → Postgres via DATABASE_URL → Memory). |
AgentState backend:
| Variable | Default | Purpose |
|---|---|---|
AGENTSTATE_API_KEY | — | AgentState API key. Required when CONVERSATION_STORE_BACKEND=agentstate. |
AGENTSTATE_BASE_URL | https://agentstate.app/api | AgentState API base URL. |
AGENTSTATE_AI_ENRICH | false | Set true to enable AI enrichment of stored conversations. |
Cloudflare D1 backend:
| Variable | Default | Purpose |
|---|---|---|
CHM_CLOUD_D1_DATABASE_ID | — | Cloudflare D1 database UUID for the CHM_CLOUD_D1 binding. |
AGENT_CHM_CLOUD_D1_DATABASE_ID | — | Alias for CHM_CLOUD_D1_DATABASE_ID. |
PostgreSQL backend:
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection string. |
POSTGRES_URL | Alternative PostgreSQL connection string. |
POSTGRES_PRISMA_URL | Prisma-specific PostgreSQL connection string. |
See Conversation history — backends for setup and fallback behavior.
User connections (server storage)
Let signed-in users save personal ClickHouse hosts to the server. All four of these must be present together, or the server returns "User connections database storage is not enabled".
| Variable | Default | Purpose |
|---|---|---|
CHM_FEATURE_USER_CONNECTIONS_DB | false | Canonical. Set true to enable. Drives the build-time VITE_FEATURE_USER_CONNECTIONS_DB. |
VITE_FEATURE_USER_CONNECTIONS_DB | (from CHM_FEATURE_USER_CONNECTIONS_DB) | Client-side build-time value. Auto-derived; only set explicitly to override. |
CHM_AUTH_PROVIDER | none | Must be clerk — connections are keyed by the Clerk userId. |
CHM_USER_CONNECTIONS_ENCRYPTION_KEY | — | 32-byte base64 key (secret) encrypting saved credentials at rest (AES-256-GCM). |
Plus a database backend: the CHM_CLOUD_D1 binding on Cloudflare, or
DATABASE_URL / POSTGRES_URL on Docker / Kubernetes.
See User connections for the full setup.
AI insights store
The AI Insights panel on /overview persists its findings through a pluggable
store. It is additive opt-in and defaults to ClickHouse; the D1, Postgres, and
AgentState backends reuse the conversation-store env above.
| Variable | Default | Purpose |
|---|---|---|
INSIGHTS_STORE_BACKEND | auto | Backend: auto, clickhouse, d1, postgres, agentstate, or memory. auto and clickhouse both use the ClickHouse monitoring_findings table. auto never silently follows other env; a selected backend missing its prerequisite falls back to ClickHouse. |
INSIGHTS_D1 | — | Optional dedicated D1 binding for the d1 backend. Falls back to CHM_CLOUD_D1 when unset. |
postgres reuses DATABASE_URL; agentstate reuses AGENTSTATE_API_KEY (+
optional AGENTSTATE_BASE_URL).
See AI agent — AI Insights persistence
for setup and fallback behavior. GET /api/v1/insights/backend reports the active
backend.
PeerDB monitoring
Optional. Set PEERDB_API_URL to enable the PeerDB section (Mirrors and Peers) in
the sidebar.
| Variable | Default | Purpose |
|---|---|---|
PEERDB_API_URL | — | PeerDB REST API base URL. For the PeerDB UI (NextAuth), include /api suffix — e.g. https://peerdb.example.com/api. For the raw flow-api, use the bare origin — e.g. http://localhost:8113. |
PEERDB_PASSWORD | — | PeerDB password. Sent as HTTP Basic with an empty username. Server-side only. |
PEERDB_CACHE_TTL_MS | 10000 | Server-side response cache TTL in milliseconds. Set 0 to disable. |
PEERDB_CACHE_MAX_ENTRIES | 500 | Max cached PeerDB responses before oldest are evicted. |
PEERDB_FETCH_TIMEOUT_MS | 10000 | Upstream PeerDB request timeout in milliseconds. |
chmonitor proxies only a read-only allowlist of PeerDB endpoints; mutating
operations (create/drop/pause, alert config) are rejected with 403.
# PeerDB UI behind NextAuth
PEERDB_API_URL=https://peerdb.example.com/api
PEERDB_PASSWORD=your-peerdb-ui-password
# Raw flow-api (no auth)
PEERDB_API_URL=http://localhost:8113Analytics & branding
All client-side, all build-time. VITE_* for TanStack app; NEXT_PUBLIC_* for
legacy Next.js.
| Variable | Default | Purpose |
|---|---|---|
VITE_TITLE_SHORT | ClickHouse | Browser tab title (short form). |
VITE_LOGO | — | Custom logo URL shown in the header. |
VITE_AUTOCOMPLETE_LIMIT | — | Max results in autocomplete dropdowns. |
VITE_MEASUREMENT_ID | — | Google Analytics measurement ID (G-...). |
VITE_SELINE_ENABLED | false | Enable Seline analytics. |
VITE_VERCEL_ANALYTICS | false | Enable Vercel Analytics. |
VITE_ANALYTICS_KEY (derived from CHM_ANALYTICS_KEY) | — | PostHog project key for opt-in Product Analytics (SaaS funnel tracking). Empty = disabled. |
VITE_ANALYTICS_HOST (derived from CHM_ANALYTICS_HOST) | https://us.i.posthog.com | PostHog ingest host — point at a self-hosted PostHog instance. |
Runtime & build
| Variable | Default | Purpose |
|---|---|---|
NODE_ENV | development | Runtime environment: development, production, test. |
ENABLE_CLOUDFLARE | false | Enable Cloudflare-specific build configuration. |
CLOUDFLARE_WORKERS | — | Set to 1 when running on Cloudflare Workers. |
CF_PAGES | — | Set automatically by Cloudflare Pages at runtime. |
MINIFLARE | — | Set to 1 when running locally with Miniflare. |
DOCS_CONTENT_ROOT | — | Override docs content source directory. |
Build-time only
Injected at build time for the About page.
| Variable | Purpose |
|---|---|
VITE_GIT_SHA | Current commit SHA. |
VITE_GIT_REF | Current git branch or tag. |
VITE_BUILD_TIMESTAMP | ISO build timestamp. |
VITE_CI | Set to true in CI environments. |
Query config source
| Variable | Default | Purpose |
|---|---|---|
CHM_CONFIG_SOURCE | ts | Query-config resolution path: ts (hand-written TypeScript configs, current default everywhere) or declarative (opt-in data-only catalog, validated against the same schema and kept at parity — see Catalog contributing). Any value other than the literal string declarative resolves to ts. A malformed declarative entry logs an error and falls back to the ts config rather than crashing. |
CHM_CONFIG_DIRECTORY | /etc/chmonitor/queries.d | Self-hosted only. Directory scanned once at startup for *.yaml local query-config overrides — no PR or fork needed. Each file must satisfy the same declarative schema as the catalog; an invalid file (bad YAML, schema violation, or duplicate name) is skipped with a warning log, never crashes boot. Checked first, ahead of CHM_CONFIG_SOURCE — a local file overrides a same-named built-in ts or declarative config regardless of that setting. Requires a real filesystem (Docker/K8s; not available on the Cloudflare Worker deploy). Mount example in the repo's docker-compose.yml. |
CHM_PACK_REGISTRY_URL | — | Comma-separated community query-pack URLs (HTTP(S) or file://), consulted when CHM_CONFIG_SOURCE=declarative. Each pack is a single YAML manifest ({ name, version, queries: [...] }) fetched once at startup (no hot-reload). HTTP(S) fetches are SSRF-guarded; file:// reads a local, operator-chosen path. Precedence (highest to lowest): the self-hosted local queries.d override > packs > the built-in declarative catalog > ts configs. Within packs, the last URL wins on a name collision (logged). An unreachable or malformed pack — or a single bad query inside an otherwise-valid pack — is skipped with a warning; the built-in catalog always still serves. |
Legacy / migration
| Variable | Notes |
|---|---|
NEXT_PUBLIC_AUTH_PROVIDER | Renamed to VITE_AUTH_PROVIDER in v0.3. Old name still works as a fallback. |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Renamed to VITE_CLERK_PUBLISHABLE_KEY in v0.3. |
NEXT_PUBLIC_FEATURE_CONVERSATION_DB | Deprecated alias for VITE_FEATURE_CONVERSATION_DB=true. |
NEXT_PUBLIC_AUTOCOMPLETE_LIMIT | Renamed to VITE_AUTOCOMPLETE_LIMIT in v0.3. |
NEXT_PUBLIC_RUNNING_QUERIES_REFRESH_MS | Renamed to VITE_RUNNING_QUERIES_REFRESH_MS in v0.3. |
CLICKHOUSE_EXCLUDE_USER_DEFAULT | Comma-separated usernames excluded from history-queries by default. |
See Migrate to v0.3 for the full rename list.
Related
Configuration
How the three sources layer and where to set each category.
Settings
In-app settings and their server-side counterparts.
Connection presets
Create a least-privilege read-only ClickHouse user.
Feature permissions
Config-file and env-override details.
Authentication
Choose and configure an auth provider.
Product Analytics
Opt-in, off-by-default PostHog funnel analytics.
Migrate to v0.3
Full rename list from the Next.js era.
Catalog contributing
The declarative query-config catalog CHM_CONFIG_SOURCE opts into.