Skip to content

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:

  • IDENTITY and SETTINGS → GDPR Art. 6(1)(b) contract
  • SENSITIVE_HEALTH and FITNESS (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 as unda_inferred after 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). notes currently 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_time snapshot
  • 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_slug is UNDA-catalog; phase_at_time is unda_inferred at time of completion; felt_score is user_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 outside MockImporter.
  • 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)

readiness signals (transient, never persisted) ​

  • Source: Apple Health / Health Connect (device sensors → HKQuantityType records)
  • Categories read: sleep asleep + in-bed, steps today, HRV SDNN (last 30 days for baseline), resting HR (last 30 days for baseline)
  • Classification: SENSITIVE_HEALTH
  • Purpose: Feed the ReadinessEngine to decide whether today's planned workout should be swapped for an easier alternative. Not persisted — computed at render time on Today and discarded.
  • Retention: Zero — no storage layer touches these values. They live only in memory during the FutureProvider evaluation.
  • Processors: None — computation is on-device.
  • Analytics: NEVER
  • User editable: Values themselves are edited in Apple Health, not UNDA. UNDA only reads.
  • User deletable: Withdraw the readiness scope in iOS Settings → Health → UNDA, or toggle "Use readiness signals" off in Personalize.
  • Exported: No (nothing to export — not persisted).
  • Legal basis: Art. 6(1)(b) + Art. 9(2)(a) explicit consent — captured as a distinct feature toggle so it's separate from the base HealthKit connect grant.

pushed_intervals_events ​

  • Fields: id, profile_id, workout_slug, event_id, target_date, pushed_at
  • Classification: OPERATIONAL (bookkeeping — the UNDA slug we pushed and the Intervals event_id we got back)
  • Source: UNDA action (user tapped "Add to Intervals plan")
  • Purpose: Show "already added ✓" state on future visits; support Remove; prevent duplicate pushes for the same workout on the same date (UNIQUE constraint on profile_id × workout_slug × target_date)
  • Retention: Life of account; deleted with account
  • Analytics: NEVER
  • User editable: No (managed via Add/Remove on the WorkoutDetail screen)
  • User deletable: Yes (via Remove button on the workout, and via Delete-all-my-data)
  • Exported: Currently no; add if requested
  • Legal basis: Art. 6(1)(b) contract (necessary for the "push to plan" feature the user asked for)

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.

UNDA is a fitness and training support product. It is not a medical device.