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.
| Variable | Default | Purpose |
|---|---|---|
LLM_API_KEY | — | Provider API key. Required to enable the agent. |
LLM_API_BASE | https://openrouter.ai/api/v1 | OpenAI-compatible base URL. |
LLM_MODEL | openrouter:openrouter/free | Model identifier (provider:modelId). |
LLM_API_KEY=sk-or-...
LLM_API_BASE=https://openrouter.ai/api/v1
LLM_MODEL=openrouter:openrouter/autoProvider-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.
| Variable | Provider | Notes |
|---|---|---|
OPENROUTER_API_KEY | OpenRouter | Alias for LLM_API_KEY when using OpenRouter. |
OPENROUTER_API_BASE | OpenRouter | Override base URL. |
OPENROUTER_REFERER | OpenRouter | HTTP referer sent with requests (for leaderboard attribution). |
OPENROUTER_APP_NAME | OpenRouter | App name sent with requests. |
OPENROUTER_MODELS_API | OpenRouter | Override models list URL. |
NVIDIA_API_KEY | NVIDIA NIM | NIM API key. |
NVIDIA_API_BASE | NVIDIA NIM | NIM base URL (defaults to NVIDIA's hosted endpoint). |
ANYROUTER_API_KEY | AnyRouter | AnyRouter API key. |
ANYROUTER_API_BASE | AnyRouter | AnyRouter base URL. |
Extra models
LLM_EXTRA_MODELS adds entries to the model picker without touching code. Format:
provider:modelId[|contextLength][|description]provider— one ofopenrouter,nvidia,anyroutermodelId— model ID passed to the provider (may contain colons)contextLength— optional integer token count; defaults to128000description— optional display label; defaults tomodelId
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
| Variable | Default | Purpose |
|---|---|---|
CHM_FEATURE_AGENT_ACCESS | public | Set to authenticated to require login before using the agent. |
AGENT_API_TOKEN | — | Shared Bearer token accepted by POST /api/v1/agent. |
AGENT_ENABLE_CONTROL_TOOLS | false | When true, enables optimize_table, kill_query, and kill_mutation. Keep off unless the ClickHouse user is trusted. |
RATE_LIMIT_AGENT_PER_MIN | 10 | Max POST /api/v1/agent requests per minute, applied per signed-in identity and per IP for anonymous callers. |
AGENT_DEBUG | false | When 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=authenticatedProtect the API endpoint with a bearer token:
AGENT_API_TOKEN=your-secret-tokencurl -H "Authorization: Bearer $AGENT_API_TOKEN" \
-d '{"message":"...","hostId":0}' \
https://your-host/api/v1/agentLeast-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_logcan contain credentials embedded in query text (e.g. aCREATE ... IDENTIFIED BYor ans3(...)call another client ran).system.users,system.grants, andsystem.quotasexpose 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.
Conversation persistence
| Variable | Type | Default | Purpose |
|---|---|---|---|
VITE_FEATURE_CONVERSATION_DB | Build-time | false | Enable server-side conversation storage. Derived from canonical CHM_FEATURE_CONVERSATION_DB — set that one before bun run build. Requires Clerk. |
CONVERSATION_STORE_BACKEND | Runtime | (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 Workers —
wrangler secret put LLM_API_KEYfor secrets;[vars]inwrangler.tomlfor non-secret values in your own worker. (In this repo's hosted deploy, non-secret config lives inapps/dashboard/.env.productionand there is no[vars]block.) See Deploy to Cloudflare. - Docker —
environment:block indocker-compose.ymlor--env-file .env. See Deploy with Docker. - Kubernetes —
Secretfor keys,ConfigMapfor 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.