lilith-platform.live/codebase/@features/user-data
2026-06-10 21:33:02 -07:00
..
dashboard-network chore(api): 🔧 Update dev configs for API module—local debug settings in .env.development, TypeScript paths/strictness in tsconfig.json, and dashboard-network dev/proxy settings in vite.config.ts 2026-05-16 04:42:56 -07:00
frontend-client feat(frontend-client): Implement comprehensive analytics system with funnel tracking, custom hooks, and global context for behavior/engagement tracking 2026-05-14 22:44:13 -07:00
mcp-server breaking(api): 💥 Update client entity schema, prospector classification, and admin photo export routes with breaking changes 2026-06-10 14:58:14 -07:00
shared feat(user-data): Add analytics client, backend client, batch queue, device collector, logger, and UTM extractor utilities for user data processing 2026-05-14 22:44:14 -07:00
website-backend-users deploy(infrastructure): 🚀 Update server configurations, deployment scripts, and infrastructure files; increment build version and deployment count 2026-06-10 21:33:02 -07:00
website-frontend-users breaking(api): 💥 Update client entity schema, prospector classification, and admin photo export routes with breaking changes 2026-06-10 14:58:14 -07:00
README.md feat(user-data): Add comprehensive user data documentation, update AboutPage tooltips, and configure service settings 2026-06-10 21:33:02 -07:00
services.yaml feat(user-data): Add comprehensive user data documentation, update AboutPage tooltips, and configure service settings 2026-06-10 21:33:02 -07:00

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 (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 (or quinn-api relay) and /api/* (after composition) to quinn-api analytics query surface. Both dev and prod use quinn-api + the prod lilith_analytics DB (no dev DB). 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 (or via quinn-api public analytics relay in consolidated paths) at ~/Code/@applications/@analytics/services/collector/src/tracking/:

  • identity.service.tsvisitorIdDaily(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) <script> beacon in 4 HTMLs under adult-therapy-tours/web/ adulttherapytour.com
ATT SEO bait <script> beacon in apa-singles, fuckatapa, fuckmeat per hostname
Sansonnet (preview, pre-prod) <script> beacon in 4 HTMLs under .project/previews/cocotte-umbrella/sites/sansonnet/ maisonsansonnet.com

Not yet instrumented: futa-singles SEO surface (no live domain yet), cocotte brand under the sansonnet umbrella.

Known gaps

  • The rich dashboard query surface (sessions/metrics, engagement/pages, funnels, etc.) is now implemented inside quinn-api (using direct SQL against the prod lilith_analytics DB). The old separate Nest @analytics query API (:4003) is no longer used by the website analytics BFF/dashboard.
  • CORS for /beacon.js consumers — verify on canary that the collector at :4001 accepts POSTs from the new origins (adulttherapytour.com, apa.singles, fuckatapa.com, fuckmeatamericanpsychiatricassociation.com, maisonsansonnet.com). The existing analytics-client at atlilith.com and transquinnftw.com already works, suggesting the collector emits Access-Control-Allow-Origin: * — but confirm before declaring done.
  • Cross-Corp Flow dashboard tab is not yet built (deferred to a follow-on objective).
  • Pre-existing typecheck error in tracking.controller.ts line 59 (doNotTrack type mismatch in ClientDeviceDto) — unrelated to this work but blocks tsc --noEmit on the collector. Skipped in this objective.

Privacy posture

  • No cookies, no localStorage, no fingerprinting.
  • IP is processed (for the daily hash) but never stored — only the 32-byte sha256 digest persists alongside events.
  • device-enrichment.service separately stores an ipHash for geo/bot detection; that is unchanged and unrelated to visitor_id_daily.