Retention Schedule¶
UK GDPR Art 5(1)(e) (storage limitation). Controls PRIV-03,
PRIV-04, SEC-12. Status: implemented for the backend hard-delete spine
delivered in infra PR-12 plus the account-erasure purge delivered in infra
PR-14, plus the consent/event foundation delivered in infra PR-17, first
product event hookpoints delivered in infra PR-18, worker lifecycle event
hookpoints delivered in infra PR-19, the anonymized ML snapshot boundary
delivered in infra PR-20, the backend boarding-pass-gated presence contract
delivered in infra PR-21, message send delivered in infra PR-22, directory
delivered in infra PR-23, connection requests delivered in infra PR-24, and
professional profile fields delivered in infra PR-25, relevance ranking delivered
in infra PR-26, cached AI rationale delivered in infra PR-27, stateless AI
profile interview delivered in infra PR-28, the persistent connections list
delivered in infra PR-29, and tagged pre-launch seed/purge tooling delivered in
infra PR-30; durations remain
placeholders [counsel required] until counsel
ratifies the policy numbers.
Policy: policies/privacy-and-retention-policy.md,
policies/backup-and-recovery-policy.md.
Evidence:
- infra PR-12 (
retention_jobs.py,worker.run_retention) implements worker sweeps for sessions, presences, non-matched candidates, and stale matched conversations/messages. - infra PR-14 (18047a3) implements authenticated
DELETE /account, soft-delete withusers.deleted_at, all-session revocation, active-only auth, LinkedIn no-revival guard, andretention_jobs.sweep_deleted_users. - infra PR-17 (7562d25) adds
user_consentsandapp_events; both are tied tousersby cascade so user hard delete removes consent rows and actor-linked upstream event rows. - infra PR-18 (b0590be) emits auth/presence/match/safety product events through
try_emit_event. - infra PR-19 (94d4018) emits worker
match.candidate_proposed/match.expiredevents as actor-scoped T1 rows through the same consent gate. - infra PR-20 (9e828db) adds
ml_training_snapshots/ml_training_snapshot_rows: anonymous aggregate snapshot rows with no user FK, built from whitelisted T2 events only; sourceapp_eventsstill cascade on user erasure while snapshot rows survive as a distinct anonymous class. - infra PR-21 (52cd2c8) tightens product
POST /presenceto requireboarding_pass_barcode+document_verified, reject manual/self-reported/ unverified product writes, and preserve the no-raw-pass-artifact boundary. - infra PR-22 (2680a01) implements backend message send with metadata-only
message.sentevents; message bodies remain inconversation_messages. - infra PR-23 (75d8337) implements the in-window directory without adding new retention classes.
- infra PR-24 (a6cf2af) adds
connection_requestswith user-authored request notes, request status, presence references, timestamps, and optional conversation bridge. - infra PR-25 (0934291) adds self-authored profile
summaryandintent_tagstotraveler_profiles;headlinealready existed on that table. - infra PR-26 (e40f0fd) adds internal-signal relevance ranking without adding a retained data class.
- infra PR-27 (e43b60c) adds
match_rationales, a cached rationale sentence per canonical pair plus minimized-input hash and expiry; prompts, provider metadata, and raw model responses are deliberately not stored. - infra PR-28 (d2db03f) adds the advisory
/profile/interviewroute; the endpoint is stateless, returns 503 without an Anthropic key, and smoke tests verify interview content is not saved to profile summaries, events, or logs. - infra PR-29 (1fbcf6e) adds
GET /connectionsover acceptedconnection_requestswithout creating a new table. - infra PR-30 adds
users.is_seed,app.seed,app.purge_seed, andapp.seed_smokeso pre-launch mock traveler graphs can be created, purged, and verified without targeting realis_seed=falseaccounts. app.safety_smoke.pyverifies each retention class, including that recent matched conversations are not prematurely deleted andRETENTION_ENABLED=falseis a no-op.app.account_erasure_smoke.pyverifies unauthenticated rejection, generic response/no PII, multi-session revocation, no session revival, purge cascade, recent-deletion grace behavior, and disabled-retention no-op behavior.- Runtime knobs (labels only, values not stored here):
RETENTION_ENABLED,RETENTION_SESSIONS_DAYS,RETENTION_PRESENCES_DAYS,RETENTION_CANDIDATES_DAYS,RETENTION_CONVERSATIONS_DAYS,RETENTION_ACCOUNT_ERASURE_DAYS.
| Data class | Table(s) | Live retention | Hard-delete target | Mechanism today | Remaining gap |
|---|---|---|---|---|---|
| Session rows | app_sessions |
Until expiry + grace | 30d after expiry/revoke | PR-12 worker sweep deletes expired/revoked rows after RETENTION_SESSIONS_DAYS |
Idle timeout/rotation policy remains separate (SEC-12) |
| Presence rows | flight_presences |
TTL (default 4h, max 48h) | 7d after expires_at |
PR-12 worker sweep deletes expired rows after RETENTION_PRESENCES_DAYS; PR-21 ensures product-created rows are boarding-pass-derived document_verified rows with no raw pass material retained |
Counsel ratifies duration and C25 boarding-pass processing posture |
| Non-matched candidates | match_candidates |
Until terminal dismissal/expiry | 30d after updated_at for dismissed/expired candidates |
PR-12 worker sweep deletes non-matched terminal rows after RETENTION_CANDIDATES_DAYS |
Counsel ratifies duration |
| Matched conversations + messages | match_candidates, conversations, conversation_messages |
Life of match | 90d after last conversation activity | PR-12 worker sweep deletes the owning matched candidate after RETENTION_CONVERSATIONS_DAYS; FK cascade removes conversation and messages; PR-22 adds backend message send into the existing table |
Counsel ratifies duration |
| Connection requests | connection_requests |
Pending until accepted/declined/expired; accepted rows may act as durable connection evidence | On account erasure; independent duration TBD | PR-24 creates directional request rows with note, status, presence references, timestamps, and optional conversation bridge; user hard delete cascades requester/target rows | Request-note retention/minimization pending counsel C28/C4; no independent sweep exists yet |
| Persistent connection records | connection_requests (accepted rows) plus conversation bridge |
Persists beyond trip window as a durable professional contact | On account erasure; independent duration TBD | PR-24 accepted requests persist as accepted connection rows and bridge to the conversation substrate; PR-29 exposes them through self-scoped, block-aware, minimized GET /connections |
Independent duration pending counsel C28; dedicated contacts model/job still future |
| Profile fields | traveler_profiles |
Life of account | On account deletion after erasure grace | PR-25 stores self-authored headline, summary, and intent tags on traveler_profiles; user hard delete cascades the row |
Counsel confirms profile-field retention and correction/export process |
| AI rationale cache | match_rationales |
Until expires_at for the pair/input hash |
On account erasure or cache expiry; independent maximum duration TBD | PR-27 stores only a generated sentence, canonical pair user IDs, minimized-input hash, created_at, and expires_at; user hard delete cascades either side | Counsel confirms cache duration and transparency language under C27; no independent sweep exists yet beyond expiry checks on read |
| AI profile interview turns | None in Partile DB (transient route/provider processing) | Not retained by the Partile endpoint | Not applicable unless the user saves a suggested summary into traveler_profiles later |
PR-28 interview_smoke.py verifies prompt content is not saved to profile summaries, events, or logs; suggested output is advisory |
Mobile interview UI remains upcoming; provider/DPA/caching terms pending C27 |
| Blocks | user_blocks |
While account active | On account deletion | PR-14 account purge cascades rows on DELETE FROM users |
Counsel confirms safety-retention stance |
| Reports | user_reports |
While open; archive on resolve | 180d after resolution; currently cascades on account purge | Durable report capture implemented in PR-12; PR-14 account purge cascades current rows | Moderation workflow and counsel C7 report-retention policy |
| LinkedIn-derived PII | external_identities |
Life of account | On account deletion after erasure grace | PR-14 account purge cascades rows after RETENTION_ACCOUNT_ERASURE_DAYS |
Counsel confirms grace duration and notice wording |
| Consent grants | user_consents |
Life of account | On account deletion after erasure grace | PR-17 table cascades on user hard delete; event_consent_smoke.py verifies cascade |
Counsel confirms notice-version evidence duration |
| Upstream app events | app_events |
While account active | On account deletion for actor-linked rows | PR-17 actor_user_id cascades on user hard delete; event_consent_smoke.py verifies cascade; PR-18 event_hookpoint_smoke.py verifies auth/presence/match/safety emission through the consent gate; PR-19 worker_event_hookpoint_smoke.py verifies actor-scoped worker match lifecycle emission through the consent gate; PR-22 adds message.sent T1 metadata-only events; PR-24 adds connection.requested T1 and connection.accepted/connection.declined T2 events; PR-27/PR-28 smokes verify rationale/interview content is not emitted to events |
Counsel C8/C9 ratification still future; ranking/AI event hookpoints are not currently built |
| Anonymous ML training snapshots | ml_training_snapshots, ml_training_snapshot_rows |
Independent anonymous-aggregate class | Not tied to account erasure; independent retention TBD | PR-20 ml_snapshot_smoke.py verifies T2-only k-anonymous rows, low-k suppression, stripping non-whitelisted fields, source app_events cascade on user delete, and snapshot rows survive erasure |
Counsel C8/C9 final anonymization standard; decide independent snapshot retention duration before real model training |
| Tagged pre-launch seed accounts | users.is_seed = true plus related rows |
Pre-launch testing only | Purge before GA and before accepting real users | PR-30 app.seed creates only tagged mock accounts; app.purge_seed deletes seed-related event object refs and seed users, then relies on FK cascades; app.seed_smoke verifies seed, idempotency, purge, and a real marker row survives |
ACT-004 purge-before-GA gate remains open; no real PII may be seeded |
| Soft-deleted accounts | users.status, users.deleted_at |
Grace window after user requests deletion | Default 7d after deleted_at |
PR-14 worker sweep hard-deletes status='deleted' users after RETENTION_ACCOUNT_ERASURE_DAYS |
Counsel confirms grace duration; DSAR process still separate |
| OAuth state / handoff codes | Redis | 10 min / 120 s | self-expiring | Redis TTL and single-use consume | OK |
Erasure design → readiness/data-inventory-and-retention.md.
Counsel duration review → counsel-queue.md C4.