Product Telemetry
Anonymous product telemetry — on by default, opt out anytime, no PII, no third-party trackers.
chmonitor includes an anonymous, privacy-first telemetry system. It is on by default and you can turn it off at any time — with a single environment variable. It collects lightweight, aggregate product analytics (five events, three dimensions, no PII) to help the project understand how the dashboard is used at a high level.
This is separate from Self-Tracking, which writes dashboard usage events to your own ClickHouse instance, and from Product Analytics, an opt-in, off-by-default PostHog integration used for the hosted SaaS product's conversion funnel.
Opt out — on by default
Telemetry runs unless you explicitly turn it off. Any one of these disables it:
| Variable | Where it applies | Value to disable |
|---|---|---|
CHM_TELEMETRY | Server runtime (Worker binding / process.env) | off (also 0, false, no) |
VITE_TELEMETRY_ENABLED | Client build-time (inlined by Vite) | off |
DO_NOT_TRACK | Server runtime — the cross-tool opt-out standard | 1 (any truthy value) |
CHM_TELEMETRY_ENDPOINT | Server runtime — collection endpoint | "" (empty = hard kill-switch, no network call) |
DO_NOT_TRACK is a hard override: when set, telemetry is off regardless of
CHM_TELEMETRY. Leaving everything unset keeps telemetry on.
CHM_TELEMETRY=offApplies to Cloudflare Worker, Docker, and Kubernetes deployments. 0, false,
and no are also accepted.
DO_NOT_TRACK=1The cross-tool opt-out standard. A hard
override — telemetry is off regardless of CHM_TELEMETRY.
VITE_TELEMETRY_ENABLED=offOr, to remove the endpoint entirely:
VITE_TELEMETRY_ENDPOINT=""Inlined at bun run build / vite build.
Collection endpoint
Data is sent to the project's collector at
https://telemetry.chmonitor.dev/v1/ping by default. Point it elsewhere (e.g.
your own collector)
by setting CHM_TELEMETRY_ENDPOINT (server) or VITE_TELEMETRY_ENDPOINT
(client build). Setting it to an empty string disables the network call entirely.
What is collected
Events
| Event | When it fires |
|---|---|
app_loaded | Dashboard page first loads |
cluster_connected | A ClickHouse connection is established |
health_viewed | Health page is opened |
queries_viewed | Queries page is opened |
ai_query_sent | A natural language query is sent to the AI agent |
Dimensions (attached to every event)
| Dimension | Values | Example |
|---|---|---|
deploy_target | docker, helm, cf, dev, unknown | docker |
ch_version | MAJOR.MINOR only | 24.8 |
ch_flavor | oss, altinity, cloud, unknown | oss |
What is NOT collected
- Query text, SQL statements, or fingerprints
- Hostnames, IP addresses, or connection strings
- User identifiers, email addresses, or session tokens
- Table names, database names, or schema information
- Any free-text or user-generated content
A defensive redaction layer (redact.ts) runs before every event is emitted. It
drops any prop whose key name implies sensitive content (host, ip, url,
query, sql, email, token, …) and any string value that matches an email,
IPv4/IPv6 address, or URL pattern. When in doubt, it drops.
The ClickHouse version is always truncated to MAJOR.MINOR (e.g. 24.8) — never
the full four-part string — to avoid matching the IPv4 redaction pattern.
Transport and storage
Instance ping (maybePingInstance) runs once per day when due, sending a
hashed install id + deploy target + ClickHouse MAJOR.MINOR to the collection
endpoint. It is a hard no-op when telemetry is disabled, when the endpoint is an
empty string, or in non-browser contexts (SSR/prerender).
The endpoint is received by the telemetry collector — a public, write-only Cloudflare Worker that records to Analytics Engine (and, when D1 is attached, keeps a deduped daily row per install indefinitely). It cannot be read back over HTTP.
Retention
Analytics Engine stores data for 3 months, then auto-deletes. For permanent retention the collector can also write to D1 (Cloudflare's SQLite), which keeps one deduped row per install per day forever. See the collector README.
Event tracking (track()) is typed and redacted, then delivered by the event
sink to the collector's /v1/event endpoint. The sink installs once per app load
and is a hard no-op when telemetry is disabled or no endpoint resolves.
Activation metric
The project tracks a single derived metric called "activation":
activation =
cluster_connectedAND (health_viewedORqueries_viewed)
A session is considered activated when a cluster is connected and the user viewed
either the Health or Queries page in that session. This definition lives in
activation.ts and is the same across the client, any analytics query over the
telemetry store, and this documentation.
Privacy stance
Privacy by design
- One-switch opt-out —
CHM_TELEMETRY=offorDO_NOT_TRACK=1disables it completely, anytime. - Self-hosted data stays put — chmonitor is fully self-hostable; your ClickHouse data never leaves your network regardless of telemetry settings.
- No third-party trackers in this system — telemetry uses only chmonitor's own collector, never a third-party analytics SDK. (chmonitor separately offers an opt-in, off-by-default Product Analytics integration that sends funnel events to PostHog — only if you explicitly configure a key.)
- No PII by design — the event schema and redaction layer are written to make PII collection structurally impossible, not just policy-forbidden.
- Anonymous — only a hashed random install id is sent; it cannot be tied to an identity, and the collector never stores request IPs.
- Hard kill-switch — setting the endpoint env to an empty string stops all network calls regardless of any other setting.
Related
Self-Tracking
Writes dashboard usage events (page visits, query executions) to your own ClickHouse instance. A different system, entirely within your infrastructure.
Product Analytics
Opt-in, off-by-default PostHog funnel analytics for the hosted SaaS product.
Environment Variables
Full reference for all configuration variables.
Self-Tracking
chmonitor writes its own usage events to a ClickHouse table — configure the target table, required grants, and how to suppress writes.
Agent Conversation Storage
Conversation history persistence has moved — find backend setup, env vars, and store selection on the dedicated AI agent pages.