Analytics without surveillance: explicit consent, layered gates, and never sending Class A data

Published: (February 19, 2026 at 12:02 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Overview

Analytics in health‑adjacent apps has a special failure mode: you add “just one metric”, then another, then a refactor starts sending richer context, and suddenly you’re collecting things you never intended to collect.

If you want privacy‑first, offline health tech to exist without surveillance, fund it by sponsoring the build: .

Pain Tracker avoids that drift with a few simple rules:

  1. Analytics is off by default (explicit consent).
  2. Defense‑in‑depth gating (multiple checks).
  3. Never include Class A health content (no notes, no symptoms, no meds).

The post is grounded in the repository’s implementation:

  • src/analytics/analytics-gate.ts
  • src/analytics/analytics-loader.ts
  • src/analytics/ga4-events.ts
  • src/components/AnalyticsConsentPrompt.tsx
  • src/components/settings/PrivacySettings.tsx
  • src/utils/usage-tracking.ts

Deployment‑Level Gate

Analytics is controlled with a Vite environment variable (e.g. VITE_ENABLE_ANALYTICS). When disabled, the app behaves as if analytics does not exist.

Why it matters

  • Privacy‑friendly deployments can hard‑disable analytics.
  • Prevents “whoops, we accidentally shipped tracking” moments.

Even if analytics is enabled for a deployment, it does not run without user opt‑in. Pain Tracker includes a dedicated consent surface (prompt + settings) designed for trauma‑informed UX:

  • Control is part of safety.
  • “No” must be respected without nagging.
  • “Change my mind later” must be possible.

Layered Checks

Analytics can accidentally turn on in several ways:

  • A component calls a tracking helper on mount.
  • A shared hook starts emitting events.
  • A remote analytics script partially loads.

The repository mitigates this with layered checks:

  1. Env‑gate and consent‑gate decide if analytics should be enabled.
  2. The loader injects remote analytics only when allowed.
  3. Event senders verify the runtime is actually present before sending (no gtag, no event).

This ensures that “disabled means no‑op” remains true.


Class A Data Protection

If your app stores Class A data (pain entries, symptoms, meds, free‑text notes), assume it will leak unless you deliberately design against it.

Pain Tracker’s event helpers aim to send only coarse information:

  • Counts
  • Booleans
  • Category/bucket values

They do not send:

  • Free text
  • Per‑entry timestamps
  • Anything that can reconstruct a user’s log

This is a pragmatic engineering rule, not a compliance claim: analytics should be useful without being invasive.


Local Usage Tracking

The repo also includes local usage‑tracking utilities (stored on‑device) intended for lightweight UX behavior and product insight without shipping data off‑device.

Guidelines

  • Sanitize metadata.
  • Do not store raw notes.
  • Do not store enough detail to recreate someone’s history.

Local‑only tracking can still cover useful scenarios:

  • “Has the user seen this onboarding tip?”
  • “How often is export used?” (as a count, not content)

Quality Gates

Part 9 of the series introduces tests and quality gates that keep privacy promises from drifting over time. These gates enforce the rules described above throughout the codebase.


Sponsorship & Community

  • Primary sponsor:
  • Star the repository:

0 views
Back to Blog

Related posts

Read more »

Apex B. OpenClaw, Local Embeddings.

Local Embeddings para Private Memory Search Por default, el memory search de OpenClaw envía texto a un embedding API externo típicamente Anthropic u OpenAI par...