Off until you say yes.
The first time you run Poppy in a terminal, it asks once whether to send anonymous usage events. The default is no. Nine events if you accept, no memory content, EU region.
The first-run question
Telemetry is off when you install Poppy. The first time you run it in a terminal, it asks once, on stderr:
Poppy can send anonymous usage events so we can see which features get used.
Events carry counts, lengths and a fixed set of event names. Memory content,
recall queries and project names are never sent. You can change this later
with poppy telemetry on, or poppy telemetry off.
Send anonymous usage events? [y/N]:The default is no, so pressing Enter declines. Until you answer yes, Poppy sends nothing and generates no identifier for you. Either answer counts, so you are asked once: the answer is written to ~/.poppy/config.json and read from there afterwards. Pressing Ctrl-C leaves the question unanswered, telemetry stays off, and the command you actually ran carries on.
When Poppy does not ask
The question only appears when there is a person at the keyboard to answer it, which means stdin, stdout and stderr all have to be a terminal. None of the following ever see it, and none of them send anything while the question is unanswered:
- Hooks and the MCP server (
poppy serve) - Daemon commands, and detached background workers such as auto-sync
- Any piped or redirected run, and any command asked for
--jsonoutput --helpoutput, and unattended installs (poppy setup <client> --yes)- Terminals being driven by a coding agent or a CI runner
Change your answer
poppy telemetry status # on, off, or not answered yet
poppy telemetry on
poppy telemetry off
# session-only env var, overrides everything else
POPPY_TELEMETRY_OFF=1 poppy remember "..."Both poppy telemetry on and poppy telemetry off count as an answer, so running either one up front means you are never asked. poppy telemetry status tells the three states apart and says where the setting came from:
Telemetry: off (not answered yet)
Nothing is sent until you answer. Turn it on with: poppy telemetry onPOPPY_TELEMETRY_OFF=1 wins over everything else, including a stored yes. It overrides the setting for the runs it applies to without changing what is stored.
What gets sent
Nine events, fire-and-forget, fully non-blocking (the CLI exits regardless of network). Nothing below is sent unless you have answered yes. This table lists every event Poppy can emit:
| Event | Fires on | Properties |
|---|---|---|
cli_install | Once, before the first event from a machine | version (Poppy version), python_version, platform (darwin, linux, win32) |
memory_write | Every stored memory (poppy remember, the MCP remember tool, and automatic capture) | memory_type (fact, decision, preference, lesson, summary, context), has_project (true/false, never the project name), source (source app: manual, claude-code, cursor, ...). No content. |
recall_call | poppy recall | query_length (character count, length only, never the query itself), result_count, engine (engine name) |
agent_setup | Every poppy setup <client> | agent (client name, for example claude-code) |
setup_completed | Once per machine, on the first poppy setup <client> | agent (client name) |
consent_granted | Once per machine, when you turn automatic capture on | via (consent_cmd, setup_yes, or setup_prompt) |
consent_pending_shown | Once per machine, when the consent nudge is first shown | channel (session_start or setup) |
first_autocapture_stored | Once per machine, the first time automatic capture stores a memory | trigger (mid_session, post_compact, or session_end) |
closed_loop | Once per machine, the first time a memory captured in an earlier session is recalled back into a session | channel (session_start, recall_prompt, or recall_file), injected_count, loop_count (counts only) |
The last five are one-time funnel milestones: each fires at most once per machine. The first four can fire repeatedly.
Never sent: memory content, recall query text, project names, file paths, session ids, config values, API keys, or anything else you type into Poppy. Properties are counts, lengths, version strings, and fixed enum values only.
Update check
Separate from telemetry, poppy doctor may make one anonymous request to PyPI at most once every 24 hours to see whether a newer poppy-memory release exists. The request carries only the installed version in its User-Agent and no identifiers, and the result is cached in ~/.poppy/update_check.json. Normal commands never make this call; they can only surface a once-per-version upgrade hint already sitting in that cache.
Turning telemetry off turns this check off as well, and so does leaving the first-run question unanswered. You do not need to disable it separately: a fresh install makes no network request of any kind, to PyPI or to PostHog, until you have said yes. If you want telemetry on but the update check off, that is what poppy config set update_check off and POPPY_UPDATE_CHECK_OFF=1 are for. With telemetry off, the only network calls Poppy makes are the ones you explicitly asked for: cloud sync if you configured it, and model downloads on first run.
How identity works
Say yes and events are tagged with a stable device_id: a random UUID generated on your machine at that moment and stored in ~/.poppy/analytics.json, not derived from hardware, accounts, or anything identifying. Decline and no ID is generated at all. When you run poppy setup trags, the device_id is shared with the Trags server so it can link this machine’s telemetry to your account. Without cloud sync, the device_id stays purely local and uncorrelated.
Where it goes
PostHog Cloud EU (https://eu.i.posthog.com), an EU-hosted PostHog project, which keeps telemetry within GDPR scope for our Norway-based business.
Retention: events are retained under PostHog’s platform retention. We do not yet run a purge or aggregation job of our own; when we adopt a fixed retention window, this page will say so. You can ask us to delete your telemetry data at any time.
Why we capture this
We’re a small team measuring whether the product is being used. What we look at:
- Day-7 retention: do people come back?
- Write and recall volume: are active users building up a store?
- Setup completion rate: does
poppy setup <agent>get run per agent? - Engine selection: which engine do recalls run on?
- Activation funnel: how far machines get from install through consent to the first time a captured memory is recalled back (the one-time milestone events exist for exactly this).
None of this requires the content of your memories. We don’t want it, we don’t collect it, and we’ve gone to deliberate length to keep query strings out of the recall event.
Auditing
The telemetry plumbing is in src/poppy/telemetry.py, and every event is emitted through its capture / capture_once helpers, so the call sites are greppable. If you spot anything beyond the list above, that’s a bug; please file it.