Back to Claude Mem

Telemetry

docs/public/telemetry.mdx

13.5.613.4 KB
Original Source

Telemetry

Claude-mem includes anonymous usage analytics (via PostHog) to help prioritize fixes and features.

It is on by default (opt-out). Events are anonymous, identified only by a random install UUID, and every property passes a strict whitelist — see What is collected and What is NEVER collected below. Turning it off is one command:

bash
npx claude-mem telemetry disable

The standard DO_NOT_TRACK environment variable is also honored and overrides everything. The installer asks once at the end of npx claude-mem install so the default is never silent for new installs — your answer (either way) is remembered and never re-asked, and the prompt is skipped entirely when DO_NOT_TRACK is set or in CI/non-interactive installs.

What is collected

When enabled, events are anonymous and identified only by a random install UUID (crypto.randomUUID(), generated locally on first use).

Low-volume lifecycle events (install_*, uninstall_completed, worker_started) build an analytics profile keyed to that random UUID so aggregate retention and cohort statistics are computable — the profile contains nothing beyond the whitelisted fields below (platform, version, IDE/provider choice). It is not, and cannot be, connected to you: there is no name, email, IP, hardware ID, or any other identifier. All high-volume events (session_compressed, search_performed, context_injected, error_occurred) are sent with $process_person_profile: false and build no profile at all.

Every event property passes through a strict whitelist scrubber — any key not in this table is silently dropped before sending:

