UNDA — Data Inventory
Status: Draft. Every personal-data field must appear here. Update in the same PR that adds/changes a field. Last reviewed: 2026-08-08 Owner: Product engineering (TBD) Legend for Classification: IDENTITY · SENSITIVE_HEALTH (Article 9) · FITNESS · SETTINGS · OPERATIONAL
Legal basis defaults, to be confirmed by counsel:
IDENTITYandSETTINGS→ GDPR Art. 6(1)(b) contractSENSITIVE_HEALTHandFITNESS(used for personalisation) → Art. 6(1)(b) contract plus Art. 9(2)(a) explicit consent
Fields
profiles.id
- Source: UNDA (UUID generated at onboarding finish)
- Classification: IDENTITY (pseudonymous)
- Purpose: Primary key for cross-table joins
- Database:
profiles.id; FK on every user-owned table - Retention: Life of account
- Processors: Local device only (Supabase when sync is enabled)
- Analytics: NEVER
- User editable: No
- User deletable: Yes (via delete-account flow — pending B1)
- Exported: Yes
- Legal basis: Art. 6(1)(b) contract
profiles.last_period_start
- Source: User-reported (onboarding + Settings)
- Classification: SENSITIVE_HEALTH
- Purpose: Anchor date for phase computation
- Retention: User-controlled; on account deletion, purged
- Analytics: NEVER
- User editable: Yes (Settings)
- User deletable: Yes
- Exported: Yes
- Legal basis: Art. 6(1)(b) + Art. 9(2)(a) explicit consent
profiles.avg_cycle_length, profiles.avg_period_length
- Source: User-reported (onboarding + Settings)
- Classification: SENSITIVE_HEALTH
- Purpose: Scale phase model to the user's cycle length
- Retention/analytics/edit/delete/export: as above
- Legal basis: as above
profiles.on_hormonal_contraception
- Status: Not currently collected (removed from onboarding for data minimisation).
- Note: The column exists in the model as a placeholder for the "flat programming" toggle. If we re-introduce collection, we must add a data-inventory entry with justification and a specific consent event.
profiles.fitness_level, profiles.goals, profiles.equipment, profiles.sports
- Source: User-reported (Personalize screen) or inferred by
ProfileInferenceService(marked asunda_inferredafter B2). - Classification: FITNESS
- Purpose: Individualize the workout recommender
- Retention: Life of account, user-editable
- Analytics: NEVER as raw values. Aggregate counts (once analytics is added) are on the allowlist.
- Legal basis: Art. 6(1)(b) contract
profiles.notifications_enabled
- Classification: SETTINGS
- Legal basis: Art. 6(1)(a) consent (notifications-specific)
profiles.sync_enabled
- Classification: SETTINGS
- Purpose: Whether Supabase sync is on
- Legal basis: Art. 6(1)(b) + Art. 9(2)(a) explicit consent for cross-device replication of health data (health-data sync is a separate consent event)
profiles.onboarding_complete, profiles.updated_at
- Classification: OPERATIONAL
- Purpose: Boot routing + sync bookkeeping
cycle_days (date, flow, symptoms, mood, energy, sleep_hours, notes)
- Source: User-reported (Calendar tab symptom log).
notescurrently unused. - Classification: SENSITIVE_HEALTH
- Purpose: Log per-day symptoms and per-day energy, feed Insights and the recommender's felt-score history
- Retention: User-controlled; retained while account exists
- Analytics: NEVER
- User editable: Currently only for today (planned: any date)
- User deletable: Yes (per-row toggle; full via delete-account)
- Exported: Yes
- Legal basis: Art. 6(1)(b) + Art. 9(2)(a) explicit consent
cycles (start_date, end_date, period_length_days, cycle_length_days, notes)
- Status: Table exists in schema, not yet written to.
- Classification: SENSITIVE_HEALTH
- Planned purpose: Historical cycle records for length distribution and prediction confidence
workout_sessions (workout_slug, date, phase_at_time, day_of_cycle_at_time, completed, felt_score, notes)
- Source: User action ("Mark completed")
- Classification: FITNESS + inferred SENSITIVE_HEALTH via
phase_at_timesnapshot - Purpose: History for Insights, felt-score training signal for the recommender
- Retention: Product-useful period (TBD in retention-policy.md); user-deletable
- Analytics: NEVER as raw rows
- Provenance:
workout_slugis UNDA-catalog;phase_at_timeisunda_inferredat time of completion;felt_scoreisuser_reported - Legal basis: Art. 6(1)(b) + Art. 9(2)(a)
imported_activity (from Strava/Intervals.icu — planned)
- Status: Interface exists (
ActivityHistoryImporter); no data ingested outsideMockImporter. - Classification: FITNESS + SENSITIVE_HEALTH (avg HR, HRV where present)
- Purpose: Profile inference (sport interests, level)
- Provenance:
provider_imported - When wired: re-enter here with vendor, scope, retention, disconnect behaviour.
consents
- Fields: id, profile_id, consent_type, policy_version, granted, granted_at, withdrawn_at, source, updated_at
- Classification: OPERATIONAL (evidence of consent)
- Source: UNDA-recorded from user's affirmative action (onboarding checkboxes, Settings toggles)
- Retention: Life of account; deleted with account
- Analytics: NEVER
- User editable: Users flip the underlying consent via Settings (planned Privacy Center will expose every type); UNDA never rewrites past rows
- Exported: Yes
- Legal basis: Art. 7 GDPR (records of consent)
profiles.age_confirmed_18_plus, profiles.age_confirmed_at
- Source: User attestation at onboarding
- Classification: OPERATIONAL (eligibility attestation)
- Purpose: §16 minors policy — UNDA v1 is 18+
- Retention: Life of account
- Analytics: NEVER
- User editable: No (attestation is immutable once made; delete-account and re-onboard to re-attest)
- Exported: Yes
- Legal basis: Art. 6(1)(b) contract (eligibility to enter into service)
Fields we deliberately do NOT collect
- Email / real name at account level — only when Supabase sync is enabled and the user signs in.
- Precise location — not required for any current feature.
- Contacts, messages, or unrelated device categories — never requested.
- Basal body temp, cervical mucus, LH tests — schema extension point noted in
docs/DATA_MODEL.md; not currently collected. If added, gets a separate explicit-consent event (§4.2). - Pregnancy status — out of scope; if added, requires legal review (§26).
- Sexual activity / partner data — out of scope.
Change control
Add or modify a row here in the same PR that adds/modifies the field. PR checklist (§22) enforces this.