chmonitor
AI Agent

Configuration

All environment variables for configuring the AI Agent — LLM provider, model picker, access control, and conversation persistence.

Configure the agent entirely through environment variables. Start with the core LLM settings, then add provider keys, access control, and persistence as you need them.

Core LLM settings

These three variables cover most deployments.

VariableDefaultPurpose
LLM_API_KEYProvider API key. Required to enable the agent.
LLM_API_BASEhttps://openrouter.ai/api/v1OpenAI-compatible base URL.
LLM_MODELopenrouter:openrouter/freeModel identifier (provider:modelId).
LLM_API_KEY=sk-or-...
LLM_API_BASE=https://openrouter.ai/api/v1
LLM_MODEL=openrouter:openrouter/auto

Provider-specific keys

Set the key for each provider you want to use. Only providers with a key configured appear as selectable in the model picker.

VariableProviderNotes
OPENROUTER_API_KEYOpenRouterAlias for LLM_API_KEY when using OpenRouter.
OPENROUTER_API_BASEOpenRouterOverride base URL.
OPENROUTER_REFEREROpenRouterHTTP referer sent with requests (for leaderboard attribution).
OPENROUTER_APP_NAMEOpenRouterApp name sent with requests.
OPENROUTER_MODELS_APIOpenRouterOverride models list URL.
NVIDIA_API_KEYNVIDIA NIMNIM API key.
NVIDIA_API_BASENVIDIA NIMNIM base URL (defaults to NVIDIA's hosted endpoint).
ANYROUTER_API_KEYAnyRouterAnyRouter API key.
ANYROUTER_API_BASEAnyRouterAnyRouter base URL.
ANYROUTER_DYNAMIC_MODELSAnyRouterSet to false / 0 / off to disable live catalog enrichment (static registry only). On by default when ANYROUTER_API_KEY is set.
ANYROUTER_TOP_MODELS_NAnyRouterMax usage-ranked models to add to the picker (default 8, max 32).
ANYROUTER_METRICS_CANDIDATE_CAPAnyRouterMax catalog models to score via /models/{id}/metrics (default 24, max 64).
ANYROUTER_PRESETSAnyRouterSet to false / 0 / off to hide your workspace presets from the picker. On by default when ANYROUTER_API_KEY is set.
ANYROUTER_PRESETS_MAXAnyRouterMax presets to surface (default 8, max 32).
ANYROUTER_OAUTH_CLIENT_IDAnyRouterPin a pre-registered OAuth client for "Sign in with AnyRouter". Optional — the server registers one on first use.
OPENROUTER_DYNAMIC_MODELSOpenRouterSet to false / 0 / off to disable live catalog enrichment (curated registry only). On by default when OPENROUTER_API_KEY is set.
OPENROUTER_TOP_MODELS_NOpenRouterMax dynamically ranked models to add to the picker (default 12, max 32).

OpenRouter dynamic models (relevance ranking)

When OPENROUTER_API_KEY is set, GET /api/v1/agents/models also loads OpenRouter's public catalog (GET {OPENROUTER_MODELS_API}) and appends a bounded set of tool-capable models on top of the curated registry.

Unlike AnyRouter, OpenRouter exposes no usage-ranking API — the order query parameter is accepted but ignored, and the ranking endpoints behind openrouter.ai are not public. The ranking is therefore a deterministic relevance score (curated-registry membership, well-known model authors, free variants, context size, recency), not measured usage. Models without tool calling are excluded, since the agent cannot use them.

AnyRouter presets

When ANYROUTER_API_KEY is set, the authenticated catalog response also carries your workspace's presets, which the picker lists as anyrouter:@preset/<slug>. Selecting one sends the preset's own model, system prompt, and sampling settings.

Sign in with AnyRouter (bring your own credits)

Users can sign in to AnyRouter directly from the model picker (or the Provider & Models settings tab) instead of relying on the deployment's key. The server runs an OAuth 2.1 authorization-code + PKCE flow in a popup; the resulting token is held in that browser only and replayed per request through the existing BYOK path, so it is never persisted or logged server-side. AnyRouter tokens last about 30 days and have no refresh token — signing in again mints a fresh one.

Sign-in is offered only when ANYROUTER_API_KEY is not set. With a key configured the provider already works for everyone, so the flow is hidden; without one, it unlocks AnyRouter models that would otherwise be unavailable.

AnyRouter dynamic models (usage ranking)

When ANYROUTER_API_KEY is set, GET /api/v1/agents/models also loads AnyRouter’s public catalog (GET {ANYROUTER_API_BASE}/models) and ranks a bounded set of tool-capable models by real usage (GET .../models/{id}/metricsrequest_count). List-level sort=usage is not supported by AnyRouter — ranking is always metrics-based.

The picker then includes:

  1. anyrouter:auto — default when AnyRouter is configured. The agent route resolves it at request time to the current top tool-capable model by usage (cached ~5 minutes), falling back to the curated static default (anyrouter:google/gemma-4-26b-a4b-it) if the catalog/metrics are unavailable.
  2. Router aliases when present in the catalog — e.g. anyrouter/agent, anyrouter/free, anyrouter/coding.
  3. Top-N concrete models by request_count (function-calling preferred).

Failures are fail-soft: OpenRouter-only / OSS deploys without AnyRouter keep the static MODEL_REGISTRY path with no live dependency on anyrouter.dev.

Extra models

LLM_EXTRA_MODELS adds entries to the model picker without touching code. Format:

provider:modelId[|contextLength][|description]
  • provider — one of openrouter, nvidia, anyrouter
  • modelId — model ID passed to the provider (may contain colons)
  • contextLength — optional integer token count; defaults to 128000
  • description — optional display label; defaults to modelId

Comma-separate multiple models:

LLM_EXTRA_MODELS="nvidia:meta/llama-3.3-70b|131072|Llama 3.3 70B,openrouter:x-ai/grok-2"

Extra models are appended after the built-in registry. If an extra entry shares a provider:modelId key with a built-in, the built-in wins.

Access & safety

VariableDefaultPurpose
CHM_FEATURE_AGENT_ACCESSpublicSet to authenticated to require login before using the agent.
AGENT_API_TOKENShared Bearer token accepted by POST /api/v1/agent.
AGENT_ENABLE_CONTROL_TOOLSfalseWhen true, enables optimize_table, kill_query, and kill_mutation. Keep off unless the ClickHouse user is trusted.
CHM_AGENT_FIRECRAWL_MCPtrueConnect Firecrawl's hosted MCP (no API key) on every agent request. Set false to disable.
CHM_AGENT_FIRECRAWL_ALLOW_DOMAINS(empty)Comma-separated hostnames Firecrawl may scrape/crawl/map. Empty / unset = unrestricted. Apex tokens also match subdomains; *.example.com is subdomain-only (not the apex). Search-only calls (no URL) stay allowed.
RATE_LIMIT_AGENT_PER_MIN10Max POST /api/v1/agent requests per minute, applied per signed-in identity and per IP.
RATE_LIMIT_AGENT_GUEST_PER_MIN5Cloud-only tighter per-guest identity limit (key agent:guest:guest:<hash>). OSS anonymous callers stay IP-only.
CHM_GUEST_AI_REQUESTS_PER_DAY3Cloud-only daily AI messages for anonymous visitors. Tracked in D1 ai_usage_daily per guest:<ip-hash>. Signed-in users keep their plan cap (Free 5 / Pro 100 / …). OSS is not gated.
AGENT_DEBUGfalseWhen truthy (1/true/yes/on), logs verbose agent request/usage details. Leave off in production — it prints message keys, resolved user ids, and token usage.

Require auth on public deployments:

CHM_FEATURE_AGENT_ACCESS=authenticated

Protect the API endpoint with a bearer token:

AGENT_API_TOKEN=your-secret-token
curl -H "Authorization: Bearer $AGENT_API_TOKEN" \
     -d '{"message":"...","hostId":0}' \
     https://your-host/api/v1/agent

Cross-source Postgres tools

Set CHM_FEATURE_POSTGRES_SOURCE=true to expose the agent's read-only Postgres tools (run_postgres_select_query, get_postgres_metrics, list_postgres_slow_query_patterns, get_postgres_table_stats). They resolve a per-call pgHostId against the POSTGRES_* env lists below — a flat positional index, exactly like a ClickHouse hostId indexes the CLICKHOUSE_* lists. All lists are comma-separated and index-aligned with POSTGRES_HOST; a single POSTGRES_USER / POSTGRES_PASSWORD broadcasts to every host.

VariableDefaultPurpose
CHM_FEATURE_POSTGRES_SOURCEfalseMaster gate. When true, the three Postgres tools are added to the agent. Pure env gate (no Clerk) so self-hosted has equal support.
POSTGRES_HOSTComma-separated Postgres hosts (host or host:port). Defines the pgHostId id space.
POSTGRES_PORT5432Ports (used when a host has no inline :port).
POSTGRES_USERpostgresUsers; a single value applies to all hosts.
POSTGRES_PASSWORDPasswords; a single value applies to all hosts.
POSTGRES_DATABASEpostgresDatabase names.
POSTGRES_SSLMODElibpq sslmode (disable | require | verify-full).
POSTGRES_NAMEDisplay names for the sources.

Env hosts only, for now

The agent's Postgres tools read env-configured hosts (POSTGRES_*), like the ClickHouse tools read CLICKHOUSE_*. Per-user (D1) Postgres connections managed in the UI are not yet visible to the agent — the same limitation the ClickHouse agent tools already have. Env Postgres hosts are operator-supplied and trusted (no SSRF re-guard), matching the CLICKHOUSE_HOST precedent.

Point the Postgres user at a read-only role. The tools pin the session read-only and gate the SQL, but a least-privilege user is defense in depth.

CHM_FEATURE_POSTGRES_SOURCE=true
POSTGRES_HOST=db.internal:5432
POSTGRES_USER=readonly
POSTGRES_PASSWORD=secret
POSTGRES_DATABASE=appdb

Least-privilege ClickHouse user

The agent's query tool (and the MCP server) run read-only SELECTs that the model composes. Because the model can read any table the connection user can, point it at a dedicated, restricted user — not default or an admin account. Two concerns in particular:

  • system.query_log can contain credentials embedded in query text (e.g. a CREATE ... IDENTIFIED BY or an s3(...) call another client ran).
  • system.users, system.grants, and system.quotas expose your access model.

Create a monitoring user restricted to the system tables the dashboard needs and deny the sensitive ones:

CREATE USER chmonitor_agent IDENTIFIED BY 'a-strong-password'
  SETTINGS readonly = 1;

-- Read-only access to the metrics/monitoring tables the dashboard reads.
GRANT SELECT ON system.* TO chmonitor_agent;

-- Revoke the tables that leak secrets or your access model.
REVOKE SELECT ON system.query_log FROM chmonitor_agent;
REVOKE SELECT ON system.users FROM chmonitor_agent;
REVOKE SELECT ON system.grants FROM chmonitor_agent;
REVOKE SELECT ON system.quotas FROM chmonitor_agent;

Keep control tools off by default

Leave AGENT_ENABLE_CONTROL_TOOLS=false unless the ClickHouse user is trusted — it enables mutating tools (optimize_table, kill_query, kill_mutation). A readonly = 1 user is a second line of defense even if it is ever turned on.

Built-in Firecrawl MCP

The agent connects Firecrawl's hosted MCP (https://mcp.firecrawl.dev/v2/mcp) on every request. No API key is required for scrape, search, and parse (rate- limited). Set CHM_AGENT_FIRECRAWL_MCP=false to disable. Register the same URL with a bearer token to override the built-in server.

To restrict which sites Firecrawl may fetch, set CHM_AGENT_FIRECRAWL_ALLOW_DOMAINS to a comma-separated list of hostnames (clickhouse.com,chmonitor.dev,github.com). Unset or empty leaves scrape/crawl/map unrestricted (the OSS default). When set, any url / urls argument whose host is not on the list is blocked before the MCP call; tools with no URL (for example search with only a query) stay allowed. An apex token matches that host and its subdomains (clickhouse.com covers docs.clickhouse.com). A leading *. is subdomain-only (*.example.com matches foo.example.com but not example.com). The same allowlist applies to a user-registered Firecrawl server (mcp.firecrawl.dev or a sanitized name of firecrawl), so renaming the server cannot bypass it.

Conversation persistence

VariableTypeDefaultPurpose
VITE_FEATURE_CONVERSATION_DBBuild-timefalseEnable server-side conversation storage. Derived from canonical CHM_FEATURE_CONVERSATION_DB — set that one before pnpm run build. Requires Clerk.
CONVERSATION_STORE_BACKENDRuntime(auto)Backend: agentstate, d1, postgres, or memory. Auto-selects when unset.

For per-backend env vars and setup, see Conversation history and Store backends.

Where to set these

  • Cloudflare Workerswrangler secret put LLM_API_KEY for secrets; [vars] in wrangler.toml for non-secret values in your own worker. (In this repo's hosted deploy, non-secret config lives in apps/dashboard/.env.production and there is no [vars] block.) See Deploy to Cloudflare.
  • Dockerenvironment: block in docker-compose.yml or --env-file .env. See Deploy with Docker.
  • KubernetesSecret for keys, ConfigMap for non-secret values. See Deploy to Kubernetes.
  • Vercel / self-hosted — environment variables in the project dashboard or .env.local.

Keep LLM keys server-side

Never put LLM API keys in VITE_* variables. Anything prefixed VITE_ is baked into browser JavaScript at build time and is visible to anyone who opens DevTools. Use plain (non-VITE) variables for all keys — they stay on the server.

On this page