Skip to content

feat(telemetry): opt-in, anonymous CLI usage telemetry to PostHog - #460

Merged
fu351 merged 2 commits into
mainfrom
feat/telemetry/posthog-opt-in
Aug 25, 2026
Merged

feat(telemetry): opt-in, anonymous CLI usage telemetry to PostHog#460
fu351 merged 2 commits into
mainfrom
feat/telemetry/posthog-opt-in

Conversation

@fu351

@fu351 fu351 commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

What

Opt-in, anonymous CLI usage telemetry to PostHog, so we can see which commands and modes people use and whether they keep using Doberman. Off by default; nothing leaves the machine unless the user says yes.

  • Consent: doberman setup (interactive path only) asks once, default No; doberman setup --yes never asks and stays off. doberman telemetry on|off|status. Kill switches that override the local file: DO_NOT_TRACK, DOBERMAN_TELEMETRY=0, CI. State lives in <DOBERMAN_HOME or ~>/.doberman/telemetry.json (0600) with a random uuid4 distinct id (not derived from hardware, user, hostname, or repo).
  • Events (5, all CLI-side; the hook and proxy hot paths never import the module, enforced by a new import-linter contract): telemetry_enabled, telemetry_disabled, setup_completed (mode, host, hooks_installed, global_install, source), cli_command (command name only, e.g. taint.clear; never args), usage_summary (lifetime PASS/AUTH/BLOCK counts from the existing device rollup, at most once per 24 h).
  • Structural redaction: per-event property allowlist plus a value guard (scalars only; strings up to 64 chars of letters, digits, underscore, dot, plus, minus). A path, prompt, or secret cannot pass the pattern; the tests prove a synthetic path and token never reach the request body. Person profiles and GeoIP are disabled on every event.
  • Transport: stdlib urllib to the PostHog Cloud US capture endpoint, 3 s timeout, daemon thread, atexit join capped at 1 s total; every exception swallowed. The PostHog project key is a public ingestion key; in this PR it is still the placeholder constant, which makes the whole module a no-op until the real key is committed in a follow-up.
  • Docs: docs/TELEMETRY.md (exact event table, what is never sent, opt-out), README one-liner, docs/CLI.md rows, CHANGELOG bullet.

main.py hunk is 9 lines (import, register_cli_telemetry, callback context, two calls in setup); the rest lives in src/doberman/telemetry.py and src/doberman/cli/telemetry_cmd.py. #447 and #414 also touch main.py; same landing order as #458 (fu351, 2026-08-25): merge this first, re-apply their deltas when they land.

Test plan

  • pytest tests/unit/test_telemetry.py tests/unit/test_setup_wizard.py tests/unit/test_device_metrics.py (default off means no HTTP; enabled means one POST with the exact key set; disallowed keys dropped and a synthetic path/token absent from the body; kill switches; placeholder key; transport failure; 24 h summary gate; wizard y/n/--yes; hook pre emits nothing; atexit join stays under ~1.5 s)
  • ruff check, ruff format --check, lint-imports (5 contracts kept, including the new telemetry vs proxy/hosthooks one)
  • full unit suite locally
  • CI green

fu351 added 2 commits August 25, 2026 11:13
Off by default. The setup wizard asks once on its interactive path
(default No); --yes never asks. A telemetry on/off/status command group,
plus DO_NOT_TRACK, DOBERMAN_TELEMETRY=0 and CI as kill switches. Five
allowlisted events (enable, disable, setup_completed, cli_command by name,
a once-a-day usage_summary from the device rollup); every property value
is a bounded scalar so a path, prompt, or secret cannot travel. No person
profile, no GeoIP, random uuid4 id in the user-level state file. Stdlib
transport in a daemon thread with a 1 s exit budget; never raises. The
hook and proxy hot paths never import the module (new import-linter
contract). The project key is a placeholder, which keeps the module a
no-op until the real public key is committed. Documented in
docs/TELEMETRY.md.
@fu351
fu351 merged commit 69aed82 into main Aug 25, 2026
6 checks passed
@fu351
fu351 deleted the feat/telemetry/posthog-opt-in branch August 25, 2026 20:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant