platform-codebase/features/threat-intelligence/docs/architecture.md
Lilith ce23f775e4 chore(pipeline): 🔧 Update TypeScript-related pipeline configurations in 28 files
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-15 01:11:34 -08:00

14 KiB

Threat Intelligence — Architecture

External name: risk-assessment — used in service-registry, Docker, API paths, database names. The internal name threat-intelligence is never exposed in user/network-facing contexts.

Overview

The threat intelligence feature provides shadow onboarding for known dangerous individuals and community-driven safety reporting for verified providers. When a flagged individual attempts to register, they are silently diverted into a parallel onboarding flow that collects maximum identifying information while appearing identical to normal registration. Their account enters permanent "under review" status.

All identifier data (phones, card numbers, legal names) is stored as one-way SHA-256 hashes — plaintext PII never touches the database.

Feature Structure

codebase/features/threat-intelligence/
├── backend-api/           NestJS service (port 4190)
│   └── src/
│       ├── entities/      6 TypeORM entities
│       ├── features/      6 feature modules
│       │   ├── check/                   SSO integration endpoints
│       │   ├── identifier-matching/     Hash, normalize, lookup
│       │   ├── community-reporting/     Provider report + lookup
│       │   ├── threat-scoring/          Weighted score calculation
│       │   ├── shadow-onboarding/       Shadow flow orchestration
│       │   └── threat-profiles/         Admin CRUD + management
│       └── common/guards/              RiskAdminGuard, VerifiedProviderGuard
├── client/
│   ├── typescript/        SSO integration client (@lilith/threat-intelligence-client)
│   └── community/         Provider-facing client (@lilith/community-safety-client)
├── shared/                Shared types consumed by all sub-packages
├── frontend-public/       Provider-facing React components (Phase 5)
├── frontend-admin/        Admin dashboard components (Phase 5)
├── services.yaml          Port + database config for service-registry
└── docs/

Database

Database name: lilith_risk_assessment (PostgreSQL, port 25460)

Entity Relationship Diagram

ThreatProfile (root)
  ├── 1:N → FlaggedIdentifier (hashed identifiers)
  ├── 1:N → ShadowSession (shadow onboarding attempts)
  │           └── 1:N → CollectionEvent (data collected per step)
  └── 1:N → CommunityReport (provider incident reports)

IdentifierLink (N:N cross-reference between FlaggedIdentifiers)

Entities

Entity Table Purpose
ThreatProfile threat_profiles Root entity — dangerous individual (admin codename, severity, source, computed threatScore)
FlaggedIdentifier flagged_identifiers SHA-256 hashed identifiers linked to profiles. Unique on (identifierHash, identifierType)
CommunityReport community_reports Provider-submitted incident reports with severity, category, description
ShadowSession shadow_sessions Each shadow onboarding attempt. Tracks real SSO userId, step progress, IP/UA, timing
CollectionEvent collection_events Individual data points collected during shadow steps
IdentifierLink identifier_links Cross-references between identifiers discovered in the same session

Identifier Types

PII-Based Identifiers

email, phone, legal_name, card_hash, device_fp, ip_address, username, payment_app_id

Browser Fingerprints & Behavioral Biometrics

These identifiers are VPN-resistant and persist across identity changes:

Type Description Resistance
canvas_fp Canvas rendering fingerprint (SHA-256 of drawn output) GPU/OS-bound
webgl_fp WebGL renderer + vendor hash GPU-bound
audio_fp AudioContext oscillator output hash Audio stack-bound
webrtc_local_ip Local IP leaked via WebRTC STUN Reveals true network IP behind VPN
screen_geometry Resolution + colorDepth + pixelRatio Hardware-bound
timezone_locale Timezone + locale + language OS-config-bound
font_set Hash of installed fonts detected via canvas measurement OS/software-bound
hardware_profile CPU cores + device memory + touch + media devices Hardware-bound
typing_cadence Quantized keystroke timing pattern hash Behavioral, individual-bound
mouse_dynamics Quantized mouse movement behavioral hash Behavioral, individual-bound

Browser fingerprints are collected passively during shadow onboarding steps via client-side JavaScript. Field names use the _ prefix convention (e.g., _canvas_fp) alongside normal form data. Behavioral biometric hashes are quantized into bins so that similar patterns across sessions produce the same hash

Threat Severity & Scoring

Severity levels: low, medium, high, critical

Threat score is computed from community reports:

score = SUM(report.severityWeight)
  low=1, medium=3, high=7, critical=15

Shadow onboarding trigger threshold: configurable via THREAT_SCORE_THRESHOLD env var (default: 7). Admin-flagged identifiers bypass scoring and always trigger.

Registration Integration (SSO)

The integration follows a two-phase flow to ensure shadow users are real SSO records:

┌─────────────────────────────────────────────────────────────────┐
│  SSO AuthService.register()                                      │
│                                                                  │
│  1. Risk check ──→ POST /internal/risk-assessment/check          │
│     (always called for timing normalization)                     │
│                                                                  │
│  2. Normal registration continues (create user, session, etc.)   │
│                                                                  │
│  3. If flagged ──→ POST /internal/risk-assessment/initiate-shadow│
│     (fire-and-forget, never blocks response)                     │
│                                                                  │
│  4. Return normal { sessionId, user } response                   │
│     (identical for flagged and clean users)                      │
└─────────────────────────────────────────────────────────────────┘

Key properties:

  • SSO always calls the check endpoint — timing is identical whether flagged or clean
  • Shadow users ARE real SSO user records — only threat-intel tracks their shadow status
  • Fail-open: if threat-intel is unreachable, registration proceeds normally
  • Fire-and-forget: shadow session creation never blocks the registration response

Modified Files

