¶ 2026-07-24 Bodylog privacy-safe product analytics
Bodylog now supports optional, off-by-default product analytics across the web and Android clients. Analytics record only fixed, typed product-interaction metadata. Health data, chat text, images, audio, files, and free-text feedback are excluded.
Passive product feedback is classified into fixed categories and sentiment values by the profile-chat model only when a user explicitly comments on the Bodylog product. The classification is stored only when analytics consent is enabled; no source text or summary is retained.
- Explicit opt-in with consent version and timestamps.
- Server independently enforces current consent for every event batch.
- Pseudonymous install and session identifiers are hashed server-side.
- Event and feedback retention is 90 days, with a daily cleanup worker.
- Users can export or delete their analytics data and can disable analytics at any time.
- Android clears its local analytics queue and identifiers when disabled or signed out.
- Admin reporting contains aggregates only. Cohorts with fewer than five distinct users are suppressed.
- Source implementation:
874b930
- Android CI test correction:
44bbcad
- Deployed
main: c72dfd9bdaedcb40a4c6f21cbba4cf54db9bafb2
- Kubernetes image:
gitea.app.lan/bodylog/bodylog.dev:sha-c72dfd9bdaed
- Database migration:
202607240005 privacy product analytics
- Android download:
0.1.2026060767-c72dfd9bdaed, version code 2026060767
- Backend unit suite: 164 passed, 0 failed, 7 ignored.
- Disposable PostgreSQL integration tests passed for consent gating, ownership, deletion, passive-feedback no-op without consent, and five-user admin cohort suppression.
- Frontend: 61 tests passed and production build completed.
- Android unit tests and release builds passed with Garmin Connect IQ enabled, matching CI configuration.
- Gitea internal-testing workflow 766 succeeded and published to Play internal testing.
- Gitea autodeploy workflow 767 succeeded.
- Kubernetes deployment rolled out successfully with one ready pod.
https://api.bodylog.dev/healthz returned ok; the web application returned HTTP 200.
- Migration and all three analytics tables were confirmed in production. They contained zero records immediately after rollout, confirming the default-off state.
- Unauthenticated analytics preference access returned HTTP 401.
The first deployment attempt failed only in the Android analytics test because the Garmin-enabled CI environment caused Robolectric to start BodylogApplication, which initialized WorkManager. The test was corrected to use a plain Android Application, reproduced locally under the exact CI environment, and the complete retry succeeded.