# user-data Parent feature: cross-domain, cross-corp visitor flow on the lilith-platform.live mesh. Absorbs what used to be `@features/analytics/` and adds two missing layers — a server-side cookie-free visitor identity and a first-class corp/domain taxonomy — so a visitor's journey across `adulttherapytour.com → maisonsansonnet.com → transquinnftw.com` becomes a single SQL join. Deployed surface: [`quinn.data`](../../../deployments/@domains/quinn.data/) (data.transquinnftw.com). ## Shape | Path | Package | Role | |---|---|---| | `shared/` | `@lilith/analytics-client` | Browser client core — batch queue, attribution, device collector. No React dep. | | `frontend-client/` | `@lilith/analytics-client-react` | React provider + hooks (`useAnalytics`, `useFunnelEvents`, `usePageViewTracking`). | | `website-backend-users/` | `@lilith/analytics-website-backend` | Stateless BFF proxy. Forwards `/track/*` to the collector and `/api/*` to the query API. Does NOT do identity — that lives in the collector. | | `website-frontend-users/` | `@lilith/analytics-website-users` | Dashboard SPA at `data.transquinnftw.com/`. Cross-Corp Flow tab is TODO. | | `beacon/beacon.js` | (single static file, no package) | ~4 KB cookie-free IIFE for static-HTML sites. Served by quinn.data nginx at `https://data.transquinnftw.com/beacon.js`. Staged into the SPA dist by `quinn.data/deploy.sh` step 1a. | ## Identity & dimensioning — lives in the collector, NOT here The identity hash and corp/domain resolution run inside the @analytics collector at `~/Code/@applications/@analytics/services/collector/src/tracking/`: - `identity.service.ts` — `visitorIdDaily(ip, ua, lang) = sha256(daily_salt ‖ ip ‖ ua ‖ lang)`. Salt rotates at 00:00 UTC, stored in `visitor_salts`, purgeable after 7 days. - `domain-resolver.service.ts` — Origin → `domains` row → `(corp_id, domain_id)`. Cached in-process; reloaded on cache miss. - Both are wired into `TrackingService.trackView()` and stamp every `raw_events` row. Schema lives in `@applications/@analytics/services/api/migrations/1747200000000-AddVisitorIdentityAndCorpDomain.ts`. Seed mirror: `@applications/@analytics/infrastructure/seed-cross-domain.sql`. This split is intentional: identity logic has exactly one consumer (the collector), so publishing it as a `@lilith/*` package would be premature abstraction. ## Visitor identity model - **No cookies.** No localStorage. No fingerprint canvas. - `visitor_id_daily = sha256(salt_today ‖ X-Real-IP ‖ User-Agent ‖ Accept-Language)`. - Same visitor → same id across all our domains within a UTC day. - Salt rotates at 00:00 UTC; older salts are purged → historical re-identification is mathematically impossible. - Tab-scoped `sessionId` (client-side, in-memory) is preserved for per-tab funnels. ## Corp / domain taxonomy Every event row carries `corp_id` and `domain_id`. Seed: | corp slug | legal_name | |---|---| | `lilith-apps-ehf` | Lilith Apps ehf | | `att` | Adult Therapy Tour | | `sansonnet` | Maison Sansonnet | | `transquinnftw` | transquinnftw | Seeded domains include `adulttherapytour.com`, `adulttherapy.tours`, `apa.singles`, `fuckatapa.com`, `fuckmeatamericanpsychiatricassociation.com`, `maisonsansonnet.com`, `transquinnftw.com`, `tqftw.com`, `atlilith.com`, `trustedmeet.com`. See the migration file for the full list. ## Instrumented sites | Site | Instrumentation | Domain row | |---|---|---| | Landing | React provider (`AnalyticsProvider`) | `atlilith.com`, `trustedmeet.com` | | Provider website (transquinnftw.com) | React provider | `transquinnftw.com` | | ATT canonical (`adulttherapytour.com`) | `