ADR-029: Content-free session forensics & MCP enforcement (response to Varonis/Cursor)
Status
Accepted
Context
On 2026-07-08 Varonis extended its Atlas platform to Cursor, marketing three
capabilities for AI-native coding: runtime enforcement (an "AI Gateway" in the live
request path), full session forensics (reconstructing prompts, tool usage, MCP server
activity, and command execution), and repo-level shadow-AI discovery (skills, rules,
MCP configs). Full competitive analysis: docs/competitive/2026-07-08-varonis-cursor-gap.md.
A code audit of the monorepo surfaced two concrete gaps and one strategic asset:
-
Session reconstruction is absent, yet the signal already exists. The CLI hooks (
surfaces/cli-hooks/src/report.ts) already transmit a rich per-event forensic signal —session_id,tool_name,hook_event,surface,signal_types,signal_count, severity flags — toPOST /api/v1/discovery/batch. Butapps/api/internal/handlers/discovery_batch.gopersists onlyhostfordiscovery_type:"agent"and discards the rest. There is no session entity, no timeline query, and no GraphQL type for sessions. Each event is stored standalone. -
MCP enforcement is built but unwired.
apps/api/internal/mcp/{policy,firewall,audit}.gois a complete tool-call firewall (allow/deny, chain-depth, cross-tenant-leak, exfil heuristics, hashed audit) — but POC-only: no HTTP route, and its metrics are called only from tests. The extension parsestools/callJSON-RPC envelopes but drops the tool name/args; the CLI carriestool_nameend-to-end butdecide()ignores it. No MCP config file (.cursor/mcp.json,.mcp.json,claude_desktop_config.json) is scanned anywhere; there is no MCP server registry. -
Strategic asset: a hard privacy invariant. Across surfaces, raw content never leaves the machine — the
Interactionmodel storescontent_hash(SHA-256) only ("Raw content is never stored"), andreport.tstransmits only signal metadata. Varonis' forensics reconstruct raw prompts and commands through a US cloud gateway.
The decision: how do we answer Varonis without abandoning what differentiates Noxys?
Decision
-
Build content-free session forensics, not a content-exfiltrating gateway. Session reconstruction assembles the timeline of who / when / which surface / which tool / which signal types / which decision — keyed on
content_hash+ signal metadata, with an optional redacted preview only where local redaction/MitM already produced one. The privacy invariant is treated as a product feature (RGPD-by-design), not a limitation. -
Sequence Epic A (session reconstruction) before Epic B (MCP & agents). Epic A is the frontal counter to Varonis' headline and reuses the most existing scaffolding (
interaction_lineage_stepsas a table template,InteractionTraceTimeline/interactionTraceModelas UI, thesource_type='cli'enum already modelled, theinteractions.createdNATS subject). -
Persist the CLI forensic signal into a dedicated
agent_session_eventstable via a dedicated ingest endpoint — do not overloadinteractions(prompt/file/code-centric) or the discovery/catalog path (host inventory). Follow the multi-tenant RLS + idempotent upsert pattern ofinteraction_lineage_steps. -
For Epic B, wire the firewall cheapest-first. B1 = expose the existing
apps/api/internal/mcpfirewall behind an HTTP handler + per-tenant policy persistence + wire the metrics. B2 = enforce atool_nameallowlist in the CLIdecide()path. Defer extension capture/block (B3), MCP config discovery + server registry (B4), and proxy MCP-over-HTTP inspection (B5) to the explicit "MCP & agents à terme" bucket — not scheduled now. -
Explicitly out of scope: data-estate classification (SharePoint/Salesforce — not a Noxys wedge) and named prompt-injection detection (backlog).
Consequences
Positive
- Directly neutralises Varonis' "session forensics" headline while keeping the on-device / EU-sovereignty differentiator intact.
- High reuse: Epic A leans on an existing table template, an existing timeline UI, an existing enum value, and an existing event bus.
- Epic B unlocks a fully built firewall for near-zero engine cost (B1) and adds real MCP enforcement at the cheapest seam (B2) before committing to greenfield work.
- Fixes a live data-loss bug: forensic signal the CLI already pays to transmit is currently discarded.
Negative
- Content-free forensics cannot show a reviewer the raw offending prompt/command the way Varonis can — mitigated by hashes, signal types, redacted previews, and positioned as a privacy feature.
- Two new backend surfaces (session-events table + query API) add schema, RLS, and export maintenance.
- User attribution for opaque CLI
session_id(device → user mapping) is unresolved and must be designed in Epic A.
Neutral
- Epic B's deferred sprints (B3–B5) remain documented but unscheduled; revisit when agent coverage becomes a priority.
- No change to the
NOXYS_MODElocal content-detection contract; conditional/session work composes on top.
References
docs/competitive/2026-07-08-varonis-cursor-gap.mddocs/roadmap/2026-07-08-varonis-gap-moc.md- Epic A:
docs/tasks/api/feature/2026-07-08_session-reconstruction-forensics/spec.md - Epic B:
docs/tasks/mcp/feature/2026-07-08_mcp-agent-enforcement/spec.md apps/api/docs/security/mcp-threat-model.md- ADR-019 — Prompt capture, storage, retention & sovereignty
- ADR-020 — Interaction lineage visualization