Originally created by: fu351
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.
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).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).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.
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)
Ticket changed by: fu351