2 Commits
Author SHA1 Message Date
debpalashandClaude Fable 5 23f1767e3c feat(analytics): in-repo publishable token — source builds get the same consent-gated analytics (#1193)
Owner-sanctioned reversal: the publishable write-only PostHog client key is
committed as the in-repo default in backend/core/analytics.py and
frontend/src/utils/analytics.ts (env / baked release token still wins), so
source builds show the same first-run consent ask as installers — skip = off,
nothing is ever sent without an explicit yes. Adds an install_channel property
(installer / docker / source) to lifecycle events, stamped by the desktop shell
via OMNIVOICE_INSTALL_CHANNEL and by the Docker image's existing
OMNIVOICE_SERVER_MODE marker. Guard tests now pin the two-canonical-files
allowlist + same-token invariant, and the uninstall-ping info file works on the
default token.

Fixes #1193

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 11:51:40 +05:30
debpalash 09f7c92045 feat(analytics): consent-gated lifecycle events — installs, updates, crashes, errors
New content-free events on the same rails as everything else (dual gate:
build token AND explicit consent; allowlisted metadata only; never raises):

- app_installed — once per install, fired the first time the install is both
  consented and configured (consent lands mid-first-run, so the marker is
  only burned when the event was actually sent).
- app_updated — persisted last-version marker vs current; from_version /
  to_version. The marker advances on every startup, consented or not, so a
  later opt-in never replays pre-consent history.
- app_crashed — ONE authoritative source: the backend run sentinel (#1164).
  The desktop shell's markers describe the same deaths (its watcher restarts
  the backend, whose next startup finds the sentinel), so the frontend never
  emits a crash event — no double-count, pinned by test. Props: exit_kind,
  coarse stage, BUCKETED uptime (never raw seconds), version, os.
- error_occurred — hooked into core.error_journal.record: error_class +
  route-head stage only, deduped by journal fingerprint, hard cap 10/session.

Allowlist extended (from_version/to_version/exit_kind/uptime_bucket/
error_class/stage) in backend and frontend alike, now pinned equal by a
locked mirror test. Also writes DATA_DIR/analytics_info.json (present iff
analytics is enabled; removed on opt-out) so the uninstall scripts can send
a consent-gated app_uninstalled ping without any baked token.
2026-07-16 19:56:42 +05:30