CodeGraph collects a small set of anonymous usage statistics — which commands and tools get used, which languages get indexed, which agents drive usage — so we can tell which of the 20+ languages and 8 agent integrations deserve the most work. This page is the complete list of what is collected. If a field isn't on this page, it isn't collected; the ingest endpoint enforces this list as an allowlist and is itself public, auditable code in this repository.
Any of these works, permanently:
codegraph telemetry off # stores your choice (and deletes any unsent data)export CODEGRAPH_TELEMETRY=0 # per-shell / per-CI override
export DO_NOT_TRACK=1 # the cross-tool standard — always honoredcodegraph telemetry status shows the current state, what decided it, and your machine ID.
The interactive installer (codegraph install) asks up front with a visible default-on
toggle and never re-asks. If you never saw the installer (e.g. npx straight into init),
a one-line notice is printed to stderr before the first time anything is sent.
Off means off: when disabled, CodeGraph records nothing, opens no connection to the telemetry endpoint, and sends no "opted out" ping.
Separately from telemetry, the MCP server checks GitHub for a newer release in the
background (at most once a day) so it can tell you an update exists — it fetches a
version number and sends nothing about you or your machine. DO_NOT_TRACK=1 disables
this check too; to turn off only the update check, use CODEGRAPH_NO_UPDATE_CHECK=1.
Every payload carries this envelope:
| field | example | notes |
|---|---|---|
machine_id |
b3a8c1… |
random UUID minted on first send — derived from nothing |
codegraph_version |
0.9.9 |
|
os / arch |
darwin / arm64 |
platform identifiers only |
node_major |
22 |
major version only |
ci |
false |
whether the CI env var was set |
schema_version |
2 |
bumped when this page changes (v2 dropped the index event's sqlite_backend field) |
And one of four events:
install— whencodegraph installconfigures agents: which agents (["claude","cursor",…]), global vs project-local, and whether it was a fresh install, an upgrade, or a re-run.index— when a full index completes: the language names present (e.g.["typescript","go"]), the file count as a coarse bucket (<100,100-1k,1k-10k,10k+), and the duration as a bucket (<10s,10-60s,1-5m,5m+).usage_rollup— one line per day per tool: the tool or CLI command name (e.g.codegraph_explore,init), how many times it ran, how many errored, and — for MCP tools — the connecting agent's name and version from the MCP handshake (e.g.Claude Code 2.1). The Claude Code prompt hook also counts its gate decision (fired fully, fired as a hint, or did nothing — fixed counter names likeprompt-hook-gate-medium-segment); the prompt itself is never read, stored, or sent.uninstall— whencodegraph uninstall/uninitruns: which agents were removed.
Usage is aggregated locally into daily totals before anything is sent — there is no per-call event stream, and nothing is sent in real time.
- No source code. No file paths, file names, directory names, repository names or URLs, symbol names, search queries, or anything else derived from the contents of an indexed project.
- No IP addresses. The ingest endpoint never reads, logs, or stores the client IP — and there is no analytics vendor downstream that could. No geolocation.
- No fingerprinting. The machine ID is a random UUID stored in
~/.codegraph/telemetry.json— delete that file (or runcodegraph telemetry off, thenon) and the old ID is gone forever, with no way to reconnect it. - No personal data. No usernames, hostnames, emails, or environment variables.
Events POST to telemetry.getcodegraph.com — a first-party endpoint whose complete
source lives in telemetry-worker/ in this repository. It validates
every event and property against the allowlist above (anything else is dropped), never
reads the client IP, and rate-limits per machine ID. Sends are fire-and-forget with a
short timeout: offline or air-gapped machines buffer a bounded local file (256 KB cap)
and never retry-loop, log errors, or slow a command down. Telemetry never adds latency to
MCP tool calls — recording is an in-memory counter.
Accepted events are written to our own database on Cloudflare (D1) and go nowhere else. No third-party analytics vendor receives any of this data, because the ingest endpoint makes no outbound requests at all — its source is the entire path your events take, and there is nothing after it. This is a stronger guarantee than a promise not to share: there is no second party to share with.
What is kept is checkable rather than asserted. The storage schema —
telemetry-worker/migrations/0001_init.sql,
checked in beside the endpoint that writes it — is the complete list of what a row can
hold, with a comment on every column.
Individual events are deleted after 90 days. What outlives them is anonymous daily totals: counts per day of things like operating system, version, and language, plus which days each machine ID was active so returning-user numbers survive. No event details, and still nothing that identifies a person or a codebase.
The engineering contract behind all of this — including the rule that schema changes must
update this page, the client, and the public endpoint in one PR — is in
docs/design/telemetry.md.