Files
HealthLog/docs/security/cycle-data-threat-model.md
Marc-André BombeckandClaude Opus 4.8 6fdbb8d889 feat(cycle): predicted-period notifications + reproductive-data privacy docs
Add two cycle reminders through the existing notification cascade and the
honest self-hosting privacy framing for reproductive-health data.

Notifications:
- New event types CYCLE_PERIOD_SOON (fires ~2 days before the predicted
  next-period start, from the cached prediction) and CYCLE_PERIOD_CONFIRM
  (a gentle "did your period start?" on/after the predicted start while no
  period is logged, within a short grace window). Both default OFF and are
  gated again on cycleTrackingEnabled — opt in twice for a sensitive surface.
- A 15-minute pg-boss cron mirrors the mood-reminder worker: it scans users
  with cycle tracking enabled + predictionEnabled, computes who is in-window
  from the cached forecast, and dispatches one push per event per local day.
  Idempotency anchors on the push_attempts ledger (no new table). The queue
  is declared, registered in allQueues, scheduled, and wired to a boss.work
  handler, with a source-grep guard so it cannot ship unregistered.
- Honours notificationPrefs.cycle.clientManaged (skip the server push when
  the device owns local reminders, with a suppressed annotation, mirroring
  the medication path) and CycleProfile.discreetNotifications (generic
  "HealthLog reminder" body so no cycle event is named on the lock screen).
- Every attempt is recorded in push_attempts via the unchanged dispatcher.

Privacy / disclaimer:
- docs/self-hosting/reproductive-health-data.md — the "no cloud vendor to
  subpoena" framing with the honest residual-threat caveat (your own host,
  provider, backups, and devices remain part of the surface), citing the
  2021 FTC settlement and 2025 verdict as public record only.
- docs/security/cycle-data-threat-model.md — a compact "protects against /
  does not protect against" note, plus README pointers.
- CyclePredictionDisclaimer component (reusing the GLP-1 estimate-disclaimer
  pattern) for every fertility/prediction surface: descriptive, not a
  contraceptive method, not medical advice, never a safe-to-have-unprotected
  -sex claim. 6-locale copy under cycle.disclaimer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 10:00:42 +02:00

2.6 KiB

Cycle-data threat model

A compact statement of what self-hosting HealthLog's cycle tracking protects against and what it does not. Pair this with the operator guidance in self-hosting/reproductive-health-data.md.

The scope here is reproductive-health data specifically (menstrual, fertility, pregnancy, contraception, sexual-activity fields), because that is the category where the "who can reach my data" question carries the highest stakes.

What self-hosting + encryption-at-rest protects against

  • A cloud vendor holding your data. There is no SaaS account and no vendor key escrow. No third party can be served process for data it does not have.
  • A stolen disk or a leaked backup, while the key is held separately. The intent-revealing fields are AES-256-GCM encrypted at rest; a disk image without the ENCRYPTION_KEYS value does not yield them.
  • Casual lock-screen disclosure. Discreet notifications collapse every cycle reminder to a generic "HealthLog reminder" so the event is never named on a lock screen.
  • Third-party analytics / ad-tech leakage. HealthLog ships no telemetry and no third-party trackers; cycle data is never sent to an analytics or advertising endpoint.

What it does NOT protect against

  • Seizure or compulsion of your own host. If the running machine is seized, imaged, or compelled, the data is on it. Encryption at rest does not help against a live host or a key stored beside the data.
  • Your hosting provider. A VPS provider can snapshot the disk and can be served process. Self-hosting on rented infrastructure narrows the set of parties that hold the data; it does not eliminate them.
  • Your own backups. Any pg_dump or off-host export carries the same data and must be protected as part of the same surface.
  • A compromised or unlocked client device. A phone or laptop with a live session or cached client holds data locally. Server-side protection does nothing for an unlocked device.
  • Re-identification from "plaintext" fields. Flow, basal body temperature, ovulation tests, cervical mucus, and cycle dates stay in plaintext so the engines can read them. They are health observations, not the disclosure-sensitive intent fields — but they are still health data on your host.

The one-line version

Self-hosting means no cloud vendor to subpoena. It does not mean the data is beyond reach: your own infrastructure, your provider, your backups, and your devices are all still part of the surface. Size your operator-side hardening to your own threat model.