chmonitor
Advanced

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:

VariableWhere it appliesValue to disable
CHM_TELEMETRYServer runtime (Worker binding / process.env)off (also 0, false, no)
VITE_TELEMETRY_ENABLEDClient build-time (inlined by Vite)off
DO_NOT_TRACKServer runtime — the cross-tool opt-out standard1 (any truthy value)
CHM_TELEMETRY_ENDPOINTServer 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=off

Applies to Cloudflare Worker, Docker, and Kubernetes deployments. 0, false, and no are also accepted.

DO_NOT_TRACK=1

The cross-tool opt-out standard. A hard override — telemetry is off regardless of CHM_TELEMETRY.

VITE_TELEMETRY_ENABLED=off

Or, 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

EventWhen it fires
app_loadedDashboard page first loads
cluster_connectedA ClickHouse connection is established
health_viewedHealth page is opened
queries_viewedQueries page is opened
ai_query_sentA natural language query is sent to the AI agent

Dimensions (attached to every event)

DimensionValuesExample
deploy_targetdocker, helm, cf, dev, unknowndocker
ch_versionMAJOR.MINOR only24.8
ch_flavoross, altinity, cloud, unknownoss

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_connected AND (health_viewed OR queries_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-outCHM_TELEMETRY=off or DO_NOT_TRACK=1 disables 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.

On this page