Privacy is architecture, not a policy document.
One first-party event stream, three layers (state / stream / features), analytics and recommendation as sibling consumers — and India's DPDP baked into the shapes: itemised consent, pseudonymous events, no personalisation for minors, erasure that actually erases.
Three layers, one direction.
likes · saves · profile
Set-state, not toggle (PUT/DELETE); idempotent via natural PK + client event_id. Counts you see derive from here.
FeedEvent v2
Envelope: event_id · request_id (minted per feed response — the join key) · user_pseudo_id · purpose. → Pipelines → Iceberg on R2 Data Catalog; Workers Analytics Engine as an ops sidecar.
profiles · counters
Batched aggregation; online features mirrored to KV; impressions/dwell offline-only. Analytics and recsys read the stream as siblings — never one through the other.
DPDP, as shapes.
The Rules were notified Nov 2025; substantive obligations land ~May 2027. The architecture already has them.
Itemised consent
auth / analytics / personalisation — multilingual, one-tap withdrawable with real effect; receipts stored.
Minors
18+ terms + age declaration + risk-based escalation; a known-child account never gets personalisation — the contextual feed is the automatic fallback (one flag in rank()).
Pseudonymous by construction
user_pseudo_id on every event; identity mapping in one guarded table; phone numbers never leave the PII plane.
Retention + erasure
raw 90–180d → pseudonymous aggregates 1–2y → anonymised; erasure = instant mapping/OLTP delete + periodic Iceberg rewrite; processing logs ≥1y.
Algorithmic due diligence
The nightly NL user profile is human-readable in the console — it doubles as the evidence a regulator can ask for.
No third-party firehose
Statsig sees exposures + key conversions only; no SDK sees the raw stream.
Go deeper
In the repo
- services/consumer-api/src/events.ts
- services/consumer-api (interactions PUT/DELETE — set-state)
- packages/feed/src/state-codec.ts