Skip to content

Data Flow & RoPA

Derived from registers/ropa.md (UK GDPR Art 30), technical/data-architecture.md, and registers/retention-schedule.md. Lawful bases are provisional [counsel required]. There is no real user data yet; pre-launch seed data is fictional and tagged users.is_seed = true.

What personal data enters, and where it goes

flowchart LR
  %% source: registers/ropa.md, technical/data-architecture.md (2026-06-14)
  subgraph inputs["Personal data entering"]
    li["LinkedIn OIDC<br/>sub, email, name, picture"]
    bp["Boarding pass<br/>decoded ON DEVICE — raw payload,<br/>PNR, name, seat never leave transient memory"]
    authored["User-authored content<br/>headline, summary, intent tags,<br/>request notes, messages, reports"]
    grants["Consent choices<br/>analytics / ml_training / marketing"]
  end

  subgraph sor["System of record — Cloud SQL (private)"]
    ident["users · external_identities · traveler_profiles<br/>(PII — life of account)"]
    pres["flight_presences<br/>(derived personal: airport, window,<br/>verification level — 7d after expiry)"]
    matches["match_candidates · connection_requests<br/>· match_rationales"]
    msgs["conversations · conversation_messages<br/>(90d after last activity)"]
    safety["user_blocks · user_reports"]
    consents["user_consents (notice-version evidence)"]
    events["app_events — pseudonymized, consent-tiered,<br/>no content/secrets/identity (ML-04/05)"]
  end

  ml[("ml_training_snapshots<br/>anonymous k-anonymous aggregates —<br/>survive erasure (ML-07)")]
  claude["Anthropic Claude (key-gated)<br/>receives ONLY minimized fields:<br/>display name, headline, intent tags"]

  li --> ident
  bp -->|"derived fields only"| pres
  authored --> matches
  authored --> msgs
  authored --> safety
  grants --> consents
  pres --> matches
  ident -->|"minimized prompt"| claude
  claude -->|"cached rationale sentence"| matches
  consents -.->|"gates T1/T2 emission"| events
  events -->|"whitelisted T2 only,<br/>low-k suppressed"| ml

Re-identification rule (load-bearing): airport + gate + departure time + time-of-day is a small anonymity set, so presence and match data are treated as personal data even with no name attached. Redis holds only self-expiring security state (OAuth state ~10 min, handoff codes 120 s, hashed rate-limit counters) — no durable personal data.

Processing activities (RoPA summary)

The authoritative record is registers/ropa.md; this table summarizes it. Bases marked † are [counsel required].

# Activity Key data Lawful basis (provisional) Retention target
1 Account & authentication LinkedIn sub, email, name, picture Contract Life of account + grace
2 Session management Hashed token, timestamps Contract 30d after expiry/revoke
3 Flight presence intake Derived airport/window/verification only Consent (proximity) 7d after expiry
4 Matching & connect requests Pair, place/time context, note, state Legitimate interest / contract 30d after terminal status
5 Messaging Body, sender, timestamps Contract 90d after last activity (TBD)
6 Trust & safety Block pairs; report reason/details Legitimate interest (safety) Block: life of account; report: 180d after resolve (TBD)
7 Analytics / ML (planned) Tiered events Consent (T1/T2) TBD
8 Persistent connections Accepted pair, minimized profile fields Contract / LI † Beyond trip window; erased with account
9 Profile enrichment (deferred) PDL/ZoomInfo attributes — none today Legitimate interest (provisional) Deferred
10 Profile management LinkedIn display + self-authored fields Contract / LI † Life of account
11 AI rationale & interview (key-gated) Minimized fields only; interview stateless LI / contract † Cache expires with input TTL
12 Pre-launch seed testing Fictional mock data only Not personal data if fictional † Purge before GA
13 Trip-history persistence (STUB — not built) Movement profile: past flights beyond the trip window OPEN † (C29 — required before user-facing) OPEN — no retention class yet

Erasure path

Account deletion (DELETE /account) soft-deletes, revokes all sessions, then a worker hard-purges after the grace window; FK cascades remove the personal graph. Erasure propagates to the pseudonymized event store but not to anonymous ML snapshots — the anonymization boundary is the gating control (ML-07). Full lifecycle: Retention Lifecycle.