* feat(privacy): Settings → Usage — local-only insights, the answer to cloud analytics
A PostHog integration was proposed and rejected (PR #1110, closed): sending
usage events to a third-party endpoint would break the one promise this product
is built on — nothing leaves your machine — and local-first is the reason people
choose it over ElevenLabs. But the question analytics was meant to answer ("how
am I using this?") is a fair one, so answer it locally.
services/local_stats.py aggregates the history the app has ALREADY written to
the user's own SQLite DB: takes, audio produced, compute time, starred, active
days, voices/dubs/projects/exports, and distributions by mode and language.
GET /stats/usage serves it over loopback; Settings → Usage renders it.
The three properties that stop this becoming telemetry by accident:
- READ-ONLY. No new table, column, or event stream. Delete the feature and not
one byte of stored data changes.
- NO CONTENT. Counts and totals only — the `text` column of a take is never
read and never returned; no paths, no ids, no person. Pinned by a test that
asserts the payload contains no take text, no /Users/ path, no row id.
- NO NETWORK. There is no client, no endpoint, no token. It has no way to send
anything anywhere.
The panel states the guarantee in the UI, because a privacy promise the user
can't see isn't worth much.
Route added to the API-surface snapshot (the inventory guard caught it, as
designed — one line: GET /stats/usage).
4 backend tests (aggregation / never-leaks-content / empty install / missing
table degrades to 0) + 4 frontend tests. Backend suite 2924 passed; lint,
format, typecheck clean.
Closes the analytics question opened by #1110.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(settings): use the real --chrome-fg-dim token in UsageTab (css-token guard)
cssTokens.test.js is a frontend guard that every var(--…) a component
references actually exists — an undefined custom property with no fallback is an
invalid declaration, so the style silently does nothing. UsageTab referenced
--chrome-fg-subtle, which doesn't exist; the dim sub-label token is
--chrome-fg-dim (what the other settings panels use).
My miss: I ran the full BACKEND suite but only the two new frontend test files,
so this guard never ran locally. Full frontend suite now green (1211 passed).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: mergetest <nizam4103@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>