Files
HealthLog/docs/security/cycle-data-threat-model.md
T
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

53 lines
2.6 KiB
Markdown

# 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`](../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.