Skip to main content

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:

  1. 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 — to POST /api/v1/discovery/batch. But apps/api/internal/handlers/discovery_batch.go persists only host for discovery_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.

  2. MCP enforcement is built but unwired. apps/api/internal/mcp/{policy,firewall,audit}.go is 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 parses tools/call JSON-RPC envelopes but drops the tool name/args; the CLI carries tool_name end-to-end but decide() ignores it. No MCP config file (.cursor/mcp.json, .mcp.json, claude_desktop_config.json) is scanned anywhere; there is no MCP server registry.

  3. Strategic asset: a hard privacy invariant. Across surfaces, raw content never leaves the machine — the Interaction model stores content_hash (SHA-256) only ("Raw content is never stored"), and report.ts transmits 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

  1. 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.

  2. 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_steps as a table template, InteractionTraceTimeline / interactionTraceModel as UI, the source_type='cli' enum already modelled, the interactions.created NATS subject).

  3. Persist the CLI forensic signal into a dedicated agent_session_events table via a dedicated ingest endpoint — do not overload interactions (prompt/file/code-centric) or the discovery/catalog path (host inventory). Follow the multi-tenant RLS + idempotent upsert pattern of interaction_lineage_steps.

  4. For Epic B, wire the firewall cheapest-first. B1 = expose the existing apps/api/internal/mcp firewall behind an HTTP handler + per-tenant policy persistence + wire the metrics. B2 = enforce a tool_name allowlist in the CLI decide() 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.

  5. 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_MODE local content-detection contract; conditional/session work composes on top.

References