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 pnpm 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.

CLI telemetry (a separate stream)

The standalone chm CLI and its install.sh installer emit their own anonymous telemetry — tracked as a separate stream (source=cli) from the dashboard telemetry above, in its own cli_daily table. Same privacy posture: anonymous, opt-out, no PII.

It is best-effort and non-blocking: the CLI fires the ping on a background thread with a sub-second timeout and never delays a command's exit by more than ~300ms, and never fails a command if the network is down.

Opt out (any one disables it)

The CLI honours the same switches as the dashboard:

CHM_TELEMETRY=off              # also 0 / false / no
DO_NOT_TRACK=1                 # cross-tool standard, hard override
CHM_TELEMETRY_ENDPOINT=""      # hard kill-switch — no network call

The install.sh install ping additionally skips automatically when CI=true.

What the CLI collects

FieldValuesNotes
install_id64-char opaque hexRandom id persisted at $XDG_CONFIG_HOME/chmonitor/cli-id (default ~/.config/chmonitor/cli-id). Counts distinct installs only; maps to no identity.
eventcli_install, cli_run, cli_diagnoseInstall ping vs. a command run vs. a diagnose run.
commandhosts, chart, table, tui, diagnose, installThe subcommand name.
cli_versionsemver (e.g. 0.1.0)The chm version.
oslinux, macos, windows, unknown
archx86_64, aarch64, unknown

It sends nothing else — no ClickHouse host, query text, command arguments, file paths, usernames, or IPs. The install-script ping uses an ephemeral random id (one-shot installs do not persist identity).

The public analytics page shows a dedicated CLI section (installs over time, active CLI users, runs by command, version distribution, and OS/arch split), sourced from the collector's aggregate-only /v1/summary cli block.

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