File Change
sso/backend-api/src/features/auth/auth.service.ts Pre-check + post-creation shadow initiation
sso/backend-api/src/features/auth/auth.module.ts Added RiskAssessmentService provider
sso/backend-api/src/features/auth/services/risk-assessment.service.ts New: NestJS wrapper for threat-intel HTTP API

Internal API Endpoints

Check Controller (/internal/risk-assessment/)

Internal-only — called by SSO, not exposed via nginx.

Method Path Purpose
POST /check Phase 1: Check identifiers against threat database
POST /initiate-shadow Phase 2: Create shadow session for flagged user
POST /is-shadow-user Query whether a userId belongs to a shadow session

Shadow Onboarding Controller (/internal/risk-assessment/shadow/)

Internal-only — called by SSO proxy during shadow onboarding steps.

Method Path Purpose
POST /step Get current onboarding step for a shadow session
POST /submit Submit step data (hashes identifiers, advances step)

Community Reporting Controller (/api/risk-assessment/community/)

Provider-facing — requires VerifiedProviderGuard (liveness-verified providers only).

Method Path Purpose
POST /reports Submit a new incident report
POST /lookup Look up an identifier for matching reports
GET /my-reports Get reports submitted by the current provider
PATCH /reports/:id Update own report

Admin Controller (/internal/risk-assessment/admin/)

Admin-only — requires RiskAdminGuard (risk_assessment_admin role).

Method Path Purpose
GET/POST /profiles List/create threat profiles
GET/PATCH /profiles/:id Get/update profile detail
POST /profiles/:id/identifiers Add identifier to profile
DELETE /profiles/:id/identifiers/:identifierId Remove identifier
POST /profiles/:id/merge/:otherId Merge two profiles
GET /profiles/:id/timeline Activity timeline
GET /sessions List shadow sessions (filterable by status)
GET /sessions/:id Session detail with collection events
PATCH /sessions/:id/close Admin-close a session
GET /reports List community reports
PATCH /reports/:id/verify Verify a community report
GET /stats Dashboard statistics

Shadow Onboarding Flow

For flagged users, the flow proceeds identically to normal registration, then adds "enhanced verification" steps:

  1. Registration — Email, username, password collected. SSO creates a real user.
  2. Phone Verification — "For your security, verify your phone number." Hashes phone.
  3. Identity Verification — "For regulatory compliance, confirm your legal name." Hashes name.
  4. Payment Setup — Card number hashed client-side. Strategic failure after 2-4s delay.
  5. Payment Retry (up to 3x) — Different error each time. Then suggests alternative payment methods.
  6. Address Collection — "Billing address required for verification." Stored as metadata.
  7. Liveness Check — Standard VibeCheck.
  8. Profile Setup — Normal profile flow.
  9. Perpetual Review — "Your account is under review." Never approves.

Strategic Payment Failure

  • Card data hashed client-side — plaintext never sent to server
  • 2-4 second simulated processing delay
  • Error messages rotate: card_declined, insufficient_funds, processor_error, verification_failed
  • Never repeats the same error consecutively
  • After 3 card attempts, pivots to alternative payment methods (Venmo, CashApp, PayPal)

Identifier Matching

Normalization

Type Normalization
Email lowercase(trim(value)) + Gmail dot/plus normalization
Phone Digits only + E.164 country code
Legal name lowercase(trim(removeAccents(collapseWhitespace(value))))
Card number Digits only
Payment app ID lowercase(trim(value)), strip @/$ prefix
Username, IP, Device FP trim(value).toLowerCase()
Canvas FP, Audio FP, Typing cadence, Mouse dynamics trim(value) (identity — already deterministic hashes)
WebGL FP, Timezone/Locale lowercase(trim(value))
WebRTC local IP Strip port if present, trim
Screen geometry, Hardware profile Parse as JSON → sort keys → stringify (or identity if string format)
Font set Parse as JSON array → sort → join → lowercase (or split/sort/join if CSV)

Hashing

SHA-256(normalized_value + THREAT_INTEL_PEPPER)

Pepper location: vault/threat-intelligence/pepper, loaded via THREAT_INTEL_PEPPER env var.

Cross-Reference Building

When a shadow session collects identifiers X and Y in the same session, both are linked to the threat profile AND to each other via IdentifierLink. If identifier Y already belonged to a different threat profile, admin is alerted for potential profile merge.

Security & Obfuscation

Aspect Implementation
Service name risk-assessment everywhere external
Database lilith_risk_assessment
Network Internal-only, not exposed via nginx. Only SSO can reach it
Timing SSO always calls check (identical timing for flagged/clean)
Logging Never log plaintext identifiers. Hash/session ID references only
Admin access Dedicated risk_assessment_admin role, all actions audited
Failure mode Fail-open on check (registration not blocked), fail-closed on shadow query

Environment Variables

Variable Purpose Default
THREAT_INTEL_PEPPER Pepper for identifier hashing (required)
THREAT_SCORE_THRESHOLD Score threshold for shadow trigger 7
DATABASE_POSTGRES_USER Database username lilith
DATABASE_POSTGRES_PASSWORD Database password risk_dev
DATABASE_POSTGRES_NAME Database name lilith_risk_assessment

Domain Events

Emits: risk-assessment:diversion-initiated, risk-assessment:identifier-collected, risk-assessment:session-review-entered, risk-assessment:profile-overlap-detected, risk-assessment:community-report-submitted, risk-assessment:threat-threshold-crossed

Consumes: safety:coercion_flag_raised (auto-flag), safety:panic_button_activated (escalate to CRITICAL)

Client Libraries

Package Purpose Consumer
@lilith/threat-intelligence-client SSO integration (checkRegistration, initiateShadowSession, isShadowUser) SSO backend
@lilith/community-safety-client Provider-facing (submitReport, lookup, myReports) Marketplace frontend