UNDA — Data Retention Policy
Status: Draft baseline. Final values must be reviewed by counsel before production (§13).
| Data category | Retention baseline | Trigger for deletion | Notes |
|---|---|---|---|
Account identity (profiles.id, email if signed in) | While account exists | Account deletion via Privacy Center | Some residual security logs may retain hashed identifier per §15. |
Cycle history (profiles.last_period_start, avg_cycle_length, avg_period_length, cycles) | User-controlled; retained while account exists | Full delete via Privacy Center; per-cycle edit in Settings | Never auto-expired. |
Per-day logs (cycle_days) | User-controlled | Per-day clear + full delete via Privacy Center | User can also individually untoggle symptoms today. Historical edit is B (pending). |
Workout sessions (workout_sessions) | User-controlled; suggested cap: 3 years for recommendation training data | Account deletion, or "Clear workout history" toggle in Privacy Center (planned) | felt_score older than N is de-weighted by the recommender rather than deleted. |
| Imported activities (Strava/Intervals/Garmin) | Until disconnect; disconnect deletes previously imported rows by default (§8.4) | Disconnect the integration | The UI must state this behaviour. |
| OAuth tokens (per integration) | Until disconnect or provider revocation | Disconnect the integration or provider-side revoke | Stored via flutter_secure_storage — never in the sqflite DB or logs. |
| Recommendation history | Product-useful window (baseline: 12 months) | Rolling deletion job (planned once cloud sync exists) | Persist enough to explain "why this workout" for the past N sessions. |
| Debug logs (crash SDK if added) | 30 days baseline | Automatic vendor-side | Sensitive-value scrubbers configured per §6.3. |
| Security logs (auth events, admin access) | 12 months | Automatic | Redacted per §6.2. |
| Backups (Supabase, when enabled) | 30-day rolling | Automatic | Deleted rows may persist in backup until rotation; document to user in Privacy Notice. |
Enforcement
Currently the app has no automated retention jobs — every retention window above is either user-controlled or bounded by "life of account" until we ship cloud sync + the associated jobs. That is acceptable pre-beta because everything lives on-device, but must be delivered before the Pre-Public-Launch Gate (§25).
Change control
Any new datastore requires a row in this table in the same PR (RULE 8: "Do not introduce a datastore without defining deletion behaviour").