cMCP
August 11, 2026 · View on GitHub
This file is the single source of truth for what ships today versus what is on the roadmap. Other docs (README, SPEC, quickstart) should link here rather than restate status, so the picture is stated once. Developer Preview: interfaces may change before v1.0.
Defaults (from cmcp_runtime.config)
| Setting | Default |
|---|---|
attestation.provider | auto (probe order azure-cvm -> tpm -> sev-snp -> tdx). Auto never degrades to software. With no hardware detected the gateway refuses to start unless CMCP_DEV_MODE=1, and provider: software-only requires that flag too, so a software-mode gateway is always a deployment that asked for one. |
attestation.enforcement_mode | enforcing |
attestation.staleness_policy | fail_closed |
attestation.validity_seconds | 86400 |
policy_reload_interval_seconds | 0 (disabled. Raising it requires a pinned CMCP_POLICY_SIGNING_KEY; see the hot-reload row below) |
attestation.allow_unmeasured_spawn | false (a stdio server the catalog does not pin is not spawned) |
attestation.required_provenance_kind | null (server provenance is recorded, not enforced) |
Capabilities
| Capability | Status | Notes |
|---|---|---|
| MCP interception + Cedar policy evaluation inside the TEE | Shipped | HTTP/SSE transport. |
stdio transport | Shipped | The gateway spawns the server as its own child inside the enclave, rather than bridging from outside, which is what the two rejected options in docs/spec/transport.md did. It measures the entrypoint against the catalog and refuses to spawn on a mismatch, so a stdio upstream carries a stronger binding than a network one: a TLS pin identifies an endpoint, a digest identifies code. The cost is real and stated in docs/spec/stdio-transport.md: the server then runs in the same isolation domain as the policy evaluator, and the gateway's launch measurement does not cover a child spawned later. Recorded as spawn-measured, or spawn-unmeasured when attestation.allow_unmeasured_spawn is on. |
Enforcement modes (enforcing / advisory / silent) | Shipped | Default is enforcing. |
| Hash-chained audit log, TEE-sealed signing key | Shipped | |
GatewayClaim (TRACE Claim) generation + signing | Shipped | Normative schema: schemas/trace-claim.schema.json. |
Offline verification (cmcp_verify) | Shipped | No operator trust required when the verifier independently checks the attestation report. |
| Agent Manifest identity binding | Shipped | Optional; trust in the issuer key is an out-of-band PKI concern. |
Attestation verifiers: sev-snp, tdx | Shipped | Verified end to end against genuine hardware evidence: an Azure CVM SEV-SNP report (VCEK chain to the AMD ARK-Milan root, ECDSA-P384 report signature, paravisor REPORT_DATA binding) and a GCP C3 Intel TDX DCAP v4 quote (PCK chain to the pinned Intel SGX Root CA, QE binding, quote signature). Runs are recorded in docs/testing/hardware-validation.md. This validates the verifier against real quotes; quote generation still requires the corresponding hardware, and TCB status stays in unverified_fields. |
Attestation verifier: tpm | Shipped in 0.4.0, with a host-dependent limit | 0.3.0 reported a forged TPM quote as hardware-attested and should not be used. The tpm2 branch of verify_trace_claim called only verify_tpm_measurement, which takes no signature parameter, so a TPMS_ATTEST with correct magic and matching qualifying_data passed with no signature and no chain (#370). verify_tpm_quote_chained existed and was hardware-validated on 2026-07-31 (an AK-signed quote from an Azure Trusted Launch vTPM verified end to end, tampered copies rejected, see docs/testing/hardware-validation.md); nothing in production called it. Fixed in #469: the quote signature and the AK certificate chain now gate hardware_attestation, supplied-but-invalid material is fatal, and absent material degrades to unverified as SNP does. Signed evidence travels as gateway.attestation_evidence, which is why 0.4.0 is a break for older verifiers. The remaining limit is the host, not the code (#453): Azure Trusted Launch presents two AK certificate hierarchies concurrently at NV index 0x01C101D0, and on the Global Virtual TPM CA - 03 variant the AIA extension is absent entirely, so there is nothing to walk and no chain to a pinnable root. On such a host the chain cannot be established and the claim reports unverified rather than verified. Pin the root your own hosts present; a mixed fleet needs both. |
opaque provider | Not implemented | Opt-in placeholder; excluded from auto-detect. Selecting it explicitly raises ATTESTATION_PROVIDER_NOT_IMPLEMENTED rather than falling through silently. |
gpu-cc (NVIDIA H100/H200/Blackwell, via NRAS) | Planned (v0.2) | |
| Transparency-log anchoring for TRACE Claims | v0.2 | Write and lookup. |
| Server-side (provider) attestation | Not yet (Phase 2) | Phase 1 attests the gateway boundary only. |
| Server provenance checking | Shipped | Consumes server-provenance-v1 records: verifies the signature against a configured publisher key, then compares the record's tool-catalog hash against the tools the server advertises to this gateway. Five outcomes reach the audit chain and none of them is silent: verified, catalog-mismatch (the document is fine and the server is not), invalid, unchecked (verified but the tool list was unavailable, so the comparison that matters never ran), absent. Absence is recorded and non-fatal by default, because almost no MCP server has a record and a gateway that refuses to route without one gets disabled on first contact. Set attestation.required_provenance_kind for a floor. |
| Real-time policy update without enclave restart | Shipped, with signed bundles; still off by default | Set CMCP_POLICY_SIGNING_KEY to the Ed25519 public key allowed to sign policy, and policy_reload_interval_seconds above 0. A bundle whose manifest.signature verifies over the bundle hash and whose version increased is installed without a restart. The version rule is not optional: without it a genuinely signed older bundle can be replayed, which is a downgrade attack, so equal-or-lower versions are refused. A pinned CMCP_POLICY_HASH still cannot authorise a changed bundle — a hash pins one artifact — so an interval alongside only a hash aborts startup (POLICY_RELOAD_PINNED_HASH). Signing is opt-in: unsigned bundles keep working where no key is pinned, and every bundle hash issued before signing existed is unchanged. Key revocation is not implemented: a compromised signing key is replaced by config plus a restart, so this buys faster policy change, not faster key change. See policy-hot-reload.md. |
| AARM R4 five decision types | Shipped, with caveats | ALLOW, DENY, MODIFY, STEP_UP, DEFER are recorded in the audit chain. MODIFY is recorded as redact, DEFER is classified but not asynchronously enforced, and the TRACE Claim still carries the pre-AARM vocabulary. See LIMITATIONS.md. |
| AARM R8 telemetry export | Shipped | OpenTelemetry spans mirroring audit entries. Opt in with CMCP_OTEL_ENABLED=1 and pip install cmcp-runtime[otel]; a no-op otherwise. Exports digests, never payloads. The audit chain stays authoritative. |
| AARM R2 declared intent | Shipped | The gateway reads the issuer-signed intent object from a bound Agent Manifest (agent-manifest spec 3.9, SDK >=0.11) and carries its digest into the TRACE claim's identity block as intent_hash. The issuer declares it, not the caller and not the agent: an intent the governed agent asserts about itself makes the check a formality, so a per-call _cmcp field was rejected for this and the spec forbids accepting one. The digest travels, never the statement, because the claim is built to be shared and an intent statement is business context. Absence is not a failure: a manifest declaring no intent binds and serves exactly as before. |
| AARM R3 intent alignment | Not implemented | R3 wants the action evaluated against its alignment with stated intent. The input now exists (R2 above) and the action half is solid via Cedar, but nothing compares the two. Doing so means comparing meaning, which is R7's problem below. Recorded as a deviation rather than claimed. |
| AARM R7 semantic distance from intent | Not implemented, and deliberately not approximated | Catalog rug-pull detection and injection detection are present; neither measures distance from a stated intent, and they are easy to mistake for it. Measuring semantic distance needs a model, and the trust in that comparison then rests on which model ran and whether its execution was attested. A structural proxy (scope overlap, tool-category distance) is not a semantic measure and is not labelled as one. Left unclaimed. |
| AARM R6 identity binding | Shipped under a named profile | Passes whenever an Agent Manifest is bound, and R6 says every receipt MUST be bound while the developer default leaves binding optional. Set conformance_profile: aarm and the gateway refuses to start without a manifest binding (CONFORMANCE_PROFILE_UNSATISFIED). Naming the profile is what lets the strict claim and the five-minute default both be true; an unrecognised profile name is a config error rather than silently enforcing nothing. |
| AARM R9 least privilege | Partial | Resource-scoped Cedar policy, credential and PII redaction, and ephemeral gateway keys are present. Absent: issuing short-lived minimal-scope credentials to the agent, so enforcement is deny-based rather than scope-based. Recorded as partial rather than claimed. |
| Full RATS/EAT conformance | v1.0 target | Claims are EAT-shaped today; full conformance is tracked for v1.0. |
See ROADMAP.md for version sequencing,
docs/testing/hardware-validation.md for what has been
verified against real TEE hardware, and LIMITATIONS.md for what cMCP does not
prevent.