diff --git a/docs/SPEC.md b/docs/SPEC.md index f8881bd..4381541 100644 --- a/docs/SPEC.md +++ b/docs/SPEC.md @@ -16,15 +16,15 @@ Agents send prompts containing PII, IP, internal records, and customer data to M Five shapes: -**P1.1 Over-sharing context.** The agent passes more of its context to a tool than the task requires. The tool persists or relays the surplus downstream. Nothing in the chain inspects the payload against a policy because nothing in the chain is responsible for inspecting it. +P1.1 Over-sharing context: The agent passes more of its context to a tool than the task requires. The tool persists or relays the surplus downstream. Nothing in the chain inspects the payload against a policy because nothing in the chain is responsible for inspecting it. -**P1.2 Chatty tool responses.** The tool returns more data than was asked for. The surplus sits in the agent context window for the rest of the session and propagates as context into every subsequent tool call, including ones with looser destinations than the original. +P1.2 Chatty tool responses: The tool returns more data than was asked for. The surplus sits in the agent context window for the rest of the session and propagates as context into every subsequent tool call, including ones with looser destinations than the original. -**P1.3 Cross-system compliance boundary violations.** Each individual tool call is authorized. The combination crosses a compliance boundary. The agent acts as a confused deputy between a covered system and an uncovered one, and nothing in the chain sees the data flow because nothing is watching the graph of calls, only the individual permissions on each. +P1.3 Cross-system compliance boundary violations: Each individual tool call is authorized. The combination crosses a compliance boundary. The agent acts as a confused deputy between a covered system and an uncovered one, and nothing in the chain sees the data flow because nothing is watching the graph of calls, only the individual permissions on each. -**P1.4 Transitive trust into upstream dependencies.** The customer approves an MCP server from their catalog. That server internally calls a third-party API the customer never authorized. The customer's data protection agreement covers the gateway. It does not cover the gateway's upstream dependencies. Phase 1 partially addresses this shape. Phase 2 closes it. +P1.4 Transitive trust into upstream dependencies: The customer approves an MCP server from their catalog. That server internally calls a third-party API the customer never authorized. The customer's data protection agreement covers the gateway. It does not cover the gateway's upstream dependencies. Phase 1 partially addresses this shape. Phase 2 closes it. -**P1.5 Session-context bleed.** Sensitive data from an earlier high-sensitivity tool call persists in the agent context window and bleeds into unrelated later tool calls. The leak surface is the session, not any single tool call. +P1.5 Session-context bleed: Sensitive data from an earlier high-sensitivity tool call persists in the agent context window and bleeds into unrelated later tool calls. The leak surface is the session, not any single tool call. OWASP: MCP10 (primary) - MCP01, MCP02, MCP05 (adjacent) @@ -34,13 +34,13 @@ Agents are non-deterministic by construction. A model can choose to call any too Four shapes: -**P2.1 Poisoned tool descriptions.** A tool description contains hidden instructions that the LLM reads as authoritative system context. The human admin sees a benign description in the catalog UI. The LLM treats the planted instruction as authoritative. Every call to that tool now triggers the injected behavior. +P2.1 Poisoned tool descriptions: A tool description contains hidden instructions that the LLM reads as authoritative system context. The human admin sees a benign description in the catalog UI. The LLM treats the planted instruction as authoritative. Every call to that tool now triggers the injected behavior. -**P2.2 Drive-by server installs.** A developer installs an MCP package, adds it to a shared config, or downloads from a marketplace without security review. The agent host auto-aggregates it with legitimate servers. There is no central registration authority for MCP servers. +P2.2 Drive-by server installs: A developer installs an MCP package, adds it to a shared config, or downloads from a marketplace without security review. The agent host auto-aggregates it with legitimate servers. There is no central registration authority for MCP servers. -**P2.3 Tool name collisions.** Two MCP servers expose the same tool name with the same JSON schema. The agent host has no protocol-level way to disambiguate by publisher identity. Routing depends on registration order or hash-map iteration order. MCP defines no signed manifest binding a tool name to a publisher. +P2.3 Tool name collisions: Two MCP servers expose the same tool name with the same JSON schema. The agent host has no protocol-level way to disambiguate by publisher identity. Routing depends on registration order or hash-map iteration order. MCP defines no signed manifest binding a tool name to a publisher. -**P2.4 Install-time scope too coarse for runtime.** The approved catalog contains tools that are correct for some workflows but catastrophic in others. There is no per-workflow scope narrowing. Install-time approval is too coarse for what a non-deterministic model might pick at runtime. +P2.4 Install-time scope too coarse for runtime: The approved catalog contains tools that are correct for some workflows but catastrophic in others. There is no per-workflow scope narrowing. Install-time approval is too coarse for what a non-deterministic model might pick at runtime. OWASP: MCP02 (primary) - MCP03, MCP07, MCP09 (adjacent) @@ -50,21 +50,21 @@ Even when policies exist, enterprises cannot prove to auditors, regulators, or c Two shapes: -**P3.1 Regulatory proof requests.** Regulators examining AI processing ask for per-decision evidence: which tool was invoked, which policy decided it, what payload left the boundary. A software-only gateway produces logs. Those logs are signed with software-held keys. The regulator's follow-up question -- "can you prove this was true at the time of processing, not just at the time of audit?" -- has no answer that does not bottom out in trusting the operator. (EU AI Act Art. 12, DORA Art. 9, NIST AI RMF) +P3.1 Regulatory proof requests: Regulators examining AI processing ask for per-decision evidence: which tool was invoked, which policy decided it, what payload left the boundary. A software-only gateway produces logs. Those logs are signed with software-held keys. The regulator's follow-up question, "can you prove this was true at the time of processing, not just at the time of audit?", has no answer that does not bottom out in trusting the operator. (EU AI Act Art. 12, DORA Art. 9, NIST AI RMF) -**P3.2 Customer pre-renewal questionnaires.** An AI vendor in late-stage renewal receives a questionnaire: for every agent action that touched customer data via MCP last quarter, can you provide evidence of which tool was invoked, which policy decided it was allowed, and what was in the payload after egress scrubbing? A SOC 2 report and an architecture diagram describe intended behavior, not proven runtime behavior. +P3.2 Customer pre-renewal questionnaires: An AI vendor in late-stage renewal receives a questionnaire: for every agent action that touched customer data via MCP last quarter, can you provide evidence of which tool was invoked, which policy decided it was allowed, and what was in the payload after egress scrubbing? A SOC 2 report and an architecture diagram describe intended behavior, not proven runtime behavior. OWASP: MCP08 ### Problem 4 : Identity and supply-chain risk on the server side -When an agent calls a third-party MCP endpoint, it has no cryptographic basis for trusting that the endpoint is what it claims to be, runs the code it claims to run, or behaves as advertised -- and no way to detect when that changes. +When an agent calls a third-party MCP endpoint, it has no cryptographic basis for trusting that the endpoint is what it claims to be, runs the code it claims to run, or behaves as advertised, and no way to detect when that changes. Two shapes: -**P4.1 Typosquatted or look-alike MCP packages.** MCP package registries are being seeded with malicious lookalikes. A developer installs the wrong package on autopilot, or an AI coding assistant suggests an install command with the wrong spelling. The malicious package presents a plausible MCP server interface and exfiltrates everything it sees. Phase 1 partially addresses this shape. Phase 2 closes it. +P4.1 Typosquatted or look-alike MCP packages: MCP package registries are being seeded with malicious lookalikes. A developer installs the wrong package on autopilot, or an AI coding assistant suggests an install command with the wrong spelling. The malicious package presents a plausible MCP server interface and exfiltrates everything it sees. Phase 1 partially addresses this shape. Phase 2 closes it. -**P4.2 Rug-pull at runtime via silent tool-definition mutation.** A previously-approved MCP server uses the MCP notifications/tools/list_changed message to silently modify tool definitions after the security review concluded. The tool name stays the same. The JSON schema stays the same. Only the description changes. The agent host does not flag the change because nothing in the protocol distinguishes drift from the approved manifest from a routine update. +P4.2 Rug-pull at runtime via silent tool-definition mutation: A previously-approved MCP server uses the MCP notifications/tools/list_changed message to silently modify tool definitions after the security review concluded. The tool name stays the same. The JSON schema stays the same. Only the description changes. The agent host does not flag the change because nothing in the protocol distinguishes drift from the approved manifest from a routine update. OWASP: MCP04, MCP09, MCP03 (runtime variant) @@ -74,7 +74,7 @@ OWASP: MCP04, MCP09, MCP03 (runtime variant) In the API era, gateways were secondary controls. Deterministic backends (DB schemas, auth checks, business-rule validators) caught misconfigured or bypassed gateways. In the agent era that contract breaks. Agents are non-deterministic by construction. Tool choices and payloads are model outputs, not deterministic code paths. There is no deterministic backstop behind the gateway that will catch an agent calling a tool it should not, sending data it should not, or being tricked by a poisoned response. -The gateway and the server are now primary control surfaces -- the only enforcement layers between an autonomous reasoning loop and the real world. Software enforcement was acceptable when these were backups. It is structurally insufficient when they are the only thing standing between a non-deterministic agent and sensitive data. Hardware is what makes those primary control surfaces themselves tamper-evident. +The gateway and the server are now primary control surfaces, the only enforcement layers between an autonomous reasoning loop and the real world. Software enforcement was acceptable when these were backups. It is structurally insufficient when they are the only thing standing between a non-deterministic agent and sensitive data. Hardware is what makes those primary control surfaces themselves tamper-evident. --- @@ -121,14 +121,14 @@ cMCP Runtime (TEE boundary) -- the sole MCP endpoint the agent host sees Tool (MCP Server) -- identity bound to catalog entry, not just tool name ``` -Network position: sits between agent host and MCP servers. The gateway is the only MCP endpoint the agent host is configured to reach. No changes required to existing MCP servers (HTTP/SSE transport -- see issue for stdio). +Network position: sits between agent host and MCP servers. The gateway is the only MCP endpoint the agent host is configured to reach. No changes required to existing MCP servers (HTTP/SSE transport; see issue for stdio). --- ## 5. TRACE Claim Schema The unit of proof handed to an auditor, produced per session (or per call, configurable). -The normative schema is `schemas/trace-claim.schema.json`, +The normative schema is `schemas/trace-claim.schema.json`, and a full worked example is in [the quickstart](quickstart.md). The envelope is a `GatewayClaim`: canonical TRACE v0.1 fields live under `trace`, cMCP-specific addenda live under `gateway`, and `signature` is detached (computed over every other field). @@ -198,7 +198,7 @@ Each binding makes a software-only-impossible property externally verifiable. | Binding | Attestation field | What it makes provable | |---------|-------------------|----------------------| -| Cedar policy bundle measured at enclave startup before any user code runs | policy_bundle_hash | The policy that ran is the policy that was approved -- no silent substitution | +| Cedar policy bundle measured at enclave startup before any user code runs | policy_bundle_hash | The policy that ran is the policy that was approved: no silent substitution | | Enforcement mode bound into attestation | enforcement_mode | Gateway ran in enforcing, not advisory | | Tool catalog measured at startup | tool_catalog.hash | Runtime catalog drift (P4.2 rug-pull) produces a measurement mismatch | | Audit signing key generated and sealed inside TEE, never exported | audit_chain_root + hardware-sealed key | Audit log cannot be reconstructed by a privileged insider | @@ -219,7 +219,7 @@ Each binding makes a software-only-impossible property externally verifiable. ### Coverage corrections (from threat model review) -APM payload capture (P1 shapes): Mitigated with default-deny egress policy, not Closed. Protection is operator-dependent -- an allowlist that permits the APM endpoint removes the protection. This must be stated as Mitigated in the threat model, not Closed. +APM payload capture (P1 shapes): Mitigated with default-deny egress policy, not Closed. Protection is operator-dependent: an allowlist that permits the APM endpoint removes the protection. This must be stated as Mitigated in the threat model, not Closed. SDK telemetry capture: Same as above. Mitigated with default-deny, not Closed. @@ -233,19 +233,19 @@ Across the four problems and 13 shapes, Phase 1 covers 11 outright and partially | Problem | Shape | Phase 1 coverage | |---------|-------|-----------------| -| P1 Data leakage | P1.1 Over-sharing context | Strong -- gateway egress DLP, attested | -| P1 Data leakage | P1.2 Chatty tool responses | Strong -- response inspection and classification-tag propagation, attested | -| P1 Data leakage | P1.3 Cross-system boundary violations | Strong -- call graph tracked; cross-boundary policy attested | -| P1 Data leakage | P1.4 Transitive trust into upstream dependencies | Partial -- gateway attests the immediate server; cannot reach into that server's own upstream calls. Phase 2 closes. | -| P1 Data leakage | P1.5 Session-context bleed | Strong -- gateway-attested session-level egress policy with sensitivity propagation | -| P2 Unsanctioned tools | P2.1 Poisoned tool descriptions | Strong -- tool-definition scanner inside attested gateway; catalog hash pinned | -| P2 Unsanctioned tools | P2.2 Drive-by server installs | Strong -- gateway is the sole MCP endpoint; new server registration is a measurable policy change | -| P2 Unsanctioned tools | P2.3 Tool name collisions | Strong -- catalog binds each tool name to a specific upstream server identity | -| P2 Unsanctioned tools | P2.4 Install-time scope too coarse | Strong -- per-workflow Cedar policy, attested | -| P3 Provable governance | P3.1 Regulatory proof requests | Strong (signature win) -- TRACE claim answers all three regulator questions directly | -| P3 Provable governance | P3.2 Customer questionnaires | Strong -- TRACE claim as portable proof artifact | -| P4 Supply chain | P4.1 Typosquatted packages | Partial -- gateway refuses non-catalog servers, but cannot detect a typosquat added to catalog. Phase 2 closes. | -| P4 Supply chain | P4.2 Rug-pull via tool-definition mutation | Strong -- catalog hash pinned; runtime drift produces measurement mismatch | +| P1 Data leakage | P1.1 Over-sharing context | Strong: gateway egress DLP, attested | +| P1 Data leakage | P1.2 Chatty tool responses | Strong: response inspection and classification-tag propagation, attested | +| P1 Data leakage | P1.3 Cross-system boundary violations | Strong: call graph tracked; cross-boundary policy attested | +| P1 Data leakage | P1.4 Transitive trust into upstream dependencies | Partial: gateway attests the immediate server; cannot reach into that server's own upstream calls. Phase 2 closes. | +| P1 Data leakage | P1.5 Session-context bleed | Strong: gateway-attested session-level egress policy with sensitivity propagation | +| P2 Unsanctioned tools | P2.1 Poisoned tool descriptions | Strong: tool-definition scanner inside attested gateway; catalog hash pinned | +| P2 Unsanctioned tools | P2.2 Drive-by server installs | Strong: gateway is the sole MCP endpoint; new server registration is a measurable policy change | +| P2 Unsanctioned tools | P2.3 Tool name collisions | Strong: catalog binds each tool name to a specific upstream server identity | +| P2 Unsanctioned tools | P2.4 Install-time scope too coarse | Strong: per-workflow Cedar policy, attested | +| P3 Provable governance | P3.1 Regulatory proof requests | Strong (signature win): TRACE claim answers all three regulator questions directly | +| P3 Provable governance | P3.2 Customer questionnaires | Strong: TRACE claim as portable proof artifact | +| P4 Supply chain | P4.1 Typosquatted packages | Partial: gateway refuses non-catalog servers, but cannot detect a typosquat added to catalog. Phase 2 closes. | +| P4 Supply chain | P4.2 Rug-pull via tool-definition mutation | Strong: catalog hash pinned; runtime drift produces measurement mismatch | --- @@ -296,11 +296,11 @@ In scope: Not in Phase 1: - MCP server-side attestation (Phase 2) -- stdio transport (requires bridge -- see issue) +- stdio transport (requires bridge; see issue) - Real-time policy updates without enclave restart - Multi-tenant TRACE Claim routing -- Tool identity verification for packages added to catalog (typosquat P4.1 residual -- Phase 2 closes) -- Transitive trust into upstream dependencies (P1.4 residual -- Phase 2 closes) +- Tool identity verification for packages added to catalog (typosquat P4.1 residual; Phase 2 closes) +- Transitive trust into upstream dependencies (P1.4 residual; Phase 2 closes) - cMCP registry --- @@ -313,10 +313,10 @@ Five attestable properties unique to Phase 2: | Property | What it makes provable | |----------|----------------------| -| Server runtime hardware-measured | The binary running now is the binary attested -- not just signed at some earlier moment. Compromised-maintainer reissue is detected. | +| Server runtime hardware-measured | The binary running now is the binary attested, not just signed at some earlier moment. Compromised-maintainer reissue is detected. | | Server tool surface measured at startup | Rug-pull caught at the source: server cannot expose a tool whose definition differs from the attested catalog. | | Server egress profile attested | Closes P1.4 transitive trust: customer verifies the server's own downstream calls are within declared scope. | | Multi-tenant isolation hardware-provable | SaaS providers demonstrate that tenant boundaries were enforced, not just configured. | -| Cross-organizational attestation chains | Party A verifies party B's server directly -- no shared operator, no SOC 2 in between. | +| Cross-organizational attestation chains | Party A verifies party B's server directly: no shared operator, no SOC 2 in between. | Not the current build focus. Revisit after Phase 1 GA and early production feedback. diff --git a/docs/spec/session-policy.md b/docs/spec/session-policy.md index 3979b1f..9722f73 100644 --- a/docs/spec/session-policy.md +++ b/docs/spec/session-policy.md @@ -4,9 +4,9 @@ Status: Draft v0.1 | Closes #36 | Related: [response-inspection.md](response-ins ## Overview -Individual call policy - Cedar evaluated before each tool call - is necessary but not sufficient. It answers "is this call permitted given what we know right now?" It cannot answer "has this session already seen PHI, and does that change what downstream calls are permitted?" That question requires session-level state that accumulates across calls. +Individual call policy, Cedar evaluated before each tool call, is necessary but not sufficient. It answers "is this call permitted given what we know right now?" It cannot answer "has this session already seen PHI, and does that change what downstream calls are permitted?" That question requires session-level state that accumulates across calls. -Phase 1.5 introduces session sensitivity tracking to close this gap. Once a session has handled data at a given sensitivity level, outbound calls to destinations that are not approved for that sensitivity level are denied - even if each individual call would pass pre-call Cedar policy in isolation. The session sensitivity state is monotonically increasing within a session: it can only go up, never down, short of an explicit operator-authorized reset. +Phase 1.5 introduces session sensitivity tracking to close this gap. Once a session has handled data at a given sensitivity level, outbound calls to destinations that are not approved for that sensitivity level are denied, even if each individual call would pass pre-call Cedar policy in isolation. The session sensitivity state is monotonically increasing within a session: it can only go up, never down, short of an explicit operator-authorized reset. ## Session Sensitivity State Machine @@ -22,7 +22,7 @@ public < pii < confidential < hipaa_phi = mnpi = trade_secret ### State Transitions -State is **monotonically increasing** within a session. It never decreases automatically. The only way to return to a lower state is an explicit operator-authorized session reset (see below). +State is monotonically increasing within a session. It never decreases automatically. The only way to return to a lower state is an explicit operator-authorized session reset (see below). The transition trigger is the `update_from_inspection` call made by the response inspection pipeline after every tool call. See [response-inspection.md](response-inspection.md) for when and how that call is made. @@ -56,13 +56,13 @@ def update_from_inspection( }) ``` -Note that `response_allowed` is recorded in injection events but does not affect state transition logic. A denied response that carried high-sensitivity tags still raises `max_sensitivity` - the agent is aware the call was attempted. +`response_allowed` is recorded in injection events but does not affect state transition logic. A denied response that carried high-sensitivity tags still raises `max_sensitivity`: the agent is aware the call was attempted. ## Cedar Egress Policy Using Session State Cedar evaluates pre-call policy with session state available as a context attribute. Three representative policies: -**Policy 1: Block PHI egress to uncovered external destinations** +Policy 1: Block PHI egress to uncovered external destinations ```cedar forbid( @@ -77,7 +77,7 @@ when { }; ``` -**Policy 2: Block MNPI egress to communication tools** +Policy 2: Block MNPI egress to communication tools ```cedar forbid( @@ -91,7 +91,7 @@ when { }; ``` -**Policy 3: Block any high-sensitivity session from writing to public channels** +Policy 3: Block any high-sensitivity session from writing to public channels ```cedar forbid( @@ -109,11 +109,11 @@ In Policy 3, `sensitivity_level_int` is a derived integer attribute on the sessi ## Session Reset Protocol -**Endpoint:** `POST /session/reset` +Endpoint: `POST /session/reset` -**Credential required:** Operator credential. Agent credentials are not accepted. This is intentional - see below. +Credential required: Operator credential. Agent credentials are not accepted. This is intentional, see below. -**Effect:** +Effect: - `max_sensitivity` is reset to `"public"` - A new `session_id` is generated; the old session ID is retired - An audit chain entry of type `"session_reset"` is written: @@ -129,7 +129,7 @@ In Policy 3, `sensitivity_level_int` is a derived integer attribute on the sessi } ``` -**When reset is not possible or not yet called:** If the runtime is in enforcement mode and `max_sensitivity` is `"hipaa_phi"`, `"mnpi"`, or `"trade_secret"`, outbound calls to non-covered or non-approved destinations are denied by Cedar egress policy (see above). The agent cannot unblock itself. There is no agent-callable override endpoint. +When reset is not possible or not yet called: If the runtime is in enforcement mode and `max_sensitivity` is `"hipaa_phi"`, `"mnpi"`, or `"trade_secret"`, outbound calls to non-covered or non-approved destinations are denied by Cedar egress policy (see above). The agent cannot unblock itself. There is no agent-callable override endpoint. This is intentional. The agent is an LLM: it is non-deterministic, and the runtime cannot trust agent assertions about what is or is not in its context window. Once a session has handled high-sensitivity data, the only entity that can attest "this session is now clean" is a human operator who has verified the agent's context. The reset endpoint is the mechanism for that attestation. @@ -137,15 +137,15 @@ If an operator reset is not available (e.g., the runtime is processing automated ## Session Lifetime and Attestation Validity -A session's maximum duration is bounded by the attestation validity period of the agent's TRACE token. When the TRACE token expires, the session must end - the gateway cannot continue to enforce session-level policy for an agent whose identity and configuration are no longer attested. +A session's maximum duration is bounded by the attestation validity period of the agent's TRACE token. When the TRACE token expires, the session must end: the gateway cannot continue to enforce session-level policy for an agent whose identity and configuration are no longer attested. In practice: session `max_duration_seconds` is set to `min(configured_session_max, trace_token_ttl_remaining)` at session creation. A session that is still active when its TRACE token would expire is terminated by the runtime with an audit entry of type `"session_expired"`. -This means a long-running agent that handles high-sensitivity data early in its session will face increasingly tight call restrictions as the session progresses - both because `max_sensitivity` is monotonically increasing and because the TRACE token TTL is monotonically decreasing. Deployments should size TRACE token lifetimes to match expected task durations. +This means a long-running agent that handles high-sensitivity data early in its session will face increasingly tight call restrictions as the session progresses, both because `max_sensitivity` is monotonically increasing and because the TRACE token TTL is monotonically decreasing. Deployments should size TRACE token lifetimes to match expected task durations. ## Agent Manifest Identity Binding -When `agent_manifest` is configured, session creation is bound to a signed Agent Manifest. The runtime loads the manifest and issuer trust anchor, delegates Agent Manifest signature and artifact verification to the `agent-manifest` SDK `verify_manifest()` API, and extracts: +When `agent_manifest` is configured, session creation is bound to a signed Agent Manifest. The runtime loads the manifest and issuer trust anchor, delegates Agent Manifest signature and artifact verification to the `agent-manifest` SDK `verify_manifest()` API, and extracts: - `manifest_id` - `agent_id`