Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/spec/error-codes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This is the normative registry for all error codes used across the cMCP Runtime.

| error_code | HTTP status | log level | description | spec reference |
|---|---|---|---|---|
| `MEASUREMENT_UNAVAILABLE` | 500 / startup | FATAL (production); WARN (dev mode) | Code/configuration/policy measurement or a TPM/NV operation failed; production startup aborts. `CMCP_DEV_MODE` warns and continues without established gateway measurement. Inspect diagnostic detail and recovery guidance; do not infer TPM replacement or rotate a signing key solely from this code | [failure-modes.md](failure-modes.md#measurement-unavailable-at-startup) |
| `ATTESTATION_REPORT_UNAVAILABLE` | 503 | FATAL | TEE provider did not return an attestation report within timeout | [failure-modes.md FM-1](failure-modes.md) |
| `ATTESTATION_PROVIDER_UNSUPPORTED` | 500 | FATAL | No supported TEE provider detected and `CMCP_DEV_MODE` is not set | [attestation.md §1.1](attestation.md) |
| `POLICY_HASH_MISMATCH` | 500 | FATAL | Measured policy bundle hash does not match deployment manifest | [failure-modes.md FM-4](failure-modes.md) |
Expand Down
30 changes: 28 additions & 2 deletions docs/spec/failure-modes.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
---
description: "Exact cMCP runtime behavior for each failure: attestation at startup and mid-session, TEE faults, policy hash mismatch, and MCP parse errors."
description: "cMCP failure behavior and recovery: gateway measurement and TPM/NV operations, production/dev startup, attestation, TEE faults, policy mismatch, and MCP parse errors."
---

# Failure Mode Specification

---
Status: Draft v0.1
Last updated: 2026-06-04
Last updated: 2026-11-01
Stability: Unstable , expect breaking changes before v1.0
---

Expand All @@ -26,6 +26,7 @@ Closes #22.
| Attestation staleness mid-session | Fail-closed. Active sessions terminated. | Yes (see FM-2) | `attestation_stale: true` on claims produced at or after deadline | `WARN attestation_stale` + session_id + expiry_utc |
| TEE fault mid-invocation | Fail-closed on in-flight call. Structured error returned to agent. Session continues if TEE recovers. | No | `status: fault` | `ERROR tee_fault` + call_id + timestamp |
| Policy bundle hash mismatch at startup | Fail-closed. Runtime does not start. | No | Not produced | `FATAL policy_hash_mismatch` + expected_hash + actual_hash |
| Gateway measurement or TPM/NV operation unavailable at startup | Production: fail-closed; runtime does not start. `CMCP_DEV_MODE`: warn and continue without established gateway measurement. | Dev mode only (see Measurement unavailable at startup) | No claim in production; dev continuation is not evidence of a successfully measured gateway | Production: `FATAL MEASUREMENT_UNAVAILABLE` + detail; dev mode: `WARN` measurement unavailable |
| Sealed signing key unavailable at startup | Fail-closed. Runtime does not start. Nothing is minted to recover. | No | Not produced | `FATAL SEALED_KEY_*` + detail (see FM-1b) |
| MCP protocol parse failure | Fail-closed on the specific call. Structured error returned. Session continues. | No | `status: parse_error` | `WARN parse_failure` + call_id + payload_hash |

Expand Down Expand Up @@ -298,3 +299,28 @@ For calls that are rejected or faulted (FM-3, FM-5), the TRACE Claim records the
"signature_note": "TEE fault prevented signing. This claim is for audit record only and must not be treated as an attested proof."
}
```

## Measurement unavailable at startup

`MEASUREMENT_UNAVAILABLE` covers failures deriving code/configuration/policy
measurements and failures completing the TPM-backed measurement path.

**Triggers**: unreadable or unsupported measured inputs, TPM open errors, NV index
define/read/validation errors, `TPM2_NV_Extend` failure, chain-value mismatch,
truncated NV values, and certify failures. Inspect the reported diagnostic detail
rather than assuming an input-file problem.

**Production**: startup aborts with `FATAL MEASUREMENT_UNAVAILABLE`; no traffic is
processed from this failed startup.

**Development**: with `CMCP_DEV_MODE`, the runtime logs a warning and continues.
This continuation is not evidence of a successfully measured gateway.

**Recovery**: restore readable, supported measurement inputs when they are the
reported cause. For a TPM/NV failure, investigate device access, library support,
NV index configuration and the reported chain or certify operation against the
intended deployment state before restarting. This code alone is not evidence
that the TPM changed. Do not bypass measurement or rotate a signing key merely
to suppress this error. Sealed-key recovery remains a separate decision (FM-1b).

This entry describes existing runtime behavior; it adds no new recovery action.
73 changes: 73 additions & 0 deletions tests/unit/test_measurement_error_registry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Keep startup measurement diagnostics and operator recovery discoverable."""

from pathlib import Path

from cmcp_runtime.tee.measurement import MeasurementUnavailable

ROOT = Path(__file__).resolve().parents[2]


def _row(text, first_column):
rows = [line for line in text.splitlines() if line.startswith(f"| {first_column} |")]
assert len(rows) == 1
columns = [column.strip() for column in rows[0].split("|")[1:-1]]
assert len(columns) == 5
return columns


def test_measurement_failure_registry_columns_match_runtime_and_modes():
registry = (ROOT / "docs/spec/error-codes.md").read_text(encoding="utf-8")
columns = _row(registry, f"`{MeasurementUnavailable.code}`")
assert columns[1] == f"{MeasurementUnavailable.http_status} / startup"
assert columns[2] == "FATAL (production); WARN (dev mode)"
for phrase in (
"TPM/NV",
"production startup aborts",
"`CMCP_DEV_MODE`",
"without established gateway measurement",
"do not infer TPM replacement",
"rotate a signing key solely from this code",
):
assert phrase in columns[3]
assert columns[4] == "[failure-modes.md](failure-modes.md#measurement-unavailable-at-startup)"


def test_measurement_failure_decision_table_covers_both_modes():
failure_modes = (ROOT / "docs/spec/failure-modes.md").read_text(encoding="utf-8")
assert "## Decision Table" in failure_modes
table = failure_modes.split("## Decision Table", 1)[1].split("\n## ", 1)[0]
columns = _row(table, "Gateway measurement or TPM/NV operation unavailable at startup")
assert "runtime does not start" in columns[1]
assert (
"`CMCP_DEV_MODE`: warn and continue without established gateway measurement" in columns[1]
)
assert columns[2] == "Dev mode only (see Measurement unavailable at startup)"
assert "not evidence of a successfully measured gateway" in columns[3]
assert "`FATAL MEASUREMENT_UNAVAILABLE`" in columns[4]
assert "dev mode: `WARN`" in columns[4]


def test_measurement_failure_recovery_guidance_is_retained():
failure_modes = (ROOT / "docs/spec/failure-modes.md").read_text(encoding="utf-8")
assert "## Measurement unavailable at startup" in failure_modes
section = failure_modes.split("## Measurement unavailable at startup", 1)[1].split("\n## ", 1)[
0
]
prose = " ".join(section.split())
for phrase in (
"TPM open errors",
"NV index define/read/validation errors",
"`TPM2_NV_Extend` failure",
"chain-value mismatch",
"truncated NV values",
"certify failures",
"**Production**: startup aborts",
"**Development**: with `CMCP_DEV_MODE`, the runtime logs a warning and continues.",
"not evidence of a successfully measured gateway",
"restore readable, supported measurement inputs",
"investigate device access, library support, NV index configuration",
"This code alone is not evidence that the TPM changed.",
"Do not bypass measurement or rotate a signing key merely to suppress this error.",
"Sealed-key recovery remains a separate decision (FM-1b).",
):
assert phrase in prose
Loading