FieldExampleDescription
event namesession_compressedWhich of the events below occurred
distinct_id7f3c… (random UUID)Anonymous install ID — not derived from you or your machine
version13.4.2claude-mem version
osdarwinOperating system platform
os_version10.0.22631OS kernel release string — distinguishes e.g. Windows 10 from 11
is_wslfalseWhether running under Windows Subsystem for Linux
archarm64CPU architecture
runtimebunbun or node
runtime_version1.2.0Runtime version string
node_version22.14.0Node.js version string
duration_ms1843How long an operation took
outcomeokCoarse result — a closed enum: ok / error / partial / invalid_output / aborted
error_categoryprovider_errorCoarse error bucket — never an error message
localeen-USLanguage tag
is_cifalseWhether running in CI
endpointby-fileWhich claude-mem search route — always one of our route names, never a query
ideclaude-codeInstaller IDE choice (the installer's own id list)
providerclaudeLLM provider choice: claude / gemini / openrouter
runtime_modeworkerworker or server runtime
triggerheartbeatWhether worker_started was a real start or the daily heartbeat
count7Integer volume, e.g. observations stored in one compression
has_summarytrueWhether a compression also produced a session summary
is_updatefalseWhether an install ran over an existing installation
interactivetrueWhether the installer ran in an interactive terminal
install_methodnpmWhich package manager launched the CLI: npm / bun / pnpm / yarn
bun_version / uv_version1.3.9 / 0.7.2Toolchain versions detected during install
claude_code_version2.0.14Claude Code CLI version, if detectable
modecodeActive claude-mem mode id (our mode list)
modelclaude-haiku-4-5Model id used for compression
hookingestWhat triggered a compression: init / ingest / summarize
observation_type, obs_type_*bugfix, 3Observation type buckets (bugfix / discovery / decision / refactor / other) — counts only
compression_ms2140Latency of the compression model call
tokens_input / tokens_output5800 / 420Real token usage reported by the model API for one compression
compression_ratio13.8tokens_input ÷ tokens_output
cost_usd0.0021Provider-reported cost of one compression call in USD (Claude SDK / openrouter.ai) — never an estimate, absent when the provider reports none
endpoint_classopenrouterWhether the OpenRouter provider targets openrouter.ai or a custom gateway
observation_count, session_count50, 12How many observations/sessions fed one context injection
timeline_depth_days90Age in days of the oldest injected observation
has_session_summarytrueWhether a session summary was part of the injection
tokens_injected17914Estimated tokens of injected context
tokens_saved_vs_naive144379Estimated tokens saved vs re-discovering that work
search_strategytimelineWhich retrieval strategy built the injection (our enum)
db_observation_count, db_session_count, db_summary_count, db_project_count92501, 5243, 9698, 379Total rows in the local memory database — counts only, never names or text
db_size_mb364.4Memory database file size in MB
install_age_days104Days since the install's first recorded session
obs_count_7d / obs_count_30d1887 / 10357Observations stored in the last 7 / 30 days
days_since_last_obs0Days since the most recent observation was stored
result_count12How many results a memory search returned — count only, never the results or the query
chroma_availabletrueWhether the vector-search backend was reachable for a search (false = fell back to full-text search)
fallback_reasonnoneWhy a search fell back from vector search: none / chroma_connection / chroma_error / chroma_not_initialized — a closed enum, never an error message
fabrication_detectedfalseWhether a compression's output referenced commit hashes that don't exist in your repo (a model-trust check)
fabricated_count0How many nonexistent commit hashes were detected — count only, never the hashes
invalid_output_classidleCoarse class of an unusable compression output: xml / idle / prose / poisoned (xml = looked like the expected format but failed to parse) — never the output itself
consecutive_invalid_outputs3How many unusable outputs occurred in a row before recovery
respawn_triggeredtrueWhether the compression agent was restarted after repeated unusable output
abort_reasonidleWhy a compression session was aborted: idle / shutdown / overflow / restart_guard / quota / poisoned / none — a closed enum
previous_shutdowncleanHow the previous worker run ended, detected at startup: crash / clean / unknown
previous_uptime_seconds86400How long the previous worker run was up, in whole seconds
uptime_seconds3600How long the worker was up when it stopped, in whole seconds
shutdown_reasonrestartWhy the worker stopped: stop / restart / signal
process_rss_mb187Worker process resident memory, integer megabytes
heap_used_mb92Worker JS heap in use, integer megabytes
hook_typeobservationWhich hook kind failed: context / session-init / observation / summarize / file-context — our handler names
error_modeworker_unavailableCoarse hook failure mode: worker_unavailable / blocking_error — never an error message
consecutive_failures3How many hook failures occurred in a row (the fail-loud counter)
threshold_trippedtrueWhether the consecutive-failure count reached the fail-loud threshold

One value is derived server-side rather than sent by the client: PostHog resolves the request's sender IP to a coarse location (country / region / city) at ingestion, before the IP itself is discarded. The client never attaches an IP to any event, and the raw IP is never stored — see What is NEVER collected.

Events

EventWhenExtra properties
install_completednpx claude-mem install finisheside, provider, runtime_mode, is_update, outcome, duration_ms, interactive, install_method, bun_version, uv_version, claude_code_version
install_failedThe installer abortserror_category (our error-taxonomy id), interactive, install_method, claude_code_version
uninstall_completednpx claude-mem uninstall finishes
worker_startedThe background worker starts, plus one heartbeat per 24h of uptimetrigger (start / heartbeat), duration_ms, ide, provider, mode, runtime_mode, process memory (process_rss_mb, heap_used_mb), the install snapshot: db_observation_count, db_session_count, db_summary_count, db_project_count, db_size_mb, install_age_days, obs_count_7d, obs_count_30d, days_since_last_obs; on a real start also crash detection: previous_shutdown (crash / clean / unknown) and, after a clean shutdown, previous_uptime_seconds
session_compressedA session compression stores observations, repeated unusable model output forces an agent respawn, the compression generator fails, or a compression session is abortedoutcome (ok / invalid_output / error / aborted), duration_ms, count, has_summary, provider, model, ide, hook, compression_ms, observation_type, obs_type_*, tokens_input, tokens_output, compression_ratio, cost_usd, endpoint_class, fabrication_detected, fabricated_count; on an invalid-output respawn invalid_output_class, consecutive_invalid_outputs, respawn_triggered; on failure error_category; on abort abort_reason
context_injectedStored memory is injected into a new sessionoutcome, duration_ms, mode, provider, search_strategy, observation_count, session_count, timeline_depth_days, has_session_summary, obs_type_*, tokens_injected, tokens_saved_vs_naive
search_performedA memory search runs (never the query text)endpoint, outcome, duration_ms, result_count, search_strategy, chroma_available, fallback_reason
worker_stoppedThe background worker shuts down gracefullyuptime_seconds, shutdown_reason (stop / restart / signal)
hook_failedA claude-mem hook fails hard — the worker is unreachable past the fail-loud threshold, or a blocking error occurshook_type, error_mode, consecutive_failures, threshold_tripped
error_occurredThe worker returns an HTTP 5xxerror_category

What is NEVER collected

Never collectedNotes
Prompts or conversation contentNot even truncated or hashed
File paths or directory namesIncluding cwd, transcript paths, data dir
Source codeIn any form
Project or repository namesIncluding git remotes and branch names
Search queriesOnly the fact that a search happened
Error messages or stack tracesOnly a coarse category string
IP addressesNever attached to events by the client; the sender IP is used transiently at ingest to derive coarse location (country / region / city), then discarded — the analytics project is configured to never store sender IPs
Hardware or machine identifiersNot even hashed MAC addresses or hostnames
Environment variable valuesEver
Emails, usernames, or any PIIEver

These are enforced in code: properties go through a whitelist (only the fields in the table above survive), not a blocklist. Every whitelisted field is either a number, a boolean, or a value from a closed set we define — there is no field that could carry free-form user content.

How to opt out (four ways)

Any one of these keeps telemetry off — they are checked in this order, first match wins:

  1. DO_NOT_TRACK — the universal opt-out. Set DO_NOT_TRACK=1 and telemetry is forced off, overriding everything else.
  2. CLAUDE_MEM_TELEMETRY=0 (also false / off) — environment override. (CLAUDE_MEM_TELEMETRY=1 conversely forces it on.)
  3. Telemetry config fileenabled: false in telemetry.json (see below).
  4. CLI command:
    bash
    npx claude-mem telemetry disable
    

Check the current state — and which of the four layers decided it — anytime:

bash
npx claude-mem telemetry status

Debug mode

Want to see exactly what would be sent? Set:

bash
CLAUDE_MEM_TELEMETRY_DEBUG=1

With debug mode on (and telemetry enabled), every would-be event payload is printed to stderr and nothing is sent over the network.

Where the config lives

Consent and the anonymous install ID are stored in telemetry.json inside the claude-mem data directory:

  • Default: ~/.claude-mem/telemetry.json
  • Or $CLAUDE_MEM_DATA_DIR/telemetry.json if you've overridden the data dir
json
{
  "enabled": false,
  "installId": "<random UUID>",
  "decidedAt": "2026-06-09T21:00:00.000Z"
}

The enabled field is only present once you've made an explicit choice (installer prompt, telemetry enable, or telemetry disable). A file with just an installId means no decision was recorded and the default (on) applies. Delete the file to reset completely — a fresh install ID is generated on next use.