feat(telemetry): foundation CLI + schema-guard + off-by-default proof (#1736)
* feat(telemetry): add consent CLI surface for maintainer-share opt-in Adds the foundation CLI commands described in RFC #1719: - bernstein telemetry enable --share-with-maintainer: prints the event schema and redaction list, requires confirmation, and persists share_with_maintainer = true to $XDG_CONFIG_HOME/bernstein/telemetry.toml. - bernstein telemetry disable: reverts the flag. - bernstein telemetry tail [-n N]: prints the next N events that would be sent, offline, from a new in-process side-channel preview ring buffer. The existing telemetry on/off/status/export/probe surface is unchanged. telemetry status grows three new lines covering the share flag value, its source provenance, and the resolved DSN. The flag lives in a separate TOML file and gates an additive opt-in path. The shipped package still hardcodes no maintainer endpoint URL. * docs(telemetry): document maintainer-share consent flow Adds docs/observability/telemetry-share.md covering: - The consent flow for the share_with_maintainer flag. - Env-var and TOML precedence (DO_NOT_TRACK, BERNSTEIN_TELEMETRY_SHARE). - The exact event schema and redaction list that drive the disclosure. - How to audit offline via bernstein telemetry tail. - Three equivalent ways to revoke consent. Links the new page from docs/observability/side-channel.md so operators reviewing the operator-controlled side channel see the additive consent surface in context. * test(telemetry): schema-guard requires redaction decision per event field Introspects every Payload dataclass in core/telemetry/events.py and asserts each field is either listed in SAFE_PRIMITIVE_FIELDS (the explicit allowlist of safe primitives shown in the consent disclosure) or has an entry in REDACTION_RULES describing how it is sanitised. Adding a new payload class without registering it in PAYLOAD_CLASSES also fails, so the schema cannot silently grow. * test(telemetry): presence-or-absence proof for share-flag off-by-default Asserts the RFC #1719 invariant: with share_with_maintainer unset, the package sends nothing on the maintainer-share path, under three DSN configurations (unset, syntactically invalid, and broken-shape). respx intercepts every outbound httpx call; the catch-all route is asserted to remain uncalled. An independent canary inside the test confirms respx is the active transport so the assertion cannot pass trivially. Complementary checks cover: - the env-var precedence (BERNSTEIN_TELEMETRY_SHARE=0 overrides file=true) - the universal DO_NOT_TRACK=1 override - the package not baking in a default maintainer URL - resolving consent never creates the TOML file as a side effect Also adds CLI snapshot coverage for the new enable / disable / tail subcommands.
C
chernistry committed
759daa9035803cb29ffa3fedf02c9117feae6a4b
Parent: 7249540
Committed by GitHub <noreply@github.com>
on 5/20/2026, 1:44:25 PM