ADR-020 — Interaction lineage visualization
1. Context
Console operators (CISO, security analyst, compliance officer) currently see Interactions as a flat list with metadata. To investigate "what happened to this prompt" they must mentally stitch together: which surface captured it (extension/agent/proxy), which processing components ran (SLM/redaction/policy/Presidio/multi-modal), what the decision was, where it ended up, and what was detected at each hop. No surface today shows this end-to-end.
Reference design submitted by Jerome (Azure ML resource lineage style — horizontal DAG, circular pastel nodes, current-resource highlight, badge counts, minimap). Style is clean, scannable, drill-down via click. We adopt it.
This ADR also resolves the deferred "Flow Graphs" item in noxys-console/CLAUDE.md Roadmap M6+ — we bring React Flow into the codebase now, with lineage as the first usage.
2. Decision summary
- Visualization: React Flow v11+, horizontal DAG (left → right), dagre auto-layout.
- Style: brand-aligned circular nodes (~80px) with 2-line labels, pastel backgrounds keyed to node category, light dotted grid background, edges thin gray (no labels by default).
- Surface:
InteractionDetailPagenew tab "Lineage" alongside Evidence / Policy / Audit. Full-view modal for fullscreen exploration. Side panel for click drill-down. - Step model: every component in the pipeline (extension / agent / proxy / api / sidecar / llm-service) emits one row to
interaction_lineage_steps, correlated by propagatedtrace_id(headerX-Noxys-Trace-Id). - Tier gating (per ADR-019 storage classes): Free=3-node summary, Pro=full processing chain + detection metadata + latency, Enterprise=+raw payload preview at click (only if tenant opted into raw storage).
- First usage of React Flow in noxys-console — sets pattern for future flow visualizations (policy graph, AI service map, multi-tenant deployment view).
3. Visual specification
Layout
- Horizontal DAG, left = source (User), right = destination (LLM service), bottom row = response path mirror.
- Auto-layout via
@dagrejs/dagrewith rank-direction LR. - Branches (e.g. attachments processed in parallel by multi-modal lanes) converge at the Decision node.
Nodes
| Category | Color (light bg / icon ring) | Icon | Examples |
|---|---|---|---|
| User | Lavender #E9E7FF / Purple #6C63FF | UserCircle | end-user identity |
| Capture surface | Mint #D4F5EC / Cyan #00D4AA | Chrome / Cpu / Network | Browser Extension / Endpoint Agent / Proxy |
| Processing | Sky #DBEAFE / Blue #3B82F6 | Sparkles / Shield / Brain / FileSearch | SLM / Smart Redaction / Multi-modal lane / Presidio / Semantic Policy |
| Decision | green/amber/red | Check / EyeOff / Ban | Allow / Redact / Block |
| Storage | Slate #F1F5F9 / Slate #64748B | Database | aggregate / hashed / redacted / raw |
| LLM service | branded per provider | per-provider logo | OpenAI / Anthropic / Mistral / etc. |
| Attachment | Peach #FFEFD5 / Amber #F59E0B | FileText / Image / Mic / Code | File / Image / Audio / Code block |
Each node renders:
- Icon centered in circle
- 2-line label below (component name + version)
- Optional badge top-right: red dot (block) / amber dot (redact) / count of detections
- Current step (when navigating from drill-down) ringed in Purple + cyan label "(Current step)"
Edges
- Thin gray lines, simple bezier
- Default: no label
- Hover: tooltip with
latency_ms+ bytes-transferred - Animated dot in transit when interaction is live (V2)
Controls (mirror Azure reference)
- Top-right: "Full View" button → fullscreen modal
- Bottom-left: zoom +/-, fit-view, layout toggle (horizontal/vertical), pin, settings
- Bottom-right: minimap (Azure-style miniature thumbnail)
- Background: dotted grid pattern using
BackgroundVariant.Dotsfrom React Flow
Interactions
- Click node → side panel slides in from right with full step detail
- Hover edge → latency tooltip
- Right-click node → context menu (filter to this surface only, copy trace_id, view component docs)
- Filter toolbar (Pro+): toggles to hide/show by surface (Extension only, Proxy only, etc.)
- Search (Enterprise): jump to step by component name in graphs with 30+ nodes
4. Data model
CREATE TABLE interaction_lineage_steps (
id uuid PRIMARY KEY,
interaction_id uuid NOT NULL REFERENCES interactions(id),
trace_id text NOT NULL, -- propagated extension→proxy→api→sidecar→llm
step_order int NOT NULL, -- monotonic per trace_id
parent_step_id uuid REFERENCES interaction_lineage_steps(id), -- for parallel branches (attachments)
surface text NOT NULL, -- 'extension'|'agent'|'proxy'|'api'|'sidecar-presidio'|'sidecar-mm'|'llm-service'
component text NOT NULL, -- 'capture'|'slm'|'redaction'|'presidio'|'policy-eval'|'mm-image'|'mm-audio'|'mm-video'|'mm-code'|'decision'|'storage-write'|'llm-call'|'response-scan'
component_version text, -- e.g. extension '1.2.3-staging.045', model 'mistral-large-2502'
action text NOT NULL, -- 'classify'|'redact'|'eval-policy'|'allow'|'block'|'persist'|'forward'|'scan-output'
decision_reason text, -- when component=decision
input_summary jsonb, -- {type, length, lang, has_pii_flag, has_secret_flag} — never raw text
output_summary jsonb, -- {redactions_count, classifications, scores}
detections jsonb, -- [{type:'pii', subtype:'email', count:3, severity:'medium', confidence:0.93}, ...]
latency_ms int,
bytes_in int,
bytes_out int,
storage_class_applied text, -- 'aggregate'|'hashed'|'redacted'|'raw' (when component=storage-write)
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
occurred_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX idx_lineage_interaction_order ON interaction_lineage_steps(interaction_id, step_order);
CREATE INDEX idx_lineage_trace ON interaction_lineage_steps(trace_id);
CREATE UNIQUE INDEX uq_lineage_trace_step ON interaction_lineage_steps(trace_id, step_order);
The unique index on (trace_id, step_order) makes step ingestion idempotent — out-of-order replay or duplicate emits don't break the graph.
5. Trace ID propagation contract
X-Noxys-Trace-Id header propagated through every hop. Format: tr_<ulid> (Universally Unique Lexicographically Sortable Identifier — sortable by time, ~26 chars).
Generation:
- Extension (browser surface): generated at content-script capture moment, attached to first POST to noxys-api or noxys-proxy.
- Agent (endpoint): generated at native capture moment.
- Proxy (network): generated at HTTP intercept; if upstream client already sent one (e.g. customer's own observability), preserve and chain via
metadata.upstream_trace_id.
Each component appends a step entry as it processes; step_order = monotonic counter (atomic increment per trace).
6. Tier gating (per ADR-019)
| Lineage capability | Free | Pro | Enterprise |
|---|---|---|---|
| 3-node summary (Capture → Decision → Service) | yes | yes | yes |
| Full step chain | — | yes | yes |
| Detection metadata per step | — | yes | yes |
| Latency + bytes per step | — | yes | yes |
| Click drill-down side panel (input/output summaries) | — | yes | yes |
| Raw payload preview at node click | — | — | opt-in (raw storage tier) |
| Filter / search / multi-modal branches | — | yes | yes |
| Cross-interaction trace search | — | — | yes |
| Export lineage as PNG/SVG | basic | yes | yes (also as JSON for BI) |
7. UX surfaces
InteractionDetailPage — new "Lineage" tab
Layout:
- Tab strip:
Overview | Lineage | Evidence | Policy | Audit - Lineage tab body:
- Top toolbar: filter (surface checkboxes), search, fit-view, full-view button
- Main canvas: React Flow graph (60% viewport height)
- Bottom: timeline strip (alternative chronological view, optional V2)
Side panel (click node)
Sticky drawer right side, ~480px wide:
- Header: node icon + component + version + timestamp
- Detail sections (collapsible, tier-gated):
- Action & decision — what this step did, why
- Detections — list with severity badges
- Performance — latency, bytes in/out
- Storage — class applied, retention, encryption key ref (Enterprise)
- Evidence preview — input/output summary (Pro+) or raw quote (Enterprise opt-in)
- Linked artifacts — config policy that triggered, AI-BOM model card, related interactions
Full View modal
Fullscreen dialog (shadcn <Dialog>) with:
- Same canvas, larger, zoom-pan-pinch enabled
- Time-axis ruler at bottom (mode toggle: spatial DAG vs chronological timeline)
- Multi-select nodes for comparison
8. Tech stack
- React Flow v11+ — chosen over alternatives (vis.js too generic, dagre-d3 lower-level, mermaid too rigid for interactivity)
- @dagrejs/dagre for auto-layout (LR direction, configurable rank/node-sep)
- shadcn dialog for Full View modal (consistent with rest of console)
- Lucide icons (already standard)
- i18n: 12 locales for static labels (matching console)
- a11y: ARIA-graph role, keyboard navigation across nodes (Tab cycles, Enter opens drill-down)
9. Performance considerations
- Tenants with high interaction volume (10k/day → 30k+ steps/day) — graph for single interaction = 5-50 nodes typically; query bounded by
interaction_id. - Cross-interaction queries (Enterprise tier feature) require pgvector or denormalized summary table — defer to V2.
- Real-time updates (live in-progress interactions): V2 via SSE/WebSocket. V1 = snapshot.
- Caching: lineage tree cached in Redis with TTL=1h after interaction completion (immutable thereafter).
10. Sprint plan — 8 sprints, 15d
| ID | Title | Repo | Window | Effort | Priority |
|---|---|---|---|---|---|
| LINEAGE-1 | DB schema interaction_lineage_steps + trace_id propagation contract docs | noxys-api | W25-26 | 2d | P1 |
| LINEAGE-2 | Step ingestion endpoint + emit hooks in extension/agent/proxy/api/sidecar | noxys-api | W25-26 | 3d | P1 |
| LINEAGE-3 | API endpoint GET /interactions/:id/lineage (tree shape, tier-gated detail) | noxys-api | W25-26 | 1d | P1 |
| LINEAGE-4 | React Flow component + brand-styled custom nodes (8 node types) | noxys-console | W27-28 | 4d | P1 |
| LINEAGE-5 | Side panel + click drill-down + tier-aware detail rendering | noxys-console | W27-28 | 2d | P2 |
| LINEAGE-6 | Full View modal + filters + minimap + zoom controls | noxys-console | W27-28 | 1d | P2 |
| LINEAGE-7 | Multi-modal branch rendering (parallel attachment lanes) | noxys-console | W29-30 | 1d | P2 |
| LINEAGE-8 | i18n labels (12 locales) + a11y (ARIA-graph, keyboard nav) | noxys-console | W29-30 | 1d | P3 |
LINEAGE-2 has cross-repo touchpoints: emit hooks must land in noxys-extension + noxys-proxy + sidecars too. Tracked as primary in noxys-api with follow-up slice issues.
11. Dependencies
- STORE-1 (ADR-019 schema) — must land before LINEAGE-1 (we use the same tier model in JWT claims).
- EXT-UX-4 (ADR-015 auto-enrollment) — JWT/header contract must include trace propagation field.
- AI-PLACE-2/3/4 — components only emit lineage steps once they exist; partial graph OK during rollout.
- ADR-013 (extension versioning) — telemetry headers already in place; add
X-Noxys-Trace-Idalongside.
12. Open questions
- Response path: same graph (mirror row below) or separate tab "Response"? Recommend same graph (V1) — mirrors Postman request/response style.
- Cross-interaction trace search (Enterprise): same prompt sent 3 times — group? Defer V2.
- Real-time live updates: V1 snapshot only. V2 SSE.
- Chart export: PNG via
html-to-imagelib straightforward; SVG native React Flow. - Token cost rendering: should latency edges also show estimated tokens consumed at LLM-call edge? Recommend yes (Pro+ feature, derived from LiteLLM telemetry per ADR-012).
13. Non-goals
- Editable lineage (operators don't modify the graph; it's a read-only investigative surface).
- Programmable lineage rules (this isn't a workflow editor — that's a separate future surface).
- Real-time agent-to-agent collaborative viewing (V3+).
- Replacing the audit log (lineage is visualization layer; audit log remains the immutable record).
14. Refs
- ADR-011 Multi-modal pipeline (lanes emit steps)
- ADR-013 Extension versioning (trace ID header alongside version headers)
- ADR-014 AI placement (components emitting steps)
- ADR-018 Subscription lifecycle (renumber: future CRM ADR moves to ADR-027; ADR-022 = UsersPage V2; ADR-024 = Authentication & MFA; ADR-026 = API/OpenAPI policy)
- ADR-019 Storage tiers (gates lineage detail depth)
- ADR-021 TanStack Query + Table adoption (LINEAGE-3..6 console pages adopt Query from day one)
- ADR-022 UsersPage V2 spec (consumes DataTable wrapper for users table; sources entitlements/cost from backend rollups)
- ADR-024 Authentication & MFA strategy (TOTP/WebAuthn/backup codes encrypted via KMS pattern; tenant token reveal + tenant-wide MFA enforcement)
- noxys-console CLAUDE.md "Flow Graphs Roadmap M6+" — first React Flow usage in codebase
- Reference design: Azure ML resource lineage (horizontal DAG, pastel nodes, badge counts, minimap)
- ADR-026 API completeness & OpenAPI policy (every endpoint defined in this ADR must have OpenAPI 3.1 spec entry; backfill obligation in §9)