Correctover Conformance Shape (CCS): An Evidence Protocol Specification for Agent Runtime Verification
draft-correctover-ccs-04
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
The information below is for an old version of the document.
| Document | Type |
This is an older version of an Internet-Draft whose latest revision state is "Active".
|
|
|---|---|---|---|
| Author | Correctover | ||
| Last updated | 2026-08-17 (Latest revision 2026-08-13) | ||
| RFC stream | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-correctover-ccs-04
Internet Engineering Task Force G. Wang
Internet-Draft Correctover
Intended status: Informational 14 August 2026
Expires: 14 February 2027
Correctover Conformance Shape (CCS): An Evidence Protocol Specification
for Agent Runtime Verification
draft-correctover-ccs-04
Abstract
The Correctover Conformance Shape (CCS) defines the evidence protocol for
the agent runtime protocol stack. It specifies the complete evidence
lifecycle: intake, generation, standardization, propagation, and
enforcement consumption. CCS occupies the position between Transport
(MCP/A2A) and Enforcement (AEB), responsible exclusively for evidence
generation, standardization, cross-agent propagation, and delivery to
enforcement consumers.
CCS produces a tamper-evident, cryptographically bound artifact -- a
CCS Receipt -- for every tool invocation decision made by an agent
runtime's governance layer. The receipt binds a verdict to the exact
invocation context, configuration, runtime environment, and
cryptographic chain, providing verifiable evidence that downstream
enforcement systems (including an Action Evidence Boundary (AEB))
consume as native input.
This document specifies the Evidence Intake Protocol, the CCS Receipt
Schema, Canonical Configuration, binding mechanisms, key management,
transport requirements, verifier source classification, conformance
levels (Levels 0 through 4), Evidence Propagation across multi-agent
chains and organizational boundaries, the Enforcement Interface for
standardized evidence consumption, negative test cases, and security
considerations.
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 14 February 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents
(https://trustee.ietf.org/license-info) in effect on the date of
publication of this document. Please review these documents
carefully, as they describe your rights and restrictions with
respect to this document. Code Components extracted from this
document must include Revised BSD License text as described in
Section 4.e of the Trust Legal Provisions and are provided
without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3
1.1. Protocol Stack Position . . . . . . . . . . . . . . . . . 3
1.2. Scope . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3. Changes from draft-02 . . . . . . . . . . . . . . . . . . 5
1.4. Changes from draft-03 . . . . . . . . . . . . . . . . . . 5
1.5. Relationship to CCS Formal Framework . . . . . . . . . . 6
2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 7
3. Evidence Intake Protocol . . . . . . . . . . . . . . . . . . 9
3.1. Intake Interface . . . . . . . . . . . . . . . . . . . . 9
3.2. Request Normalization . . . . . . . . . . . . . . . . . . 10
3.3. Intake Fail-Closed Semantics . . . . . . . . . . . . . . 11
3.4. Intake Error Responses . . . . . . . . . . . . . . . . . 12
4. CCS Receipt Schema . . . . . . . . . . . . . . . . . . . . . 12
4.1. Field Definitions . . . . . . . . . . . . . . . . . . . . 12
4.2. Signed Bytes . . . . . . . . . . . . . . . . . . . . . . 15
4.3. Receipt JSON Example . . . . . . . . . . . . . . . . . . 16
4.4. Verifier Policy Floor . . . . . . . . . . . . . . . . . . 17
4.5. Verification Outcome States . . . . . . . . . . . . . . . 18
4.5.1. Scope-Comparison Axis . . . . . . . . . . . . . . . . 18
4.5.2. Evidence Completeness (Third Axis) . . . . . . . . . 19
4.6. Verifier Capability Manifest . . . . . . . . . . . . . . 20
4.7. Challenge Loop . . . . . . . . . . . . . . . . . . . . . 22
5. Canonical Configuration . . . . . . . . . . . . . . . . . . . 24
5.1. Canonical Config Fields . . . . . . . . . . . . . . . . . 24
5.2. Canonicalization Algorithm . . . . . . . . . . . . . . . 25
5.3. Config Hash . . . . . . . . . . . . . . . . . . . . . . . 25
6. Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
6.1. request_hash Binding . . . . . . . . . . . . . . . . . . 25
6.2. response_hash Binding . . . . . . . . . . . . . . . . . . 26
6.3. runtime_context_hash Binding . . . . . . . . . . . . . . 26
6.4. action Binding . . . . . . . . . . . . . . . . . . . . . 27
6.5. params_hash Binding . . . . . . . . . . . . . . . . . . . 27
6.6. issuer Binding . . . . . . . . . . . . . . . . . . . . . 28
6.7. audience Binding . . . . . . . . . . . . . . . . . . . . 28
6.8. nonce/sequence Binding . . . . . . . . . . . . . . . . . 28
6.9. freshness Binding . . . . . . . . . . . . . . . . . . . . 29
6.10. Receipt Time-Binding . . . . . . . . . . . . . . . . . . 29
7. Key Management . . . . . . . . . . . . . . . . . . . . . . . 30
7.1. Key Generation . . . . . . . . . . . . . . . . . . . . . 30
7.2. Key Enrollment . . . . . . . . . . . . . . . . . . . . . 30
7.3. Key Rotation . . . . . . . . . . . . . . . . . . . . . . 31
7.4. Verifier-Operator Separation . . . . . . . . . . . . . . 31
8. Transport Requirements . . . . . . . . . . . . . . . . . . . 32
8.1. Fail-Closed Semantics . . . . . . . . . . . . . . . . . . 32
8.2. Timeout Behavior . . . . . . . . . . . . . . . . . . . . 32
8.3. In-Process vs Out-of-Process Deployment . . . . . . . . . 33
9. Verifier Source Classification . . . . . . . . . . . . . . . 33
9.1. Class A: Self-Built Verifier . . . . . . . . . . . . . . 33
9.2. Class B: Audited Verifier . . . . . . . . . . . . . . . . 34
9.3. Class C: Self-Declared Verifier . . . . . . . . . . . . . 34
10. Conformance Levels . . . . . . . . . . . . . . . . . . . . . 34
10.1. Level 0-3 Definitions . . . . . . . . . . . . . . . . . . 34
10.2. Level 4: Evidence Propagation . . . . . . . . . . . . . . 36
10.3. Conformance Vectors . . . . . . . . . . . . . . . . . . . 36
11. Evidence Propagation . . . . . . . . . . . . . . . . . . . . 38
11.1. Single-Agent Propagation . . . . . . . . . . . . . . . . 38
11.2. Multi-Agent Chain Propagation . . . . . . . . . . . . . . 39
11.3. Cross-Organization Propagation . . . . . . . . . . . . . 41
11.4. Evidence Accumulation . . . . . . . . . . . . . . . . . . 42
11.5. Chain Integrity Verification . . . . . . . . . . . . . . 43
12. Enforcement Interface . . . . . . . . . . . . . . . . . . . . 44
12.1. Enforcement Input Contract . . . . . . . . . . . . . . . 44
12.2. Receipt Validation Protocol . . . . . . . . . . . . . . . 45
12.3. Verification Failure Signaling . . . . . . . . . . . . . 46
12.4. Outcome Status Mapping . . . . . . . . . . . . . . . . . 47
13. Negative Test Cases . . . . . . . . . . . . . . . . . . . . . 47
13.1. Changed Arguments . . . . . . . . . . . . . . . . . . . . 48
13.2. Changed Response . . . . . . . . . . . . . . . . . . . . 48
13.3. Replay . . . . . . . . . . . . . . . . . . . . . . . . . 48
13.4. Stale Evidence . . . . . . . . . . . . . . . . . . . . . 49
13.5. Wrong Audience . . . . . . . . . . . . . . . . . . . . . 49
13.6. Key Substitution . . . . . . . . . . . . . . . . . . . . 49
13.7. Verifier/Runtime Collusion . . . . . . . . . . . . . . . 50
13.8. Missing Transport . . . . . . . . . . . . . . . . . . . . 50
13.9. Intake Fail-Closed Bypass . . . . . . . . . . . . . . . . 50
13.10. Chain Tampering . . . . . . . . . . . . . . . . . . . . 51
13.11. Cross-Org Missing Self-Verification . . . . . . . . . . 52
14. Security Considerations . . . . . . . . . . . . . . . . . . . 52
15. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 55
16. References . . . . . . . . . . . . . . . . . . . . . . . . . 55
16.1. Normative References . . . . . . . . . . . . . . . . . . 55
16.2. Informative References . . . . . . . . . . . . . . . . . 56
Appendix A. CCS v1.3 Receipt Field Summary Table . . . . . . . . 56
Appendix B. Conformance Test Vector Format . . . . . . . . . . . 58
Author's Address . . . . . . . . . . . . . . . . . . . . . . . . 59
1. Introduction
The Correctover Conformance Shape (CCS) defines the evidence protocol for
the agent runtime protocol stack. It produces a single, tamper-
evident, cryptographically bound artifact -- a CCS Receipt -- for
every tool invocation decision made by an agent runtime's governance
layer. A CCS receipt answers: "Given this exact request, this exact
configuration, this exact runtime context, and this exact set of
rules, the verifier decided ALLOW/DENY/ESCALATE, and here is the
proof."
CCS is not a policy language, an authorization framework, or a
transport protocol. It is the evidence that a verifier produces when
it finishes evaluating a command, together with the protocols for how
that evidence enters the system, propagates across boundaries, and is
delivered to enforcement consumers.
The initial reference implementation (ccs-verifier 0.4.1) provides an
in-process verifier with HMAC-SHA256 receipt signing covering the
verification context (trace_id, verdict, timestamp, tool,
params_hash, rule_summary). This document specifies extensions to
that baseline to cover the full set of bindings that
[I-D.schrock-action-evidence-boundary] Section 5.2 requires from a
native artifact: request binding, response binding, runtime context
binding, issuer/audience, freshness, nonce, and canonical
configuration. It further defines the Evidence Intake Protocol,
Evidence Propagation mechanisms, and the Enforcement Interface.
This document is an individual submission to the IETF, intended to
initiate discussion within the community. The proposals herein are
offered for review and refinement through the standard IETF process.
The author welcomes feedback on all aspects of this specification,
including the protocol architecture, evidence chain structure, and
integration points with existing and emerging agent protocols.
1.1. Protocol Stack Position
CCS occupies a defined position in the agent runtime protocol stack:
+================================================================+
| Layer | Responsibility | Specification |
+========================+======================+=================+
| Transport | Message routing and | MCP, A2A |
| (MCP/A2A) | serialization between| |
| | agents and tools | |
+------------------------+----------------------+-----------------+
| Evidence Protocol (CCS) | Evidence generation, | This document |
| | standardization, | |
| | propagation, delivery| |
+------------------------+----------------------+-----------------+
| Enforcement | Policy decision | AEB |
| (AEB) | consumption and | [I-D.schrock- |
| | action gating | action-evidence-|
| | | boundary] |
+------------------------+----------------------+-----------------+
| Tool Execution | Actual tool | Tool-specific |
| | invocation and | |
| | outcome reporting | |
+========================+======================+=================+
Figure 1
CCS is responsible for:
* Evidence generation: creating tamper-evident receipts from
verification decisions.
* Evidence standardization: defining schemas, bindings, and
canonicalization rules.
* Evidence propagation: ensuring evidence integrity across multi-
agent chains and organizational boundaries.
* Evidence delivery: providing standardized interfaces for
enforcement consumers to validate and consume evidence.
CCS is NOT responsible for:
* Transport message routing (MCP/A2A layer).
* Policy decisions or enforcement logic (AEB layer).
* Identity handshakes or authentication (identity layer).
1.2. Scope
CCS specifies:
* An Evidence Intake Protocol (Section 3) that governs how evidence
legally enters the CCS system.
* A receipt schema (Section 4) that binds a verdict to the exact
invocation context.
* A canonical configuration model (Section 5) that binds the receipt
to the exact rule set and policy under which verification
occurred.
* Nine binding mechanisms (Section 6) that connect the receipt to
the request bytes, response bytes, runtime context, action
identity, parameter identity, issuer, audience, nonce, and
freshness window.
* Key management requirements (Section 7).
* Transport requirements (Section 8) including fail-closed
semantics.
* Verifier source classification (Section 9).
* Conformance levels and test vectors (Section 10), including
Level 4 (Evidence Propagation).
* Evidence Propagation across multi-agent chains and
organizational boundaries (Section 11).
* The Enforcement Interface for standardized evidence consumption
(Section 12).
* Negative test cases (Section 13), extended for propagation and
enforcement scenarios.
CCS does NOT define:
* A new authorization receipt format that replaces or competes with
[I-D.schrock-ep-authorization-receipts]. CCS is a verifier-side
evidence record, not an organizational authorization artifact.
* A policy language. Rules are pluggable; CCS specifies how rule
outcomes are recorded and bound, not what the rules are.
* A transport protocol for inter-process communication.
* A universal evidence taxonomy. CCS defines one artifact shape for
one class of decision (tool-invocation governance).
* Enforcement policy decisions. CCS delivers evidence; it does not
decide what to do with it.
1.3. Changes from draft-02
This document (draft-03) introduces the following changes from
draft-02:
* Title changed from "A Receipt and Binding Specification" to "An
Evidence Protocol Specification" to reflect the protocol stack
positioning.
* Terminology refinement: "Accumulated Evidence Chain (AEC)"
renamed to "Evidence Chain (EC)" to avoid abbreviation collision
with concurrent IETF work. "Evidence Layer" references
softened to "evidence protocol" throughout.
* New Section 3: Evidence Intake Protocol.
* New Section 11: Evidence Propagation.
* New Section 12: Enforcement Interface.
* New conformance Level 4: Evidence Propagation (Section 10.2).
* New negative test cases (Section 13.9-13.11).
* Extended Security Considerations (Section 14).
* All existing sections renumbered accordingly.
1.4. Changes from draft-03
This draft (draft-04) introduces the following changes from
draft-03:
This draft adds three mechanisms to address IETF discussion feedback:
1. UNDECIDED with reason codes on the scope-comparison axis (Section
4.5.1). UNDECIDED is a per-comparison result, not a new top-
level verdict.
2. Verifier Capability Manifest (Section 4.6). A signed, versioned
declaration of which profiles a verifier can evaluate, used for
routing when UNDECIDED(PROFILE_NOT_HELD) is returned.
3. Challenge Loop for Evidence Completeness (Section 4.7). The
third axis is implemented as fail-closed refusal + structured
challenge, not as a new verdict token. Deadline uses derivation
(multiplier x expected_response_duration), following OSPF/BGP
convention.
1.5. Relationship to CCS Formal Framework (DOI References)
This Internet-Draft is a protocol specification derived from and
complementary to the following academic publications:
* *DOI: 10.5281/zenodo.21271910* -- "CCS Formal Framework." This
paper defines the mathematical foundation: the Required(T) subset
of Supported(T) conformance criterion, the transition model, and
the formal proof that fail-closed enforcement satisfies the
governance criterion. This I-D does not reproduce the formal
framework; it specifies the wire-level receipt and binding
mechanisms that implement it.
* *DOI: 10.5281/zenodo.21603250* -- "Action Receipts." This paper
defines the concept of an action receipt as a tamper-evident
record binding a decision to a specific action invocation. The
CCS receipt in this I-D is a concrete instantiation of that
concept, specialized for tool-invocation governance in agent
runtimes.
* *DOI: 10.5281/zenodo.21234580* -- "CCS Standard v1.0." The
overarching standard that defines the three pillars (Conformance,
model (Structure, Schema, Latency, Cost, Identity, Integrity,
Security)).
* *DOI: 10.5281/zenodo.21783723* -- "CCS 7-Dimensional Framework
Three-Endpoint Closed Loop." Contains: IETF Internet-Draft +
Zenodo DOI + PyPI ccs-verifier v1.1.0. Seven-dimensional
verification model. This record consolidates the protocol
specification, formal framework, and reference implementation
into a single verifiable package.
2. Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in BCP
14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
*CCS-specific terms:*
*Command*: An immutable, structured representation of a tool
invocation request, comprising at minimum: agent_id, tool name,
parameters, timestamp, and trace_id. The Command is the input to
verification.
*Verifier*: A component that evaluates a Command against a set of
Rules and produces a VerificationResult. The verifier MAY run in-
process (same address space as the agent) or out-of-process (separate
address space, separate crash domain).
*Rule*: A pluggable unit of policy evaluation. A Rule accepts a
Command and returns a RuleResult with a verdict (allow, deny,
escalate), an optional reason string, and a latency measurement.
*VerificationResult*: The output of a verification. Contains the
verdict, signed receipt, rule results, and metadata.
*Receipt*: A truncated HMAC-SHA256 (or Ed25519, per Section 7) digest
that binds the verification context to a shared secret or signing
key, providing tamper-evident audit.
*Config Hash*: A SHA-256 digest of the canonical JSON representation
of the verifier's configuration (rule set, policy version, signing
key identifier). Binds the receipt to the exact policy under which
verification occurred.
*Fail-closed*: A property of the enforcement mechanism whereby any
failure in the verification path -- exception, timeout, missing input,
ambiguous state -- results in the tool invocation being blocked, not
allowed. This is the opposite of fail-open (CWE-636).
*Conformance Shape*: The complete set of fields, bindings, and
semantic constraints that a CCS receipt MUST satisfy to be considered
conformant at a given level.
*Outcome status*: The epistemic state of a verification result
regarding the action's outcome. One of: confirmed, dispatched,
indeterminate. Orthogonal to the verdict (allow/deny/escalate).
*Verifier policy floor*: The minimum security requirements enforced
by a CCS verifier, regardless of the server's self-declared profile.
Non-negotiable from the server side.
*Reconcilability*: The property of an action whereby the effecting
side can attest to what actually occurred after execution.
Independent from severity.
*Evidence Intake*: The process by which an agent runtime submits tool
invocation data to the CCS layer for evidence generation. The intake
is the controlled entry point for all evidence entering the CCS
system.
*Evidence Envelope*: A wrapper structure that carries a CCS receipt
plus chain metadata for propagation across agent boundaries. The
envelope preserves the original receipt and adds propagation context.
*Evidence Chain (EC)*: An ordered list of CCS receipts
linked by chain integrity hashes. Each receipt in the chain
cryptographically references its predecessor.
*Chain Integrity*: The property that each receipt in a chain
cryptographically references its predecessor via a receipt_hash
linkage, preventing insertion, removal, or modification of chain
members.
*Enforcement Consumer*: A system that validates CCS evidence and maps
it to enforcement decisions. The AEB is a canonical enforcement
consumer.
*Intake Fail-Closed*: The property that any failure in evidence
intake results in no receipt being produced and a denial of the tool
invocation. The system MUST NOT fall back to fail-open behavior.
*Cross-Organization Evidence*: Evidence that propagates across
organizational boundaries, requiring self-contained verification data
so that the receiving organization can validate the evidence without
external lookups.
*Summary Hash*: A single cryptographic hash that represents the
entire Evidence Chain, computed over all receipt hashes
in insertion order.
*Contributors:* The distinction between evidence sufficiency and
executor lifecycle was developed through discussion with Iman Schrock
(AEB-03) and Blake Morrison (CAE 3.3/3.4, executor lifecycle, B3-y).
3. Evidence Intake Protocol
This section defines how evidence legally enters the CCS system. The
Evidence Intake Protocol establishes "entry sovereignty" -- the right
of the CCS evidence protocol to control what constitutes valid input. All
evidence in the CCS system originates through this protocol; there is
no other path.
3.1. Intake Interface
The intake interface is the standard interface through which agent
runtimes submit tool invocation data for evidence generation.
*Input contract:*
The Command object submitted to the intake interface MUST contain the
following fields:
{
"intake_version": "1.3",
"agent_id": "agent-001",
"tool": "search_web",
"params": {"query": "weather in Tokyo"},
"timestamp": 1722384000.0,
"trace_id": "a1b2c3d4e5f67890"
}
Required fields:
* intake_version: The CCS schema version. MUST be "1.3" for this
specification.
* agent_id: Identifier of the invoking agent.
* tool: Name of the tool being invoked.
* params: Structured parameters for the tool invocation.
* timestamp: Unix timestamp of the invocation request.
* trace_id: Unique identifier for this verification event.
Optional fields:
* runtime_context: JSON object containing caller_identity, tenant,
environment, and other execution environment fields.
* delegation_chain: If this invocation is part of a multi-agent
delegation, the parent receipt information.
*Synchronicity requirements:*
* For in-process deployment, intake MUST be synchronous: the caller
blocks until the intake completes or fails.
* For out-of-process deployment, intake MAY be asynchronous, but
the caller MUST block until a receipt or error response is
received before proceeding with the tool invocation.
*Versioning:*
The intake_version field ensures that the verifier and the caller
agree on the expected field set and normalization rules. If the
verifier receives an intake_version it does not support, it MUST
return an INTAKE_MALFORMED error (Section 3.4).
3.2. Request Normalization
Before evidence generation, the intake interface normalizes the
incoming request to ensure deterministic processing.
*JSON canonicalization:* The params field MUST be serialized using
JSON Canonicalization Scheme (JCS) as defined in [RFC8785]:
def canonical_params(params: dict) -> bytes:
return json.dumps(
params,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=True,
allow_nan=False
).encode("utf-8")
*Unicode normalization:* All string fields in the Command MUST be
normalized to Unicode Normalization Form C (NFC) before processing.
This prevents visually identical but byte-distinct inputs from
producing different evidence.
*Timestamp normalization:* All timestamps MUST be converted to UTC
before processing. Implementations MUST reject timestamps with
timezone offsets other than UTC.
*Binary data handling:* Any binary data in the params MUST be
encoded as base64 (standard encoding, with padding) before
canonicalization. The params_hash is computed over the canonical
bytes including the base64-encoded binary data.
*Maximum input size:*
The intake interface MUST enforce the following size limits:
+========================+==========+============================+
| Component | Max Size | Behavior on Overflow |
+========================+==========+============================+
| params (canonical | 64 KB | INTAKE_OVERFLOW error |
| bytes) | | |
+------------------------+----------+----------------------------+
| tool name | 256 bytes| INTAKE_OVERFLOW error |
+------------------------+----------+----------------------------+
| agent_id | 512 bytes| INTAKE_OVERFLOW error |
+------------------------+----------+----------------------------+
| runtime_context | 16 KB | INTAKE_OVERFLOW error |
+------------------------+----------+----------------------------+
| total intake message | 128 KB | INTAKE_OVERFLOW error |
+========================+==========+============================+
Figure 2
3.3. Intake Fail-Closed Semantics
The intake protocol enforces fail-closed semantics at the entry
point. If intake fails for ANY reason, the system MUST NOT produce
a receipt and the caller MUST treat this as a denial.
*Failure conditions:*
* Malformed input: The intake message is not valid JSON, or
required fields are missing, or field types are incorrect.
* Normalization failure: The params contain values that cannot be
canonicalized (e.g., NaN, Infinity, circular references).
* Size overflow: Any component exceeds its maximum size limit.
* Timeout: The intake processing does not complete within the
configured timeout.
* Version mismatch: The intake_version is not supported.
*Invariant:* Under no circumstances MAY the system fall back to
fail-open behavior. If the verifier cannot process the intake, the
tool invocation MUST be blocked. This is the fundamental security
guarantee of the CCS evidence protocol: no evidence, no execution.
The fail-closed invariant is enforced at the intake boundary:
def intake(command: Command) -> Union[Receipt, IntakeError]:
try:
normalized = normalize(command)
if not validate(normalized):
return IntakeError("INTAKE_MALFORMED")
receipt = generate_receipt(normalized)
return receipt
except Exception:
return IntakeError("INTAKE_FAILED")
Any exception in the intake path produces an IntakeError, which the
caller MUST treat as a denial.
3.4. Intake Error Responses
When intake fails, the verifier returns a structured error response.
*Error response format:*
{
"error_code": "INTAKE_MALFORMED",
"error_message": "Required field 'tool' is missing",
"retry_after": null,
"trace_id": "a1b2c3d4e5f67890"
}
*Error taxonomy:*
+=======================+======+===================================+
| Error Code |Retry | Description |
+=======================+======+===================================+
| INTAKE_MALFORMED | MAY | Input is not valid JSON, or field |
| | | types are incorrect. |
+-----------------------+------+-----------------------------------+
| INTAKE_MISSING_FIELD | MAY | A required field is absent. |
+-----------------------+------+-----------------------------------+
| INTAKE_OVERFLOW | MUST | A component exceeds its size |
| | NOT | limit. |
+-----------------------+------+-----------------------------------+
| INTAKE_TIMEOUT | MAY | Processing exceeded the timeout. |
+-----------------------+------+-----------------------------------+
| INTAKE_VERSION_UN- | MUST | The intake_version is not |
| SUPPORTED | NOT | supported. |
+=======================+======+===================================+
Figure 3
*Security requirements for error responses:*
* Error responses MUST NOT leak internal implementation details
(stack traces, internal paths, key identifiers).
* Error responses MUST be structurally distinguishable from valid
receipts. An error response MUST NOT contain a receipt field,
and a receipt MUST NOT contain an error_code field. This
prevents error-response-as-receipt attacks.
* The trace_id in the error response (if available) allows
correlation with the original intake attempt.
4. CCS Receipt Schema
4.1. Field Definitions
A conforming CCS Receipt is a JSON object conforming to I-JSON
[RFC7493]. The following table defines each field. All 22 fields
are implemented in ccs-verifier 0.4.1.
+====================+========+===========+===========+=================+
|Field |Type |Status |Required |Description |
+====================+========+===========+===========+=================+
|trace_id |string |Implemented|MUST |SHA-256 of |
| |(16-char| | |time.time_ns(), |
| |hex) | | |truncated to 16 |
| | | | |hex chars. |
| | | | |Uniquely |
| | | | |identifies this |
| | | | |verification |
| | | | |event. |
+--------------------+--------+-----------+-----------+-----------------+
|verdict |string |Implemented|MUST |One of: "allow", |
| |enum | | |"deny", |
| | | | |"escalate". |
+--------------------+--------+-----------+-----------+-----------------+
|timestamp |float |Implemented|MUST |Unix timestamp |
| | | | |at which the |
| | | | |verdict was |
| | | | |finalized. |
+--------------------+--------+-----------+-----------+-----------------+
|tool |string |Implemented|MUST |The tool name |
| | | | |from the Command.|
+--------------------+--------+-----------+-----------+-----------------+
|params_hash |string |Implemented|MUST |SHA-256 of |
| |(16-char| | |canonical JSON of|
| |hex) | | |Command.params, |
| | | | |truncated to 16 |
| | | | |hex chars. |
+--------------------+--------+-----------+-----------+-----------------+
|rule_summary |string |Implemented|MUST |Pipe-delimited |
| | | | |list of |
| | | | |rule_name=verdict|
| | | | |pairs. |
+--------------------+--------+-----------+-----------+-----------------+
|receipt |string |Implemented|MUST |HMAC-SHA256 over |
| |(32-char| | |signed bytes |
| |hex) | | |(Section 4.2), |
| | | | |truncated to 32 |
| | | | |hex chars. |
+--------------------+--------+-----------+-----------+-----------------+
|verified_at |float |Implemented|MUST |Unix timestamp of|
| | | | |verification. |
+--------------------+--------+-----------+-----------+-----------------+
|block_reason |string |Implemented|Conditional|Human-readable |
| | | | |reason for deny/ |
| | | | |escalate. MUST |
| | | | |be empty for |
| | | | |allow. |
+--------------------+--------+-----------+-----------+-----------------+
|request_hash |string |Implemented|MUST |SHA-256 of the |
| |(hex) | | |canonical bytes |
| | | | |of the original |
| | | | |request (Section 6.1). ||
| | | | | | |
+--------------------+--------+-----------+-----------+-----------------+
|response_hash |string |Implemented|SHOULD |SHA-256 of the |
| |(hex) | | |tool response |
| | | | |bytes (Section |
| | | | |6.2). |
+--------------------+--------+-----------+-----------+-----------------+
|runtime_context_hash|string |Implemented|SHOULD |SHA-256 of |
| |(hex) | | |canonical JSON of|
| | | | |runtime context |
| | | | |(Section 6.3). |
+--------------------+--------+-----------+-----------+-----------------+
|action |string |Implemented|MUST |CAID-compatible |
| | | | |action ident. |
| | | | |14 enum values |
| | | | |(Section 6.4). |
+--------------------+--------+-----------+-----------+-----------------+
|config_hash |string |Implemented|MUST |SHA-256 of the |
| |(hex) | | |canonical config |
| | | | |(Section 5). |
+--------------------+--------+-----------+-----------+-----------------+
|issuer |string |Implemented|MUST |Verifier URI |
| |(URI) | | |(Section 6.6). |
+--------------------+--------+-----------+-----------+-----------------+
|audience |string |Implemented|MUST |Intended consumer|
| |(URI) | | |URI (Section 6.7)|
+--------------------+--------+-----------+-----------+-----------------+
|nonce |string |Implemented|MUST |Single-use value |
| |(hex) | | |for replay prev. |
| | | | |(Section 6.8). |
+--------------------+--------+-----------+-----------+-----------------+
|sequence |integer |Implemented|SHOULD |Monotonically |
| | | | |increasing ctr. |
| | | | |(Section 6.8). |
+--------------------+--------+-----------+-----------+-----------------+
|issued_at |float |Implemented|MUST |Receipt issue |
| | | | |timestamp (Sec. |
| | | | |6.9). |
+--------------------+--------+-----------+-----------+-----------------+
|expires_at |float |Implemented|MUST |Receipt expiry |
| | | | |timestamp (Sec. |
| | | | |6.9). |
+--------------------+--------+-----------+-----------+-----------------+
|max_clock_skew |float |Implemented|SHOULD |Max clock skew |
| | | | |in seconds. |
| | | | |Default: 30.0. |
+--------------------+--------+-----------+-----------+-----------------+
|signature |string |Implemented|MUST |Ed25519 signature|
| |(128-char| | |over canonical |
| |hex) | | |JSON (RFC 8785). |
+--------------------+--------+-----------+-----------+-----------------+
Figure 4
4.2. Signed Bytes
The receipt field in the 0.4.1 reference implementation is computed
by the sign_receipt function as follows:
*Signing payload construction (0.4.1, Implemented):*
payload = "{trace_id}:{verdict}:{timestamp}:{tool}:{params_hash}:{rule_summary}"
Each field is interpolated using its string representation. The
timestamp is the Python float from time.time(). The payload is then
encoded as UTF-8.
*Algorithm (0.4.1, Implemented):*
receipt = HMAC-SHA256(secret, payload.encode("utf-8")).hexdigest()[:32]
Where secret is a 32-byte key (generated via secrets.token_bytes(32)
if not provided). The output is truncated to the first 32
hexadecimal characters of the full 64-character hex digest.
*Extended signing payload (Implemented, for EC mapping):* When the
extended fields (Section 4.1) are present, the signing payload MUST be
extended to include them:
extended_payload = "{trace_id}:{verdict}:{timestamp}:{tool}:{params_hash}:{rule_summary}:{request_hash}:{response_hash}:{runtime_context_hash}:{action}:{config_hash}:{issuer}:{audience}:{nonce}:{sequence}:{issued_at}:{expires_at}"
The extended receipt MUST use HMAC-SHA256 with the same truncation
(32 hex chars) or MUST use Ed25519 as specified in Section 7, with
the full 64-byte hex digest. The signature field (field 22) is
computed over the canonical JSON (RFC 8785) of all receipt fields.
*Critical property:* The signed bytes cover the full verification
context. Any change to any signed field -- the tool name, the
parameters, the verdict, the timestamp, the rule summary --
invalidates the receipt. This is what makes the receipt tamper-
evident.
4.3. Receipt JSON Example
The following is an example CCS receipt as produced by ccs-verifier
0.4.1 (core fields):
{
"trace_id": "a1b2c3d4e5f67890",
"verdict": "deny",
"timestamp": 1722384000.123456,
"tool": "shell_exec",
"params_hash": "f9e8d7c6b5a43210",
"rule_summary": "ssrf_protection=allow|rce_protection=deny",
"receipt": "0123456789abcdef0123456789abcdef",
"verified_at": 1722384000.123890,
"block_reason": "RCE pattern detected"
}
The following example shows the full receipt with all 22 fields:
{
"trace_id": "a1b2c3d4e5f67890",
"verdict": "allow",
"timestamp": 1722384000.123456,
"tool": "search_web",
"params_hash": "1234567890abcdef",
"rule_summary": "ssrf_protection=allow|rce_protection=allow",
"receipt": "fedcba9876543210fedcba9876543210",
"verified_at": 1722384000.123890,
"block_reason": "",
"request_hash": "sha256:e3b0c44298fc1c14...",
"response_hash": "",
"runtime_context_hash": "sha256:7d865e959b246691...",
"action": "ccs:tool-invoke:search_web:sha256:1234567890abcdef",
"config_hash": "sha256:aabbccdd0011223344...",
"issuer": "https://verifier.example.com/ccs/v1",
"audience": "https://executor.example.com/aeb",
"nonce": "f47ac10b58cc4372a5670e02b2c3d479",
"sequence": 42,
"issued_at": 1722384000.123000,
"expires_at": 1722384060.123000,
"max_clock_skew": 30.0,
"signature": "a1b2c3d4e5f6..."
}
4.4. Verifier Policy Floor
A conforming CCS verifier enforces a non-negotiable policy floor.
When a server or tool endpoint declares a security profile that is
weaker than the verifier's minimum requirements, the verifier MUST
reject the invocation rather than downgrading to match the server's
declared profile.
This requirement addresses a documented failure mode in the MCP
ecosystem where servers self-declare their security posture, and
relying parties accept the declaration at face value.
The verifier policy floor is enforced at the verification step,
before the tool invocation proceeds. The floor is determined by the
verifier's canonical configuration (Section 5) and is bound into the
receipt via the config_hash field.
*Implementation:* The verifier's canonical configuration includes a
policy_floor field. During verification, the verifier compares the
server's declared profile against this floor. If the declared profile
is weaker, the verifier MUST return a deny verdict with block_reason
set to "policy_floor_violation".
4.5. Verification Outcome States
A CCS verifier produces one of three verdict values:
* *allow*: The invocation passed all verification dimensions.
* *deny*: The invocation failed one or more verification dimensions.
* *escalate*: The verifier cannot make a definitive determination.
These three verdict values constitute the CCS evidence sufficiency
axis. This axis is independent from the executor lifecycle axis.
In addition to these verdict states, the verifier MAY record an
*outcome_status* field that captures the epistemic state regarding
the action's outcome:
* *confirmed*: The outcome is fully observable.
* *dispatched*: The verifier authorized but has no outcome evidence.
* *indeterminate*: The outcome cannot be determined. The verifier
MUST keep the record open and MUST NOT permit blind retries.
INDETERMINATE is not a sixth verdict. It is a state on the executor
lifecycle axis -- it describes what the verifier knows about the
outcome after the verdict was issued, not the verdict itself.
The outcome_status is orthogonal to the verdict. A deny verdict is
always confirmed. An allow verdict MAY be dispatched or
indeterminate if the verifier has no post-invocation visibility.
*Severity and reconcilability are independent axes.* A destructive
write with authenticated callback is high-severity but reconcilable.
A breaker trip with no telemetry is high-severity and indeterminate.
4.5.1. Scope-Comparison Axis
The scope-comparison axis determines whether a verifier can evaluate
a given profile. When a verifier cannot evaluate a profile, it
produces an UNDECIDED result with a reason code:
* NO_COMPARISON_RELATION: No verifier in the deployment can evaluate
this profile. The relying party MUST halt the comparison.
* PROFILE_NOT_HELD: This verifier does not hold the requested
profile, but another verifier may. The relying party SHOULD
consult other verifiers' capability manifests (Section 4.6) to
find one that holds the profile.
UNDECIDED is NOT a new top-level verdict. The top-level verdict
remains {allow, deny, escalate}. UNDECIDED is a per-comparison
result within the scope-comparison axis.
Example comparison result:
{
"comparison_axis": {
"verdict": "UNDECIDED",
"reason": "PROFILE_NOT_HELD",
"requested_profile": "mcp-tls-1.3",
"verifier_profiles": ["mcp-plain", "mcp-tls-1.2"]
}
}
4.5.2. Evidence Completeness (Third Axis)
Evidence completeness is a real axis, but it does not have its own
verdict token. A "provisional" or "revisable" verdict in the
authorization layer is too easily read as "soft allow," creating
authorization-layer ambiguity.
Instead, evidence completeness is implemented via the challenge loop
(Section 4.7): when a verifier lacks sufficient evidence, it issues
a deny verdict with a structured challenge field. This answers the
third axis without introducing a new token.
This also clarifies the boundary with INDETERMINATE:
- Verifier lacking records = evidence gap -> fail-closed + challenge
- Executor lacking records = INDETERMINATE on the outcome axis
4.6. Verifier Capability Manifest
A conforming verifier publishes a capability manifest declaring
which profiles it can evaluate and what comparison scope it supports.
The manifest is a signed, versioned JSON document.
Manifest structure:
{
"manifest_version": "1.0",
"verifier_id": "ccs-verifier/1.1.7",
"profiles": [
{
"profile_id": "mcp-tls-1.2",
"capabilities": ["compare", "validate"],
"comparison_scope": ["mcp-plain", "mcp-tls-1.2"]
}
],
"challenge_policy": {
"expected_response_duration": 10,
"deadline_multiplier": 3,
"max_deadline": 120
},
"signing_key_fingerprint": "ad989eaf1bb89fa8",
"published_at": 1786634576,
"expires_at": 1789226576,
"signature": "..."
}
Fields:
* manifest_version: Version of the manifest schema.
* verifier_id: Stable identifier of the verifier.
* profiles: Array of profiles the verifier can evaluate. Each
profile declares its comparison scope.
* challenge_policy: Parameters for the challenge loop (Section 4.7).
- expected_response_duration: Declared expected time (seconds) for
an issuer to respond to a challenge. MUST be falsifiable
against operational workload.
- deadline_multiplier: Multiplier applied to
expected_response_duration to derive the challenge deadline.
Following OSPF (dead_interval = 4x hello) and BGP (holdtime =
3x keepalive) convention. Default: 3.
- max_deadline: Absolute upper bound to prevent unbounded waits.
* signing_key_fingerprint: SHA-256 fingerprint of the signing key.
* published_at, expires_at: Validity window.
Discovery Protocol:
A relying party queries the verifier capability manifest before
issuing a comparison request. If the verifier returns
UNDECIDED(PROFILE_NOT_HELD), the relying party checks other
verifiers' manifests to find one that holds the requested profile.
Binding:
The manifest_hash (SHA-256 of the canonical manifest JSON) is
included in the CCS receipt's canonical configuration (Section 5.1),
creating cryptographic linkage between what the verifier claimed it
could verify and what it actually verified.
4.7. Challenge Loop
When a verifier lacks sufficient evidence to evaluate a dimension,
it issues a deny verdict with a structured challenge field:
{
"verdict": "deny",
"block_reason": "evidence_incomplete",
"challenge": {
"missing": ["server_tls_certificate_chain",
"mutual_auth_client_proof"],
"format": "pem",
"issued_at": 1786635000,
"expires_at": 1786635030,
"deadline_multiplier_applied": 3,
"base_duration": 10,
"derivation": "expires_at = issued_at + (base_duration x
deadline_multiplier)"
}
}
Properties:
* Fail-closed: Evidence gap = deny. No provisional allow, no
"maybe." The third axis is answered without introducing
authorization-layer ambiguity.
* Actionable: The challenge field specifies exactly what evidence is
missing and in what format.
* Derivation-based deadline with absolute binding: The challenge
carries both issued_at (when the challenge was created) and
expires_at (the absolute deadline), where expires_at is derived
as issued_at + (base_duration x deadline_multiplier). The
derivation parameters (base_duration, deadline_multiplier) come
from the verifier's published capability manifest and are
checkable before runtime. The absolute expires_at is bound to
each challenge instance and checkable per-case. This follows
OSPF (dead_interval = 4x hello) and BGP (holdtime = 3x keepalive)
convention where timing parameters are expressed as multiples of
declared base values, while the instantiated absolute time is
carried in each protocol message. This makes every parameter
falsifiable at two levels:
- Manifest-level (pre-runtime): expected_response_duration and
deadline_multiplier are declared and checkable against
operational workload and design intent.
- Instance-level (per-case): expires_at is an absolute timestamp
bound to the specific challenge, verifiable by any party
without needing to re-derive.
* max_deadline cap: Prevents unbounded waits when
expected_response_duration is set unreasonably high. The
effective deadline is min(derived_deadline, max_deadline).
Protocol:
1. Issuer submits verification request.
2. Verifier determines evidence is insufficient.
3. Verifier returns deny + challenge specifying missing evidence,
with issued_at and expires_at derived from manifest parameters.
4. Issuer supplies missing evidence before expires_at.
5. Verifier re-evaluates with complete evidence.
6. If expires_at passes without response, the deny stands.
5. Canonical Configuration
5.1. Canonical Config Fields
A CCS Canonical Configuration is a JSON object that describes the
exact state of the verifier at the time a receipt was issued. It
MUST contain:
+==================+==========+==========+=======================+
| Field | Type | Required | Description |
+==================+==========+==========+=======================+
| rules | array of | MUST | Ordered list of rule |
| | strings | | names as registered. |
+------------------+----------+----------+-----------------------+
| policy_version | string | MUST | Version identifier of |
| | | | the policy governing |
| | | | rule evaluation. |
+------------------+----------+----------+-----------------------+
| signing_key_id | string | MUST | Identifier of the |
| | | | signing key used for |
| | | | this receipt. |
+------------------+----------+----------+-----------------------+
| verifier_version | string | MUST | Software version of |
| | | | the verifier. |
+------------------+----------+----------+-----------------------+
| schema_version | string | MUST | Version of the CCS |
| | | | receipt schema (e.g., |
| | | | "1.3"). |
+------------------+----------+----------+-----------------------+
| deployment_mode | string | MUST | "in-process" or "out-|
| | enum | | of-process". |
+------------------+----------+----------+-----------------------+
| manifest_hash | string | SHOULD | SHA-256 hash of the |
| | | | verifier capability |
| | | | manifest (Section 4.6) |
| | | | at time of issuance. |
+------------------+----------+----------+-------------------------+
Figure 5
Example canonical config:
{
"rules": ["ssrf_protection", "rce_protection", "credential_leak"],
"policy_version": "2026-08-01",
"signing_key_id": "key-2026-08-01-primary",
"verifier_version": "0.4.1",
"schema_version": "1.3",
"deployment_mode": "in-process",
"manifest_hash": "a1b2c3d4e5f6..."
}
5.2. Canonicalization Algorithm
The canonical configuration MUST be serialized using JSON
Canonicalization Scheme (JCS) as defined in [RFC8785]:
import json
def canonical_config(config: dict) -> bytes:
return json.dumps(
config,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=True,
allow_nan=False
).encode("utf-8")
Key properties:
* Keys are sorted lexicographically.
* No whitespace between tokens (compact form).
* ASCII-only encoding with \uXXXX escapes for non-ASCII.
* NaN and Infinity are not permitted.
5.3. Config Hash
The config_hash is computed as:
config_hash = hashlib.sha256(canonical_config(config)).hexdigest()
The config_hash MUST be included in the extended signing payload
(Section 4.2) when the extended fields are in use. This binds the
receipt to the exact rule set and policy version, preventing a
verifier from being reconfigured after issuing a receipt and having
the old receipt still validate under the new configuration.
6. Bindings
This section defines the nine binding mechanisms that connect a CCS
receipt to the various elements of the invocation lifecycle. These
bindings are designed to satisfy the native artifact verification
requirements of [I-D.schrock-action-evidence-boundary] Section 5.2.
6.1. request_hash Binding
*Purpose:* Bind the receipt to the exact request bytes as received by
the verifier.
*Mechanism:*
The verifier MUST compute request_hash as the SHA-256 digest of the
canonical bytes of the request that triggered verification:
def canonical_bytes(command) -> bytes:
return json.dumps({
"agent_id": command.agent_id,
"tool": command.tool,
"params": command.params,
"timestamp": command.timestamp,
"trace_id": command.trace_id,
}, sort_keys=True, separators=(",", ":")).encode("utf-8")
request_hash = "sha256:" + hashlib.sha256(
canonical_bytes(command)).hexdigest()
*Binding strength:* The request_hash changes if any material field of
the request changes. An attacker who modifies the request after
verification will produce a different request_hash, breaking the
binding.
*Status:* Implemented in ccs-verifier 0.4.1.
6.2. response_hash Binding
*Purpose:* Bind the receipt to the exact response produced by the
tool invocation.
*Mechanism:*
After the tool is invoked, the verifier (or a post-invocation hook)
MUST compute:
response_hash = "sha256:" + hashlib.sha256(response_bytes).hexdigest()
Where response_bytes is the exact byte representation of the tool's
response. For structured responses, the canonical JSON serialization
MUST be used.
*Timing:* The response_hash MUST be computed after the tool
invocation completes. It is a post-hoc binding.
*Status:* Implemented in ccs-verifier 0.4.1.
6.3. runtime_context_hash Binding
*Purpose:* Bind the receipt to the runtime execution environment.
*Mechanism:*
The runtime context is a JSON object containing environment-scoped
fields (caller_identity, tenant, environment, etc.). The
runtime_context_hash is computed as:
context_bytes = json.dumps(context, sort_keys=True,
separators=(",", ":")).encode("utf-8")
runtime_context_hash = "sha256:" + hashlib.sha256(
context_bytes).hexdigest()
*Binding strength:* Prevents a receipt issued in one environment
from being replayed in another.
*Status:* Implemented in ccs-verifier 0.4.1.
6.4. action Binding
*Purpose:* Provide a CAID-compatible action identifier that enables
the CCS receipt to participate in the AEB's CAID matching process.
*Mechanism:*
action = "ccs:tool-invoke:{tool_name}:{params_hash}"
Where {tool_name} is the tool name from the Command and {params_hash}
is the full SHA-256 hex digest (not truncated) of the canonical
parameters.
For EC integration, this action identifier MUST be mappable to a
CAID action-type definition via a relying-party-pinned mapping
profile. The mapping profile MUST specify:
* The source media type: "application/x-ccs-receipt+json"
* The target action type: the CAID action-type definition
* The material fields: tool, params (via params_hash)
*Status:* Implemented in ccs-verifier 0.4.1 (14 enum values).
6.5. params_hash Binding
*Purpose:* Bind the receipt to the exact tool parameters.
*Mechanism (Implemented in 0.4.1):*
def params_hash(params: dict) -> str:
return hashlib.sha256(
json.dumps(params, sort_keys=True,
separators=(",", ":")).encode()
).hexdigest()[:16]
The 0.4.1 implementation truncates to 16 hex characters (64 bits).
This provides collision resistance of approximately 2^32 under
birthday attack, which is sufficient for audit uniqueness within a
single verifier instance but MAY be insufficient for cross-verifier
replay prevention in high-volume deployments.
*Recommendation:* Implementations targeting conformance Level 2 or
above SHOULD use the full 64-character SHA-256 hex digest.
*Status:* Implemented (16-char truncation) in 0.4.1.
6.6. issuer Binding
*Purpose:* Identify the verifier that produced the receipt.
*Mechanism:*
The issuer field is a URI that identifies the verifier instance.
The URI MUST be stable across key rotations. The issuer identifier
MUST be covered by the signing mechanism so that an attacker cannot
substitute a different issuer without invalidating the receipt.
*Status:* Implemented in ccs-verifier 0.4.1.
6.7. audience Binding
*Purpose:* Identify the intended consumer of the receipt, preventing
receipt misdirection.
*Mechanism:*
The audience field is a URI that identifies the intended recipient.
A receipt whose audience does not match the consumer MUST be
rejected.
*Status:* Implemented in ccs-verifier 0.4.1.
6.8. nonce/sequence Binding
*Purpose:* Prevent replay of previously-issued receipts.
*Mechanism:*
The nonce field is a cryptographically random, single-use value:
nonce = secrets.token_hex(16)
The sequence field is a monotonically increasing integer counter
scoped to the issuer.
*Anti-replay enforcement:*
* The consumer MUST maintain a replay cache of seen (issuer, nonce)
pairs.
* A receipt with a previously-seen nonce MUST be rejected.
* The replay cache MUST persist across process restarts.
* The sequence field provides an additional ordering guarantee.
*Status:* Implemented in ccs-verifier 0.4.1 (UUID4-based nonce).
6.9. freshness Binding (issued_at + expires_at + max_clock_skew)
*Purpose:* Bound the temporal validity of the receipt.
*Mechanism:*
Three fields work together to enforce freshness:
* issued_at: Unix timestamp when the receipt was issued.
* expires_at: Unix timestamp after which the receipt MUST NOT be
accepted.
* max_clock_skew: Maximum acceptable clock skew in seconds.
Default: 30.0 seconds.
*Validation algorithm:*
def check_freshness(receipt, local_time, max_clock_skew=30.0):
skew = receipt.max_clock_skew or 30.0
if local_time < receipt.issued_at - skew:
return False
if local_time > receipt.expires_at + skew:
return False
return True
*Recommended expiry:* For tool-invocation governance, an expires_at
of issued_at + 60 seconds is RECOMMENDED.
*Status:* Implemented in ccs-verifier 0.4.1.
6.10. Receipt Time-Binding
A CCS receipt captures the verification moment -- not merely "was
this approved" but "when was this verified and what was the evidence
at that moment." The timestamp, verified_at, issued_at, and
expires_at fields together establish a temporal context that enables
retroactive reconciliation.
This design supports the following temporal authorization properties:
* *Verification-moment binding*: The receipt attests to the state of
knowledge at the time of verification.
* *Post-hoc reconciliation*: When new evidence arrives, the receipt
can be correlated with the new evidence using the trace_id and
timestamp.
* *Durable record*: The receipt survives transport, retries, and
principal changes.
7. Key Management
7.1. Key Generation
*HMAC-SHA256 (Implemented in 0.4.1):*
The signing key MUST be generated using a cryptographically secure
random number generator:
signing_key = secrets.token_bytes(32)
The key MUST NOT be derived from predictable sources (timestamps,
process IDs, weak PRNGs).
*Ed25519 (Implemented in v0.4.1, for cross-domain verification):*
For deployments where the verifier and consumer are in separate trust
domains, Ed25519 key pairs SHOULD be used. The private key signs the
receipt; the public key is distributed out-of-band for verification.
7.2. Key Enrollment
Before a CCS receipt can be verified by a consumer, the consumer MUST
have enrolled the verifier's key:
* *HMAC:* The shared secret MUST be distributed via a secure channel
(e.g., configuration management system, secrets manager). The key
MUST NOT be transmitted in-band with the receipt.
* *Ed25519:* The public key MUST be pinned by the consumer via an
out-of-band mechanism. The key fingerprint (SHA-256 of the raw
public key bytes) SHOULD be used as the signing_key_id.
7.3. Key Rotation
Key rotation MUST follow a phased approach:
1. *Announce:* The verifier publishes the new key identifier and
effective date.
2. *Dual-sign:* During the overlap window, receipts are signed with
both old and new keys.
3. *Cutover:* After the overlap window, only the new key is used.
4. *Retire:* The old key is retired after all receipts signed under
it have expired (per expires_at).
The overlap window MUST be at least 2x the maximum receipt lifetime
(expires_at - issued_at).
7.4. Verifier-Operator Separation
A fundamental security property of CCS is the separation between the
verifier (which evaluates rules and signs receipts) and the operator
(the agent runtime that invokes tools).
*In-process (0.4.1):* The verifier and operator share the same
address space. The signing key is held in the verifier's memory. A
compromised agent process CAN access the signing key. This is a
known limitation.
*Out-of-process (Implemented):* The verifier runs in a separate
process with its own memory space, file descriptors, and crash
domain. The signing key is held only in the verifier process. A
compromised agent process CANNOT access the signing key. This is the
RECOMMENDED deployment mode for security-critical applications.
8. Transport Requirements
8.1. Fail-Closed Semantics
CCS enforcement MUST be fail-closed. This is the core security
guarantee, derived from the formal framework [CCS-FORMAL] and
implemented in the reference implementation.
*Requirement:* If the verifier raises an exception, times out,
returns an ambiguous result, or is unreachable, the tool invocation
MUST be blocked.
*Implementation in 0.4.1:*
The govern() wrapper in the CCS SDK intercepts the tool function call
and calls verifier.verify() before invoking the original function.
If verify() raises any exception, the wrapper catches it and raises a
PermissionError, blocking the tool invocation.
def govern(fn, policy="default"):
def wrapper(*args, **kwargs):
try:
result = verifier.verify(command)
if not result.allowed:
raise PermissionError(result.block_reason)
except Exception:
raise PermissionError("Verification failed")
return fn(*args, **kwargs)
return wrapper
This is the fundamental difference from observer-pattern hooks (which
fail-open when the observer crashes, as documented in [CWE-636]).
8.2. Timeout Behavior
*Verifier timeout:* If the verifier does not respond within the
configured timeout (default: 5000ms for out-of-process, unlimited for
in-process), the invocation MUST be blocked.
*Receipt timeout:* The expires_at field (Section 6.9) provides an
upper bound on receipt validity. A receipt that has expired MUST be
rejected even if all other fields are valid.
*Combined:* Both timeouts are independent and both MUST be enforced.
8.3. In-Process vs Out-of-Process Deployment
+==================+==================+=======================+
| Property | In-Process | Out-of-Process |
+==================+==================+=======================+
| Process boundary | None (shared | Full (separate |
| | address space) | process) |
+------------------+------------------+-----------------------+
| Signing key | Vulnerable to | Protected |
| exposure | agent compromise | |
+------------------+------------------+-----------------------+
| Crash isolation | None (shared | Full (verifier |
| | crash domain) | survives agent crash) |
+------------------+------------------+-----------------------+
| Latency | 7.5us P50 | OOP Unix-socket |
| | (measured, | P50~133us / TCP+TLS |
| | 1000 iterations) | P50~200us (measured) |
+------------------+------------------+-----------------------+
| Implementation | Implemented + | Implemented + tested |
| status | tested (0.4.1) | (0.4.1) |
+------------------+------------------+-----------------------+
| Conformance | Level 0-1 | Level 2-3 |
| level | | |
+==================+==================+=======================+
Figure 6
*Note on performance:* The P50/P99 latency figures above are
informational measurements from the reference implementation.
They are not normative requirements.
9. Verifier Source Classification
This section classifies verifiers by their provenance and the level
of assurance a relying party SHOULD place in their verdicts.
9.1. Class A: Self-Built Verifier
A verifier whose rule set, signing key, and configuration are built,
operated, and audited entirely by the relying party's own
organization.
*Properties:*
* Full visibility into source code, rule definitions, and key mgmt.
* Independent verification of fail-closed semantics is possible.
* The signing key is generated and held within the relying party's
infrastructure.
*Assurance:* Highest.
*EC mapping:* A Class A verifier's receipt is directly consumable as
a policy-permit evidence component in an EC.
9.2. Class B: Audited Verifier
A verifier built by a third party but subjected to independent audit.
*Properties:*
* The source code is available for review.
* An independent audit has verified the implementation matches the
specification, particularly fail-closed semantics.
* The audit report is available to the relying party.
*Assurance:* High, contingent on audit scope and freshness.
9.3. Class C: Self-Declared Verifier
A verifier whose operator claims conformance to CCS but has not
submitted to independent audit.
*Properties:*
* The operator declares conformance. No independent verification.
* The relying party has no direct evidence of correct fail-closed
semantics.
*Assurance:* Low.
*EC mapping:* A Class C verifier's receipt MAY be consumed as
evidence, but the relying party SHOULD weight it lower than Class A
or Class B.
10. Conformance Levels
10.1. Level 0-3 Definitions
*Level 0 -- Baseline (Implemented in 0.4.1):*
The verifier produces a signed receipt covering: trace_id, verdict,
timestamp, tool, params_hash, rule_summary. Fail-closed enforcement
is implemented. In-process deployment only.
A Level 0 implementation satisfies the core tamper-evident audit
requirement but does not provide the bindings necessary for EC
composition.
*Level 1 -- Extended Bindings + Ed25519 Signing (Implemented in
v0.4.1):*
In addition to Level 0, the verifier produces receipts with all 22
fields: request_hash, config_hash, response_hash,
runtime_context_hash, action (CAID-compatible, 14 enum values),
issuer, audience, nonce (UUID4-based), sequence, issued_at,
expires_at, max_clock_skew, and signature. Ed25519 signing is
supported as the primary signature algorithm alongside HMAC-SHA256.
A Level 1 implementation provides all 22 fields required for EC
composition, with Ed25519 signatures providing non-repudiation.
*Level 2 -- Out-of-Process Full Isolation:*
In addition to Level 1, the verifier operates out-of-process with
Unix-socket or TCP+TLS transport. The signing key is held only in
the verifier process. Cross-replica replay prevention is implemented
via shared durable state.
A Level 2 implementation provides process isolation, key protection,
and all bindings necessary for the receipt to be consumed as a native
artifact by an AEB.
*Level 3 -- Multi-Hop Verification:*
In addition to Level 2, the verifier supports chain-of-custody
verification across multiple executor hops. Each hop produces its
own receipt with local audience binding. The verifier policy floor
(Section 4.4) is non-negotiable from the server side.
A Level 3 implementation provides the strongest single-domain
security guarantees: process isolation, key protection, distributed
replay prevention, and multi-hop chain-of-custody verification.
10.2. Level 4: Evidence Propagation
*Level 4 -- Evidence Propagation:*
In addition to Level 3, the verifier supports cross-organization
evidence propagation with chain integrity verification. The verifier
produces Evidence Envelopes, maintains Evidence Chains,
and supports offline chain verification.
Level 4 requirements:
* Evidence Intake Protocol compliance (Section 3).
* Evidence Propagation support (Section 11).
* Enforcement Interface compliance (Section 12).
* Cross-org Evidence Profile support.
* Chain integrity verification (full and spot).
* EC summary hash computation.
A Level 4 implementation provides the highest assurance: evidence
generation, cross-domain propagation, chain integrity, and
standardized enforcement consumption. This is the level required for
inter-organization agent interoperability.
Backward compatibility: All Level 0-3 implementations remain
conformant. Level 4 is additive; it does not change the requirements
for lower levels.
10.3. Conformance Vectors
Each conformance vector is a self-contained test case that a
conforming implementation MUST pass.
*CV-001: Basic ALLOW receipt (Level 0)*
Input Command:
agent_id: "test-agent"
tool: "search_web"
params: {"query": "weather in Tokyo"}
Expected:
verdict: "allow"
receipt: valid HMAC-SHA256 over the signed bytes
rule_summary: contains all registered rules with verdict=allow
*CV-002: Basic DENY receipt (Level 0)*
Input Command:
agent_id: "test-agent"
tool: "shell_exec"
params: {"command": "rm -rf /"}
Expected:
verdict: "deny"
block_reason: non-empty, describes the RCE pattern
receipt: valid HMAC-SHA256 covering the deny verdict
rule_summary: contains rce_protection=deny
*CV-003: Config hash binding (Level 1)*
Given two verifiers with different rule sets,
when they verify the same command,
then their config_hash values MUST differ.
*CV-004: Request hash binding (Level 1)*
Given the same tool and rule set,
when two commands differ only in params,
then their request_hash values MUST differ.
*CV-005: Issuer/audience binding (Level 2)*
Given a receipt with issuer="A" and audience="B",
when consumer "C" receives the receipt,
then consumer "C" MUST reject the receipt (audience mismatch).
*CV-006: Freshness enforcement (Level 2)*
Given a receipt with expires_at=T,
when consumer receives the receipt at time T+1,
then the consumer MUST reject the receipt.
*CV-007: Intake fail-closed (Level 4)*
Given a malformed intake message (missing required field),
when the message is submitted to the intake interface,
then the verifier MUST return an INTAKE_MISSING_FIELD error,
and no receipt MUST be produced.
*CV-008: Evidence Envelope propagation (Level 4)*
Given a valid receipt from Agent A,
when Agent A delegates to Agent B,
then Agent B MUST receive an Evidence Envelope containing the
original receipt and chain metadata,
and Agent B MUST produce its own receipt referencing the
original receipt's hash.
*CV-009: Cross-org self-contained verification (Level 4)*
Given an Evidence Envelope for cross-org propagation,
when the envelope is received by an external organization,
then the envelope MUST contain all data necessary for
verification without external lookups,
and the receiver MUST be able to verify chain integrity offline.
*CV-010: EC summary hash (Level 4)*
Given an Evidence Chain with N receipts,
when the summary hash is computed,
then the summary hash MUST be the SHA-256 digest of the
concatenation of all receipt hashes in insertion order,
and any change to any receipt in the chain MUST produce a
different summary hash.
11. Evidence Propagation
This section defines how evidence flows through multi-agent chains,
across organizational boundaries, and accumulates across execution
stages. It establishes "chain sovereignty" -- the right of the
the CCS evidence protocol to define evidence flow standards.
11.1. Single-Agent Propagation
Within a single agent, evidence flows through a defined pipeline:
Intake -> Verification -> Receipt Emission -> Local Storage
*Local evidence store interface:*
The agent MUST maintain a local evidence store with the following
properties:
* *Append-only:* Receipts, once written, MUST NOT be modified or
deleted. The store is a write-once log.
* *Indexed:* Receipts MUST be indexed by trace_id for retrieval.
* *Durable:* Receipts MUST be persisted to stable storage before the
tool invocation proceeds. If the agent crashes after emitting a
receipt but before executing the tool, the receipt MUST survive.
*Query interface:*
The local evidence store MUST support querying by:
* trace_id: Retrieve a specific receipt.
* tool: Retrieve all receipts for a specific tool.
* verdict: Retrieve all receipts with a specific verdict.
* time_range: Retrieve all receipts within a time window.
*Ordering guarantee:*
Receipts MUST be durable within the agent's local store before the
tool invocation proceeds. This ensures that even if the tool
execution fails or the agent crashes, the evidence of the
verification decision persists.
11.2. Multi-Agent Chain Propagation
When Agent A delegates a task to Agent B, evidence MUST propagate
with the delegation. This section defines the mechanism.
*Evidence Envelope:*
The Evidence Envelope is a wrapper structure that carries the original
receipt plus chain metadata:
{
"envelope_version": "1.3",
"original_receipt": { ... },
"parent_trace_id": "a1b2c3d4e5f67890",
"child_trace_id": "b2c3d4e5f6789012",
"delegation_reason": "search requires domain-specific search",
"hop_count": 1,
"previous_receipt_hash": "sha256:abc123..."
}
Fields:
* envelope_version: The Evidence Envelope schema version. MUST be
"1.3".
* original_receipt: The complete CCS Receipt from the delegating
agent.
* parent_trace_id: The trace_id of the delegating agent's
verification.
* child_trace_id: The trace_id assigned to the receiving agent's
verification.
* delegation_reason: Human-readable reason for the delegation.
* hop_count: The number of hops this evidence has traversed.
* previous_receipt_hash: The SHA-256 hash of the delegating agent's
receipt. This creates the cryptographic chain linkage.
*Chain linkage requirement:*
Each hop MUST produce its own receipt. The chain is a linked list of
receipts where each receipt references the previous hop's
receipt_hash via the previous_receipt_hash field in the Evidence
Envelope.
*Propagation protocol:*
1. Agent A receives a tool invocation request.
2. Agent A's verifier evaluates the request and produces Receipt_A.
3. Agent A delegates to Agent B, wrapping Receipt_A in an Evidence
Envelope.
4. Agent B receives the envelope, extracts the delegation request,
and submits it through its own intake (Section 3).
5. Agent B's verifier evaluates and produces Receipt_B, with
previous_receipt_hash set to hash(Receipt_A).
6. Agent B MAY further delegate, creating another envelope.
*Maximum chain depth:*
Implementations SHOULD enforce a maximum hop_count of 16. A chain
exceeding 16 hops MUST be flagged for review. This limit prevents
unbounded delegation chains that could exhaust verification resources.
11.3. Cross-Organization Propagation
When evidence crosses organizational boundaries, additional
requirements apply to ensure the receiving organization can validate
the evidence independently.
*Cross-Organization Evidence Profile:*
Cross-org evidence MUST include all of the following fields in
addition to the standard Evidence Envelope:
{
"envelope_version": "1.3",
"original_receipt": { ... },
"parent_trace_id": "...",
"child_trace_id": "...",
"delegation_reason": "...",
"hop_count": 2,
"previous_receipt_hash": "sha256:...",
"cross_org": {
"sender_org_id": "https://org-a.example.com",
"receiver_org_id": "https://org-b.example.com",
"trust_anchor_reference": "sha256:key-fingerprint...",
"self_contained": true
}
}
*Requirements:*
* sender_org_id: Identifier of the sending organization. MUST be a
stable URI.
* receiver_org_id: Identifier of the receiving organization. MUST
be a stable URI.
* trust_anchor_reference: SHA-256 fingerprint of the public key used
to verify the receipt's signature. The receiving organization
MUST have pre-enrolled this trust anchor.
* self_contained: MUST be true. Indicates that all verification
data is included in the envelope; no external lookups are
required.
*Self-containment requirement:*
Cross-org evidence MUST be self-validating. All data necessary for
signature verification, chain linkage verification, and freshness
checking MUST be included in the envelope. The receiving
organization MUST NOT need to make network calls to the sending
organization to validate the evidence.
This requirement ensures that cross-org evidence remains valid even
if the sending organization becomes unreachable.
11.4. Evidence Accumulation
As evidence propagates through a chain, it accumulates. Each hop
adds its receipt to the chain.
*Evidence Chain (EC) structure:*
{
"aec_version": "1.3",
"chain_id": "sha256:...",
"receipts": [
{ "receipt": {...}, "hop": 0, "receipt_hash": "sha256:..." },
{ "receipt": {...}, "hop": 1, "receipt_hash": "sha256:...",
"previous_receipt_hash": "sha256:..." },
{ "receipt": {...}, "hop": 2, "receipt_hash": "sha256:...",
"previous_receipt_hash": "sha256:..." }
],
"summary_hash": "sha256:...",
"total_hops": 2
}
*Ordering:*
EC MUST maintain insertion order. No reordering of receipts is
permitted. The first receipt (hop 0) is always the origin; each
subsequent receipt references its predecessor via
previous_receipt_hash.
*Pruning:*
EC MUST support pruning -- the removal of intermediate receipts while
preserving chain integrity. When an intermediate receipt is pruned:
* The chain linkage MUST be preserved: the receipt after the pruned
receipt MUST update its previous_receipt_hash to point to the
receipt before the pruned one.
* The summary hash MUST be recomputed.
* The pruned receipt MUST be stored in a separate pruned_receipts
array for audit purposes.
*Summary hash computation:*
The summary hash is a single hash that represents the entire chain:
summary_input = receipt_hash_0 || receipt_hash_1 || ... || receipt_hash_n
summary_hash = SHA-256(summary_input)
Where || denotes concatenation and receipt hashes are in insertion
order. Any change to any receipt in the chain produces a different
summary hash.
11.5. Chain Integrity Verification
This section defines algorithms for verifying chain integrity at any
point.
*Full verification:*
To verify the entire chain:
1. For each receipt in the chain (starting from hop 0):
a. Verify the receipt's signature (HMAC or Ed25519).
b. Verify the receipt's freshness (issued_at, expires_at).
c. If hop > 0, verify that previous_receipt_hash matches the
hash of the receipt at hop-1.
2. Verify that the summary hash matches the computed value.
3. Return {chain_valid: true, total_hops: N}.
*Spot verification:*
To verify a subset of the chain (for performance):
1. Verify the receipt at the specified hop.
2. Verify the chain linkage at that hop (previous_receipt_hash).
3. Verify the summary hash (which covers the entire chain).
4. Return {chain_valid: true/false, broken_at: hop or null}.
*Chain Verification Result:*
{
"chain_valid": true,
"broken_at": null,
"missing_hops": [],
"total_hops": 2,
"verification_mode": "full"
}
If the chain is broken:
{
"chain_valid": false,
"broken_at": 1,
"missing_hops": [],
"total_hops": 2,
"verification_mode": "full",
"failure_reason": "previous_receipt_hash mismatch at hop 1"
}
*Offline verification:*
Chain verification MUST be possible offline (no network calls
required). All data necessary for verification MUST be present in
the EC structure. This is consistent with the self-containment
requirement for cross-org evidence (Section 11.3).
12. Enforcement Interface
This section defines how evidence is consumed by enforcement systems
(such as AEB). It establishes "ecosystem sovereignty" -- the right
of the the CCS evidence protocol to define how its evidence is consumed.
12.1. Enforcement Input Contract
The enforcement input contract specifies the standard interface
through which enforcement systems consume CCS evidence.
*Single receipt input:*
When consuming a single CCS receipt, the enforcement system can
expect the following fields:
Required for enforcement consumption:
* verdict: The governance decision (allow/deny/escalate).
* signature: The cryptographic signature (Ed25519 or HMAC).
* action: The CAID-compatible action identifier.
* params_hash: Hash of the tool parameters.
* issuer: Identifier of the producing verifier.
* audience: Identifier of the intended consumer.
* issued_at / expires_at / max_clock_skew: Freshness parameters.
* nonce: Anti-replay value.
Optional for enforcement consumption:
* response_hash: Hash of the tool response.
* runtime_context_hash: Hash of the execution environment.
* chain_metadata: Evidence Envelope and EC information.
* outcome_status: The epistemic state (confirmed/dispatched/
indeterminate).
*EC input:*
When consuming an Evidence Chain, the enforcement system
receives the full EC structure (Section 11.4) and can additionally
expect:
* summary_hash: Single hash representing the entire chain.
* total_hops: Number of hops in the chain.
* All individual receipts with their chain linkages.
12.2. Receipt Validation Protocol
Enforcement systems MUST follow this step-by-step validation
protocol. Each step is independent; failure at any step produces a
specific rejection reason.
*Step 1: Freshness check.*
Verify that the receipt is within its temporal validity window:
if local_time > receipt.expires_at + receipt.max_clock_skew:
return REJECT("FRESHNESS_EXPIRED")
if local_time < receipt.issued_at - receipt.max_clock_skew:
return REJECT("FRESHNESS_EXPIRED")
*Step 2: Audience check.*
Verify that the receipt is intended for this consumer:
if receipt.audience != self.identity:
return REJECT("AUDIENCE_MISMATCH")
*Step 3: Signature verification.*
Verify the cryptographic signature:
if not verify_signature(receipt, trusted_keys[receipt.issuer]):
return REJECT("SIGNATURE_INVALID")
*Step 4: Replay check.*
Verify that the receipt has not been previously consumed:
if (receipt.issuer, receipt.nonce) in replay_cache:
return REJECT("REPLAY_DETECTED")
replay_cache.add((receipt.issuer, receipt.nonce))
*Step 5: Binding verification.*
Verify that the receipt bindings match the current request:
if receipt.action != expected_action:
return REJECT("BINDING_MISMATCH")
if receipt.params_hash != compute_params_hash(current_params):
return REJECT("BINDING_MISMATCH")
*Step 6: Outcome status mapping.*
Map the outcome status to enforcement behavior (Section 12.4).
If all steps pass, the receipt is accepted as valid evidence.
12.3. Verification Failure Signaling
When validation fails, the enforcement system signals the failure
using a defined taxonomy.
*Failure categories and severity:*
+========================+==========+==============================+
| Failure Category | Severity | Enforcement Action |
+========================+==========+==============================+
| FRESHNESS_EXPIRED | CRITICAL | MUST deny. Receipt is |
| | | outside its validity window. |
+------------------------+----------+------------------------------+
| AUDIENCE_MISMATCH | CRITICAL | MUST deny. Receipt is not |
| | | intended for this consumer. |
+------------------------+----------+------------------------------+
| SIGNATURE_INVALID | CRITICAL | MUST deny. Receipt |
| | | cryptographic integrity is |
| | | compromised. |
+------------------------+----------+------------------------------+
| REPLAY_DETECTED | CRITICAL | MUST deny. Receipt has been |
| | | previously consumed. |
+------------------------+----------+------------------------------+
| BINDING_MISMATCH | CRITICAL | MUST deny. Receipt does not |
| | | match the current request. |
+------------------------+----------+------------------------------+
| OUTCOME_INDETERMINATE | WARNING | MAY escalate. Outcome is |
| | | unknown; apply conservative |
| | | policy. |
+========================+==========+==============================+
Figure 7
*Failure Report format:*
{
"failure_category": "FRESHNESS_EXPIRED",
"severity": "CRITICAL",
"receipt_trace_id": "a1b2c3d4e5f67890",
"timestamp": 1722384061.0
}
*Security requirements:*
* Failure signaling MUST NOT leak internal verification details
beyond the defined categories.
* Failure reports MUST be logged for audit purposes.
* Repeated failures from the same issuer SHOULD trigger an alert.
12.4. Outcome Status Mapping
This section defines how CCS outcome statuses map to enforcement
decisions.
+==================+===============================================+
| Outcome Status | Enforcement Behavior |
+==================+===============================================+
| confirmed | Enforcement MAY proceed with the action. |
+------------------+-----------------------------------------------+
| dispatched | Enforcement MUST wait for reconciliation |
| | before finalizing. |
+------------------+-----------------------------------------------+
| indeterminate | Enforcement MUST treat as unverified and |
| | apply conservative policy. |
+==================+===============================================+
Figure 8
*Combined verdict and outcome_status mapping:*
+==========+==============+==========================+============+
| Verdict | Outcome | Enforcement Action | Rationale |
+==========+==============+==========================+============+
| allow | confirmed | Proceed | Full |
| | | | evidence |
+----------+--------------+--------------------------+------------+
| allow | dispatched | Proceed with | Authorized |
| | | reconciliation pending | but |
| | | | unconfirmed|
+----------+--------------+--------------------------+------------+
| allow | indeterminate| Apply conservative | No outcome |
| | | policy; may escalate | visibility |
+----------+--------------+--------------------------+------------+
| deny | confirmed | Block | Blocked |
| | | | locally |
+----------+--------------+--------------------------+------------+
| escalate | * | Defer to higher authority| Verifier |
| | | | cannot |
| | | | decide |
+==========+==============+==========================+============+
Figure 9
This mapping aligns with the AEB draft terminology (INDETERMINATE,
PROVISIONAL, lifecycle boundaries) as discussed with Iman Schrock
(AEB-03).
13. Negative Test Cases
This section defines negative test cases that a conforming CCS
implementation MUST detect and reject. Each test case specifies the
attack scenario, the expected receipt change, the expected verdict,
and the detection condition.
These negative cases demonstrate that the bindings defined in
Section 6 are load-bearing: removing any one binding reopens the
corresponding attack.
13.1. Changed Arguments
*Scenario:* An attacker intercepts a valid receipt for tool
invocation A, modifies the tool parameters, and presents the modified
command with the original receipt.
*Expected receipt change:* The params_hash (and request_hash at
Level 1+) no longer matches the modified parameters.
*Expected verdict:* DENY.
*Detection condition:*
recomputed_params_hash = compute_params_hash(modified_params)
assert recomputed_params_hash != receipt.params_hash
*Binding defeated if removed:* params_hash binding (Section 6.5).
13.2. Changed Response
*Scenario:* An attacker intercepts the tool's response, modifies it,
and presents the modified response with the original receipt.
*Expected receipt change:* The response_hash no longer matches.
*Expected verdict:* The receipt is still valid for the pre-invocation
decision, but the downstream consumer detects the response mismatch.
*Detection condition:*
recomputed = hashlib.sha256(modified_response_bytes).hexdigest()
assert recomputed != receipt.response_hash
*Binding defeated if removed:* response_hash binding (Section 6.2).
13.3. Replay
*Scenario:* An attacker captures a valid receipt for a one-time
action and replays it to authorize a second invocation.
*Expected receipt change:* The nonce has already been recorded in
the consumer's replay cache.
*Expected verdict:* DENY (replay detected).
*Detection condition:*
if (receipt.issuer, receipt.nonce) in replay_cache:
return DENY
*Binding defeated if removed:* nonce/sequence binding (Section 6.8).
13.4. Stale Evidence
*Scenario:* An attacker presents a receipt issued 24 hours ago for a
time-sensitive action.
*Expected receipt change:* The expires_at field has passed.
*Expected verdict:* DENY (expired receipt).
*Detection condition:*
if current_time > receipt.expires_at + receipt.max_clock_skew:
return DENY
*Binding defeated if removed:* freshness binding (Section 6.9).
13.5. Wrong Audience
*Scenario:* An attacker obtains a receipt intended for Executor A
and presents it to Executor B.
*Expected receipt change:* The audience field identifies Executor A.
*Expected verdict:* DENY (audience mismatch).
*Detection condition:*
if receipt.audience != expected_audience:
return DENY
*Binding defeated if removed:* audience binding (Section 6.7).
13.6. Key Substitution
*Scenario:* An attacker generates their own signing key, produces a
forged receipt, and presents it with the attacker's public key.
*Expected receipt change:* The receipt's issuer is not in the
consumer's trust anchor store.
*Expected verdict:* DENY (untrusted key).
*Detection condition:*
if receipt.issuer not in trusted_issuers:
return DENY
if not verify_signature(receipt,
trusted_public_keys[receipt.issuer]):
return DENY
*Binding defeated if removed:* issuer binding (Section 6.6).
13.7. Verifier/Runtime Collusion
*Scenario:* The agent runtime and the verifier are operated by the
same entity. The verifier always returns ALLOW.
*Expected receipt change:* No change to the receipt. The receipt is
valid but the rule evaluation is trivially permissive.
*Expected verdict:* The receipt passes cryptographic verification,
but the AEB SHOULD flag the verifier as Class C and apply additional
scrutiny.
*Detection condition:*
if verifier.deny_rate < 0.001 and verifier.total_receipts > 10000:
flag_for_review(verifier)
*Mitigation:* Independent auditing (Class B) or out-of-process
deployment with independent operator.
13.8. Missing Transport
*Scenario:* The verifier is configured for out-of-process deployment,
but the transport is unavailable. The agent falls back to allowing
the invocation without verification.
*Expected receipt change:* No receipt is produced.
*Expected verdict:* A conforming implementation MUST block the tool
invocation (fail-closed).
*Detection condition:*
try:
result = await verifier_client.verify(command)
except ConnectionError:
raise PermissionError("Verifier unreachable")
*Binding defeated if removed:* Fail-closed semantics (Section 8.1).
13.9. Intake Fail-Closed Bypass
*Scenario:* An attacker submits a malformed intake message and
attempts to proceed with the tool invocation despite the intake
failure.
*Expected behavior:* The intake returns an error (Section 3.4). No
receipt is produced. The tool invocation MUST be blocked.
*Detection condition:*
result = intake(malformed_command)
assert isinstance(result, IntakeError)
assert no_receipt_produced()
assert tool_invocation_blocked()
*Protocol defeated if removed:* Evidence Intake Protocol (Section 3.3).
*New test vector:*
{
"vector_id": "NV-009",
"description": "Intake fail-closed bypass attempt",
"level": 4,
"category": "negative",
"attack": {
"type": "intake_fail_closed_bypass",
"modification": "Remove required 'tool' field from intake"
},
"expected": {
"detection_point": "intake validation",
"error_code": "INTAKE_MISSING_FIELD",
"receipt_produced": false,
"tool_blocked": true
}
}
13.10. Chain Tampering
*Scenario:* An attacker intercepts an Evidence Chain and
attempts to insert a forged receipt or remove an existing one.
*Expected behavior:* The chain integrity verification (Section 11.5)
detects the tampering. The previous_receipt_hash linkage is broken,
and the summary hash does not match.
*Detection condition (insertion):*
for i in range(1, len(aec.receipts)):
prev_hash = compute_hash(aec.receipts[i-1].receipt)
assert aec.receipts[i].previous_receipt_hash == prev_hash
assert compute_summary_hash(aec) == aec.summary_hash
*Detection condition (removal):*
assert compute_summary_hash(modified_aec) != original_summary_hash
*Protocol defeated if removed:* Evidence Propagation (Section 11).
*New test vector:*
{
"vector_id": "NV-010",
"description": "Evidence chain tampering by receipt insertion",
"level": 4,
"category": "negative",
"attack": {
"type": "chain_tampering",
"modification": "Insert forged receipt at hop 1"
},
"expected": {
"detection_point": "chain integrity verification",
"chain_valid": false,
"broken_at": 2,
"summary_match": false
}
}
13.11. Cross-Org Missing Self-Verification
*Scenario:* An attacker presents a cross-org Evidence Envelope that
is missing critical verification data.
*Expected behavior:* The receiving organization MUST reject the
envelope because it is not self-contained.
*Detection condition:*
if not envelope.cross_org.self_contained:
return REJECT("CROSS_ORG_NOT_SELF_CONTAINED")
if envelope.cross_org.trust_anchor_reference not in local_trust_store:
return REJECT("UNKNOWN_TRUST_ANCHOR")
*Protocol defeated if removed:* Cross-Organization Propagation
(Section 11.3).
*New test vector:*
{
"vector_id": "NV-011",
"description": "Cross-org evidence missing self-verification data",
"level": 4,
"category": "negative",
"attack": {
"type": "cross_org_incomplete",
"modification": "Remove signature from cross-org envelope"
},
"expected": {
"detection_point": "self-containment check",
"rejected": true,
"failure_category": "CROSS_ORG_NOT_SELF_CONTAINED"
}
}
14. Security Considerations
This section extends the security considerations from draft-02 with
new considerations for the Evidence Intake Protocol, Evidence
Propagation, and Enforcement Interface.
*Truncated hashes.* The 0.4.1 implementation truncates params_hash to
16 hex characters (64 bits) and receipt to 32 hex characters (128
bits). While sufficient for audit uniqueness within a single
deployment, these truncation levels provide reduced collision
resistance compared to full SHA-256 outputs. Implementations
targeting cross-deployment interoperability SHOULD use full-length
digests.
*HMAC key exposure in in-process mode.* When the verifier runs in the
same process as the agent, a compromised agent can read the signing
key from memory and forge receipts. Out-of-process deployment
mitigates this.
*Float timestamp precision.* The timestamp and verified_at fields use
Python floats (IEEE 754 double precision). Implementations SHOULD
use monotonic clocks for latency measurement and wall clocks only for
issuance timestamps.
*JSON canonicalization.* The params_hash and config_hash depend on
deterministic JSON serialization. Implementations MUST use the
canonical serialization specified in Section 4.2 and Section 5.2.
*Rule evaluation ordering.* The config_hash binding (Level 1+)
addresses this by binding the receipt to the exact rule set.
*TOCTOU in out-of-process mode.* The runtime_context_hash (Level 2)
mitigates this by binding the receipt to the context at verification
time.
*Verifier policy floor degradation.* A Class C verifier may declare a
weaker policy floor. Mitigation: independent auditing (Class B) or
out-of-process deployment with independent operator.
*Outcome status confusion.* Implementations MUST NOT treat
outcome_status as a verdict or use it to override the verdict
decision.
*Cross-organization evidence leakage.* When evidence propagates
across organizational boundaries, the Evidence Envelope contains the
full receipt including internal details (tool names, parameters,
rule summaries). Implementations SHOULD consider data minimization:
the cross-org profile MAY omit fields that are not necessary for
the receiving organization's enforcement decision. However, any
omitted field MUST NOT break the signature verification.
*Cross-org replay.* A receipt issued for organization A might be
replayed against organization B if both accept the same issuer.
Mitigation: the audience binding (Section 6.7) MUST be enforced at
each organizational boundary. Each organization SHOULD maintain an
independent replay cache.
*Chain integrity attacks.* An attacker may attempt to insert, remove,
or modify receipts in an Evidence Chain. The chain
linkage (previous_receipt_hash) and summary hash provide defense:
insertion breaks the linkage at the insertion point; removal changes
the summary hash; modification invalidates the receipt's signature.
However, an attacker who controls the verifier at a single hop can
produce a valid receipt for that hop. The chain will be
cryptographically valid, but the evidence at that hop is fraudulent.
Mitigation: Class B or Class A verification at every hop.
*Intake fail-closed bypass.* An attacker may attempt to circumvent
the intake validation by submitting malformed input and then
proceeding with the tool invocation despite the intake failure. The
fail-closed invariant (Section 3.3) ensures that no receipt is
produced on failure, and the caller MUST treat the absence of a
receipt as a denial. Implementations MUST ensure that the intake
error handling code path does not contain any fallback that allows
the invocation to proceed.
*Enforcement interface confusion.* An attacker may attempt to map CCS
outcome statuses incorrectly, treating "indeterminate" as
"confirmed" to bypass enforcement. The Enforcement Interface
(Section 12.4) defines an explicit mapping table that MUST be
followed. Implementations MUST NOT allow custom mappings that
weaken the enforcement guarantees.
*Evidence accumulation overflow.* An attacker may attempt to create
an extremely long chain to exhaust verification resources. The
maximum hop_count (recommended: 16, Section 11.2) limits chain
length. Implementations SHOULD enforce this limit at every hop and
SHOULD reject chains that exceed the limit without attempting to
verify them.
*Trust anchor compromise in cross-org scenarios.* If a trust anchor
(public key) used for cross-org evidence verification is compromised,
all evidence signed by the corresponding private key is suspect.
Implementations MUST support revocation of trust anchors and MUST
re-validate in-flight evidence when a trust anchor is revoked. The
key rotation protocol (Section 7.3) applies to cross-org trust
anchors with the additional requirement that revocation MUST be
propagated to all receiving organizations within the maximum receipt
lifetime.
15. IANA Considerations
This document does not request any IANA allocations.
16. References
16.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC7493] Bray, T., Ed., "The I-JSON Message Format", RFC 7493,
DOI 10.17487/RFC7493, March 2015,
<https://www.rfc-editor.org/info/rfc7493>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC8785] Rundgren, A., Jordan, B., and S. Erdtman, "JSON
Canonicalization Scheme (JCS)", RFC 8785,
DOI 10.17487/RFC8785, June 2020,
<https://www.rfc-editor.org/info/rfc8785>.
16.2. Informative References
[CCS-FORMAL]
Wang, G., "CCS Formal Framework",
DOI 10.5281/zenodo.21271910, 2026,
<https://doi.org/10.5281/zenodo.21271910>.
[CWE-636] MITRE, M., "CWE-636: Not Failing Securely (Fail Open)",
2026, <https://cwe.mitre.org/data/definitions/636.html>.
[I-D.schrock-action-evidence-boundary]
Schrock, I., "The Action Evidence Boundary for
Consequential Agent Effects", Work in Progress, Internet-
Draft, draft-schrock-action-evidence-boundary-03, 3 August
2026.
[I-D.schrock-ep-authorization-receipts]
Schrock, I., "Authorization Receipts for High-Risk Agent
Actions", Work in Progress, Internet-Draft, draft-schrock-
ep-authorization-receipts-09, 14 August 2026.
Appendix A. CCS v1.3 Receipt Field Summary Table
+==+====================+=======+=====+===========+======+============+
|# |Field |Type |Level|Status |Signed|Description |
+==+====================+=======+=====+===========+======+============+
|1 |trace_id |16-char|0 |Implemented|Yes |Unique event|
| | |hex | | | |identifier |
+--+--------------------+-------+-----+-----------+------+------------+
|2 |verdict |enum |0 |Implemented|Yes |allow / deny|
| | |string | | | |/ escalate |
+--+--------------------+-------+-----+-----------+------+------------+
|3 |timestamp |float |0 |Implemented|Yes |Verification|
| | | | | | |time |
+--+--------------------+-------+-----+-----------+------+------------+
|4 |tool |string |0 |Implemented|Yes |Tool name |
+--+--------------------+-------+-----+-----------+------+------------+
|5 |params_hash |16-char|0 |Implemented|Yes |SHA-256 of |
| | |hex | | | |params, |
| | | | | | |truncated |
+--+--------------------+-------+-----+-----------+------+------------+
|6 |rule_summary |string |0 |Implemented|Yes |Pipe- |
| | | | | | |delimited |
| | | | | | |rule results|
+--+--------------------+-------+-----+-----------+------+------------+
|7 |receipt |32-char|0 |Implemented|N/A |HMAC-SHA256 |
| | |hex | | | |signature |
+--+--------------------+-------+-----+-----------+------+------------+
|8 |verified_at |float |0 |Implemented|No |Verification|
| | | | | | |timestamp |
+--+--------------------+-------+-----+-----------+------+------------+
|9 |block_reason |string |0 |Implemented|No |Deny/ |
| | | | | | |escalate |
| | | | | | |reason |
+--+--------------------+-------+-----+-----------+------+------------+
|10|request_hash |hex |1 |Implemented|Yes |SHA-256 of |
| | |string | | | |full request|
+--+--------------------+-------+-----+-----------+------+------------+
|11|config_hash |hex |1 |Implemented|Yes |SHA-256 of |
| | |string | | | |canonical |
| | | | | | |config |
+--+--------------------+-------+-----+-----------+------+------------+
|12|response_hash |hex |1 |Implemented|Post- |SHA-256 of |
| | |string | | |hoc |response |
+--+--------------------+-------+-----+-----------+------+------------+
|13|runtime_context_hash|hex |1 |Implemented|Yes |SHA-256 of |
| | |string | | | |runtime |
| | | | | | |context |
+--+--------------------+-------+-----+-----------+------+------------+
|14|action |string |1 |Implemented|Yes |CAID- |
| | | | | | |compatible |
| | | | | | |action ID |
+--+--------------------+-------+-----+-----------+------+------------+
|15|issuer |URI |1 |Implemented|Yes |Verifier |
| | |string | | | |identifier |
+--+--------------------+-------+-----+-----------+------+------------+
|16|audience |URI |1 |Implemented|Yes |Intended |
| | |string | | | |consumer |
+--+--------------------+-------+-----+-----------+------+------------+
|17|nonce |hex |1 |Implemented|Yes |Single-use |
| | |string | | | |anti-replay |
+--+--------------------+-------+-----+-----------+------+------------+
|18|sequence |integer|1 |Implemented|Yes |Monotonic |
| | | | | | |counter |
+--+--------------------+-------+-----+-----------+------+------------+
|19|issued_at |float |1 |Implemented|Yes |Issue |
| | | | | | |timestamp |
+--+--------------------+-------+-----+-----------+------+------------+
|20|expires_at |float |1 |Implemented|Yes |Expiry |
| | | | | | |timestamp |
+--+--------------------+-------+-----+-----------+------+------------+
|21|max_clock_skew |float |1 |Implemented|Yes |Clock |
| | | | | | |tolerance |
| | | | | | |(seconds) |
+--+--------------------+-------+-----+-----------+------+------------+
|22|signature |128-char|1 |Implemented|Yes |Ed25519 |
| | |hex | | | |signature |
| | | | | | |over |
| | | | | | |canonical |
| | | | | | |JSON |
+--+--------------------+-------+-----+-----------+------+------------+
Figure 10
Appendix B. Conformance Test Vector Format
Each conformance test vector is encoded as a JSON object:
{
"vector_id": "CV-001",
"description": "Basic ALLOW receipt verification",
"level": 0,
"category": "positive",
"input": {
"command": {
"agent_id": "test-agent",
"tool": "search_web",
"params": {"query": "weather in Tokyo"},
"timestamp": 1722384000.0,
"trace_id": "0000000000000001"
},
"rules": ["ssrf_protection", "rce_protection", "credential_leak"],
"signing_key_hex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
},
"expected": {
"verdict": "allow",
"block_reason": "",
"receipt_valid": true,
"rule_summary_pattern": "ssrf_protection=allow|rce_protection=allow|credential_leak=allow"
},
"negative_variant": null
}
For negative test vectors:
{
"vector_id": "NV-001",
"description": "Changed arguments detected by params_hash mismatch",
"level": 0,
"category": "negative",
"attack": {
"type": "changed_arguments",
"modification": "params.query changed from 'weather in Tokyo' to 'weather in Osaka'"
},
"expected": {
"detection_point": "params_hash mismatch",
"verdict": "deny",
"binding_defeated_if_removed": "params_hash (Section 6.5)"
}
}
For Level 4 propagation test vectors:
{
"vector_id": "CV-008",
"description": "Evidence Envelope propagation",
"level": 4,
"category": "positive",
"input": {
"agent_a_receipt": {
"trace_id": "a1b2c3d4e5f67890",
"verdict": "allow",
"tool": "delegate_search"
},
"delegation": {
"to_agent": "agent-b",
"reason": "domain-specific search"
}
},
"expected": {
"envelope_present": true,
"envelope_version": "1.3",
"hop_count": 1,
"previous_receipt_hash_valid": true,
"agent_b_receipt_produced": true
}
}
Author's Address
Guigui Wang
Correctover
Xiamen, Fujian
China
Email: wgg234114134@163.com
URI: https://www.correctover.com