> ## Documentation Index
> Fetch the complete documentation index at: https://docs.claude-mem.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Telemetry

> Anonymous usage analytics — on by default, fully documented, one command to opt out

# 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 analytics property passes a strict whitelist — see [What is collected](#what-is-collected) and [What is NEVER collected](#what-is-never-collected) below. Turning it off is one command:

```bash theme={null}
npx claude-mem telemetry disable
```

The standard [`DO_NOT_TRACK`](https://consoledonottrack.com) 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.

## How instrumentation works

Claude-mem has a single instrumentation path (`instrument()` in `src/services/telemetry/instrument.ts`). Every observable event is described once and fans out to two sinks:

* **The local logger — always, at full fidelity.** Logging keeps working with telemetry off, and the local log never goes through the scrubber. This is where the complete, unredacted detail lives — on your machine.
* **Telemetry — only when consent passes.** The telemetry copy is scrubbed (the [whitelist](#what-is-collected) for structured properties; [allow-then-redact](#error-tracking) for error text) and, for high-volume events, rolled up into per-session/per-window aggregates before anything is sent.

So a single source of truth produces both the rich local log and the minimal, privacy-preserving telemetry — they never drift, and the scrubbing only ever happens on the telemetry branch.

**Note on session replay:** PostHog session replay is **not applicable** to claude-mem. Replay records a browser DOM session; claude-mem is a Node background worker with no browser surface, so there is nothing to replay and it is never enabled.

## 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 activity is sent with `$process_person_profile: false` and builds no profile at all.

**High-volume events are rolled up, not streamed.** Rather than emit one event per compression or context injection, claude-mem aggregates them locally and sends one summary:

* **`observer_turn_rollup`** — a **per-session** accumulator. Every compression in a session folds into one running rollup that is emitted **once, at session end** (instead of one `session_compressed` event per turn). It carries a `rollup_reason` explaining why it flushed (`session_end` | `worker_shutdown` | `safety_flush`) and a `window_seq` partial-flush counter (`0` for a normal one-shot session; `0,1,2,…` only when a long-lived session trips the periodic safety sweep).
* **`context_injected_rollup`** — a **5-minute time-window** accumulator for context injections.

This rollup model is what cut the high-volume event stream by roughly 99.9%. There is no longer any code path that emits raw `session_compressed` or `context_injected` events directly — the only path to PostHog for that activity is the rollup.

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

| Field                                                                              | Example                        | Description                                                                                                                                                |
| ---------------------------------------------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| event name                                                                         | `observer_turn_rollup`         | Which of the events below occurred                                                                                                                         |
| `distinct_id`                                                                      | `7f3c…` (random UUID)          | Anonymous install ID — not derived from you or your machine                                                                                                |
| `version`                                                                          | `13.4.2`                       | claude-mem version                                                                                                                                         |
| `os`                                                                               | `darwin`                       | Operating system platform                                                                                                                                  |
| `os_version`                                                                       | `10.0.22631`                   | OS kernel release string — distinguishes e.g. Windows 10 from 11                                                                                           |
| `is_wsl`                                                                           | `false`                        | Whether running under Windows Subsystem for Linux                                                                                                          |
| `arch`                                                                             | `arm64`                        | CPU architecture                                                                                                                                           |
| `runtime`                                                                          | `bun`                          | `bun` or `node`                                                                                                                                            |
| `runtime_version`                                                                  | `1.2.0`                        | Runtime version string                                                                                                                                     |
| `node_version`                                                                     | `22.14.0`                      | Node.js version string                                                                                                                                     |
| `duration_ms`                                                                      | `1843`                         | How long an operation took                                                                                                                                 |
| `outcome`                                                                          | `ok`                           | Coarse result — a closed enum: ok / error / partial / invalid\_output / aborted                                                                            |
| `error_category`                                                                   | `provider_error`               | Coarse error bucket — never an error message                                                                                                               |
| `locale`                                                                           | `en-US`                        | Language tag                                                                                                                                               |
| `is_ci`                                                                            | `false`                        | Whether running in CI                                                                                                                                      |
| `endpoint`                                                                         | `by-file`                      | Which claude-mem search route — always one of our route names, never a query                                                                               |
| `ide`                                                                              | `claude-code`                  | Installer IDE choice (the installer's own id list)                                                                                                         |
| `provider`                                                                         | `claude`                       | LLM provider choice: claude / gemini / openrouter                                                                                                          |
| `runtime_mode`                                                                     | `worker`                       | worker or server runtime                                                                                                                                   |
| `trigger`                                                                          | `heartbeat`                    | Whether `worker_started` was a real start or the daily heartbeat                                                                                           |
| `count`                                                                            | `7`                            | Integer volume, e.g. observations stored in one compression                                                                                                |
| `has_summary`                                                                      | `true`                         | Whether a compression also produced a session summary                                                                                                      |
| `is_update`                                                                        | `false`                        | Whether an install ran over an existing installation                                                                                                       |
| `interactive`                                                                      | `true`                         | Whether the installer ran in an interactive terminal                                                                                                       |
| `install_method`                                                                   | `npm`                          | Which package manager launched the CLI: npm / bun / pnpm / yarn                                                                                            |
| `bun_version` / `uv_version`                                                       | `1.3.9` / `0.7.2`              | Toolchain versions detected during install                                                                                                                 |
| `claude_code_version`                                                              | `2.0.14`                       | Claude Code CLI version, if detectable                                                                                                                     |
| `mode`                                                                             | `code`                         | Active claude-mem mode id (our mode list)                                                                                                                  |
| `model`                                                                            | `claude-haiku-4-5`             | Model id used for compression                                                                                                                              |
| `hook`                                                                             | `ingest`                       | What triggered a compression: init / ingest / summarize                                                                                                    |
| `observation_type`, `obs_type_*`                                                   | `bugfix`, `3`                  | Observation type buckets (bugfix / discovery / decision / refactor / other) — counts only                                                                  |
| `compression_ms`                                                                   | `2140`                         | Latency of the compression model call                                                                                                                      |
| `tokens_input` / `tokens_output`                                                   | `5800` / `420`                 | Real token usage reported by the model API for one compression                                                                                             |
| `compression_ratio`                                                                | `13.8`                         | tokens\_input ÷ tokens\_output                                                                                                                             |
| `cost_usd`                                                                         | `0.0021`                       | Provider-reported cost of one compression call in USD (Claude SDK / openrouter.ai) — never an estimate, absent when the provider reports none              |
| `endpoint_class`                                                                   | `openrouter`                   | Whether the OpenRouter provider targets openrouter.ai or a custom gateway                                                                                  |
| `rollup_reason`                                                                    | `session_end`                  | Why a per-session `observer_turn_rollup` was emitted — a closed enum: session\_end / worker\_shutdown / safety\_flush                                      |
| `window_seq`                                                                       | `0`                            | Partial-flush sequence number for a rollup — `0` for a normal one-shot session, incrementing only when a long-lived session trips the safety sweep         |
| `observation_count`, `session_count`                                               | `50`, `12`                     | How many observations/sessions fed one context injection                                                                                                   |
| `timeline_depth_days`                                                              | `90`                           | Age in days of the oldest injected observation                                                                                                             |
| `has_session_summary`                                                              | `true`                         | Whether a session summary was part of the injection                                                                                                        |
| `tokens_injected`                                                                  | `17914`                        | Estimated tokens of injected context                                                                                                                       |
| `tokens_saved_vs_naive`                                                            | `144379`                       | Estimated tokens saved vs re-discovering that work                                                                                                         |
| `search_strategy`                                                                  | `timeline`                     | Which retrieval strategy built the injection (our enum)                                                                                                    |
| `db_observation_count`, `db_session_count`, `db_summary_count`, `db_project_count` | `92501`, `5243`, `9698`, `379` | Total rows in the local memory database — counts only, never names or text                                                                                 |
| `db_size_mb`                                                                       | `364.4`                        | Memory database file size in MB                                                                                                                            |
| `install_age_days`                                                                 | `104`                          | Days since the install's first recorded session                                                                                                            |
| `obs_count_7d` / `obs_count_30d`                                                   | `1887` / `10357`               | Observations stored in the last 7 / 30 days                                                                                                                |
| `days_since_last_obs`                                                              | `0`                            | Days since the most recent observation was stored                                                                                                          |
| `result_count`                                                                     | `12`                           | How many results a memory search returned — count only, never the results or the query                                                                     |
| `chroma_available`                                                                 | `true`                         | Whether the vector-search backend was reachable for a search (false = fell back to full-text search)                                                       |
| `fallback_reason`                                                                  | `none`                         | Why a search fell back from vector search: none / chroma\_connection / chroma\_error / chroma\_not\_initialized — a closed enum, never an error message    |
| `invalid_output_class`                                                             | `idle`                         | Coarse class of an unusable compression output: xml / idle / prose (`xml` = looked like the expected format but failed to parse) — never the output itself |
| `consecutive_invalid_outputs`                                                      | `0`                            | Legacy unusable-output counter, retained as a scrubbed numeric field                                                                                       |
| `respawn_triggered`                                                                | `false`                        | Legacy recovery flag for old invalid-output restarts                                                                                                       |
| `abort_reason`                                                                     | `idle`                         | Why a compression session was aborted: idle / shutdown / overflow / restart\_guard / quota / none — a closed enum                                          |
| `previous_shutdown`                                                                | `clean`                        | How the previous worker run ended, detected at startup: crash / clean / unknown                                                                            |
| `previous_uptime_seconds`                                                          | `86400`                        | How long the previous worker run was up, in whole seconds                                                                                                  |
| `uptime_seconds`                                                                   | `3600`                         | How long the worker was up when it stopped, in whole seconds                                                                                               |
| `shutdown_reason`                                                                  | `restart`                      | Why the worker stopped: stop / restart / signal                                                                                                            |
| `process_rss_mb`                                                                   | `187`                          | Worker process resident memory, integer megabytes                                                                                                          |
| `heap_used_mb`                                                                     | `92`                           | Worker JS heap in use, integer megabytes                                                                                                                   |
| `hook_type`                                                                        | `observation`                  | Which hook kind failed: context / session-init / observation / summarize / file-context — our handler names                                                |
| `error_mode`                                                                       | `worker_unavailable`           | Coarse hook failure mode: worker\_unavailable / blocking\_error — never an error message                                                                   |
| `consecutive_failures`                                                             | `3`                            | How many hook failures occurred in a row (the fail-loud counter)                                                                                           |
| `threshold_tripped`                                                                | `true`                         | Whether 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](#what-is-never-collected).

### Events

| Event                     | When                                                                                                                                                                                                              | Extra properties                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `install_completed`       | `npx claude-mem install` finishes                                                                                                                                                                                 | `ide`, `provider`, `runtime_mode`, `is_update`, `outcome`, `duration_ms`, `interactive`, `install_method`, `bun_version`, `uv_version`, `claude_code_version`                                                                                                                                                                                                                                                                                                                                |
| `install_failed`          | The installer aborts                                                                                                                                                                                              | `error_category` (our error-taxonomy id), `interactive`, `install_method`, `claude_code_version`                                                                                                                                                                                                                                                                                                                                                                                             |
| `uninstall_completed`     | `npx claude-mem uninstall` finishes                                                                                                                                                                               | —                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `worker_started`          | The background worker starts, plus one heartbeat per 24h of uptime                                                                                                                                                | `trigger` (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`   |
| `observer_turn_rollup`    | Emitted **once per session, at session end** — a per-session rollup that aggregates every compression in that session (stored observations, invalid-output drops, failures, aborts) instead of one event per turn | `rollup_reason` (session\_end / worker\_shutdown / safety\_flush), `window_seq`, aggregated `outcomes_*` counts, `total_tokens_input`, `total_tokens_output`, `total_cost_usd`, `avg_duration_ms`, `avg_compression_ms`, `top_model`, `observations_created` (sum of observations generated in the session — pairs with `total_cost_usd` to derive cost per observation), summed `obs_type_*` buckets, `window_start_ts`, plus the per-turn fields it summarizes (`provider`, `ide`, `hook`) |
| `context_injected_rollup` | A 5-minute time-window rollup of context injections (stored memory injected into new sessions)                                                                                                                    | aggregated `outcomes_ok` / `outcomes_error` counts, `count`, `total_tokens`, `avg_tokens`, `total_observations_injected` (sum of observations served from cache into prompts), `total_tokens_saved_vs_naive`, `window_start_ts`                                                                                                                                                                                                                                                              |
| `search_performed`        | A memory search runs (never the query text)                                                                                                                                                                       | `endpoint`, `outcome`, `duration_ms`, `result_count`, `search_strategy`, `chroma_available`, `fallback_reason`                                                                                                                                                                                                                                                                                                                                                                               |
| `worker_stopped`          | The background worker shuts down gracefully                                                                                                                                                                       | `uptime_seconds`, `shutdown_reason` (stop / restart / signal)                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `hook_failed`             | A claude-mem hook fails hard — the worker is unreachable past the fail-loud threshold, or a blocking error occurs                                                                                                 | `hook_type`, `error_mode`, `consecutive_failures`, `threshold_tripped`                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `error_occurred`          | The worker returns an HTTP 5xx                                                                                                                                                                                    | `error_category`                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `$exception`              | A real error is captured for [error tracking](#error-tracking) — consent-gated and independently kill-switchable                                                                                                  | Redacted `error_type` / `error_message` / `error_stack`, `occurrence_count`, plus whitelisted context. See [Error tracking](#error-tracking) for exactly what is kept vs. redacted                                                                                                                                                                                                                                                                                                           |

## Error tracking

Claude-mem captures real errors to PostHog Error Tracking as `$exception` events. **This is a deliberate change from the old strictly-whitelist-only posture:** error messages and stack traces are free-form text, so the property whitelist (which only passes known closed-set keys) would drop them entirely. Instead, error text takes a separate **allow-then-redact** path (`src/services/telemetry/error-scrub.ts`): we keep the diagnostic text and aggressively strip anything that could leak PII or secrets.

**What is kept (redacted):**

* The error **type** (constructor name, e.g. `TypeError`), capped to 100 chars.
* The error **message**, redacted and capped to 500 chars.
* The **stack trace** — only the top 10 frames, each redacted, capped to \~2KB total.
* An `occurrence_count` (how many times this error fingerprint fired in the current window).

**What is redacted out of that text** (replaced with `[REDACTED]`, in this order):

* Home directory (`/Users/you` → `~`) — first, so a username embedded in the home path never survives.
* Absolute filesystem paths → collapsed to basename (POSIX, Windows drive, and UNC paths) — keeps "which file" without the directory tree.
* URL / connection-string credentials and query strings — userinfo (`user:pass@`) and `?…`/`#…` are stripped from any `scheme://…` (http, ws, postgres, redis, mongodb+srv, amqp, …), so DB connection-string creds and signed-URL tokens die.
* Emails.
* API tokens and keys: provider-prefixed keys (`sk-`, `phc_`, `ghp_`, `xoxb-`, …), Bearer tokens, **AWS access key IDs** (`AKIA…`), JWTs, UUIDs, long hex blobs (24+ chars), and generic high-entropy tokens.
* **IPv4 addresses** (internal IPs/hostnames that leak in network errors).

The redaction pipeline is **pure and never throws** — hostile input (null, circular, objects with throwing getters, 200KB blobs) always yields a safe, bounded result, because telemetry must never break or block the worker. Raw input is hard-capped at 8KB before any regex runs (ReDoS defense).

**Rate-limiting.** At most **one `$exception` per error fingerprint per 60 seconds**. Errors are fingerprinted by type + a normalized message template + top stack frame, so a storm of the "same" error with varying ids/numbers dedupes to a single send with an occurrence count attached. This applies to both our manual captures and any SDK autocapture. (Autocapture is additionally re-scrubbed before send — raw source-context lines that posthog-node reads off disk are deleted, and filenames are redacted to basenames.)

**Consent-gated, with an independent kill-switch.** Error capture is gated by the normal telemetry consent chain (opting out of telemetry disables errors too) **and** by a separate `CLAUDE_MEM_TELEMETRY_ERRORS` switch — see [How to opt out](#how-to-opt-out-four-ways). No person profile is built for `$exception` events (`$process_person_profile: false`).

<Warning>
  **One-way door.** Unlike the whitelisted analytics events — every field of which is a number, boolean, or value from a closed set — `$exception` events carry real (redacted) message text. Once an error message is ingested into PostHog, it **cannot be selectively deleted** after the fact. This is a deliberate trade-off made to get actionable crash diagnostics, mitigated by aggressive redaction, rate-limiting, consent-gating, and the `CLAUDE_MEM_TELEMETRY_ERRORS=0` kill-switch. If you would rather send nothing free-form, set that variable.
</Warning>

## Historical backfill

Telemetry shipped later than claude-mem itself, so installs that predate it have activity the live events never saw. On the first worker start after upgrading, claude-mem performs a **one-time** backfill of that pre-telemetry history — anonymized counts only, passed through the same whitelist scrubber as everything else:

| Event                 | When (timestamp)                                                   | What it carries                                                                                                                                                                                                                                                                                                                                                                                                                 |
| --------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `historical_activity` | One per day the install was active, stamped on that historical day | Daily activity counts only: observations, sessions, summaries, prompts, distinct-project **count**, observation-type buckets (`obs_type_*`), session outcomes (`session_completed_count` / `session_failed_count`), per-platform session counts (`sessions_claude_count` etc.), `subagent_obs_count`, `discovery_tokens`, plus `backfilled: true`. Profile-less (`$process_person_profile: false`), like all high-volume events |
| `install_inferred`    | Once, stamped on the install's first recorded activity day         | `first_active_date` (a date string, e.g. `2025-10-19`) and `backfilled: true`                                                                                                                                                                                                                                                                                                                                                   |

Like everything else, these are counts and closed-set values only — **never titles, prompts, file contents, or project names**. The same anonymous install UUID identifies them, and every property passes the whitelist scrubber.

A few things worth knowing:

* **It runs once.** A completion marker (`backfill.json` in the claude-mem data directory) is written after a successful send and prevents the backfill from ever running again. Until a run succeeds, no marker is written, so a failed attempt simply retries on the next worker start.
* **It honors the exact same consent gates as live telemetry** — `DO_NOT_TRACK`, `CLAUDE_MEM_TELEMETRY=0`, and `enabled: false` in `telemetry.json` all block it, and [debug mode](#debug-mode) prints the would-be payload without sending.
* **Opting out before the first worker start after upgrading prevents it entirely.** Nothing is sent and no marker is written while you are opted out — though if you opt back in later, the backfill will then run.
* **Location is upload-time, not historical.** The coarse location PostHog derives at ingestion (see above) reflects where the events were uploaded from, not where you were on the historical dates they describe.

## What is NEVER collected

| Never collected                 | Notes                                                                                                                                                                                                                   |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prompts or conversation content | Not even truncated or hashed                                                                                                                                                                                            |
| File paths or directory names   | Redacted out of analytics entirely, and redacted out of error text (home dir → `~`, absolute paths → basename) — see [Error tracking](#error-tracking)                                                                  |
| Source code                     | In any form — including the source-context lines posthog-node would otherwise attach to autocaptured exceptions (deleted before send)                                                                                   |
| Project or repository names     | Including git remotes and branch names                                                                                                                                                                                  |
| Search queries                  | Only the fact that a search happened                                                                                                                                                                                    |
| IP addresses                    | Never 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 identifiers | Not even hashed MAC addresses or hostnames                                                                                                                                                                              |
| Environment variable values     | Ever                                                                                                                                                                                                                    |
| Emails, usernames, or any PII   | Ever — emails, tokens, keys, and credentials are redacted out of error text too                                                                                                                                         |

**One honest exception: error messages.** Since the addition of [error tracking](#error-tracking), redacted error **messages and stack traces** ARE collected (as `$exception` events) — that is a deliberate change from the previous coarse-category-only posture, and it is consent-gated with its own [kill-switch](#how-to-opt-out-four-ways). Raw paths, prompts, project names, source code, and model output are still **never** collected — they are stripped from the error text before it leaves your machine.

Analytics properties are enforced in code: they go through a whitelist (only the fields in the [What is collected](#what-is-collected) table 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 analytics field that could carry free-form user content. Error text is the one free-form path, and it goes through the separate allow-then-redact scrubber instead.

## 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](https://consoledonottrack.com). 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 file** — `enabled: false` in `telemetry.json` (see below).
4. **CLI command**:
   ```bash theme={null}
   npx claude-mem telemetry disable
   ```

### Error tracking opt-out (independent)

[Error tracking](#error-tracking) (`$exception` events with redacted message/stack) can be disabled **on its own**, without turning off anonymous analytics:

```bash theme={null}
CLAUDE_MEM_TELEMETRY_ERRORS=0   # also accepts 'false' / 'off'
```

This is the one telemetry path that carries free-form (redacted) text and is a [one-way door](#error-tracking) once ingested, so it has its own kill-switch for operators who are fine with anonymous counters but not error text. It defaults on whenever telemetry consent is on; any of the four opt-outs above also disables it implicitly (no consent ⇒ no errors).

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

```bash theme={null}
npx claude-mem telemetry status
```

## Debug mode

Want to see exactly what would be sent? Set:

```bash theme={null}
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 theme={null}
{
  "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.
