<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" submissionType="IETF" docName="draft-correctover-ccs-00" category="info" ipr="trust200902" obsoletes="" updates="" xml:lang="en" symRefs="true" sortRefs="true" tocInclude="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.31.0 -->
  <!-- Generated by id2xml 1.5.2 on 2026-08-04T00:52:25Z -->
	<front>
    <title abbrev="CCS Receipt and Binding Specification">Correctover Conformance Shape (CCS): A Receipt and Binding Specification for Agent Runtime Verification</title>
    <seriesInfo name="Internet-Draft" value="draft-correctover-ccs-00"/>
    <author initials="G." surname="Wang" fullname="Guigui Wang">
      <organization>Correctover</organization>
      <address>
        <postal>
          <street>Xiamen, Fujian</street>
          <street>China</street>
        </postal>
        <uri>https://example.com</uri>
      </address>
    </author>
    <date year="2026" month="August" day="4"/>
    <workgroup>Internet Engineering Task Force</workgroup>
    <abstract>
      <t>The Correctover Conformance Shape (CCS) defines a tamper-evident
receipt schema and a set of cryptographic bindings that together
constitute a verifiable conformance record for an agent runtime's
decision to permit, deny, or escalate a tool invocation.  CCS is
designed as a one-receipt-per-invocation object that can be consumed
by an executor-side Action Evidence Boundary (AEB), providing the
request_hash, response_hash, runtime_context_hash,
action binding, params_hash binding, issuer,
audience, nonce/sequence, freshness, and config_hash fields that the
AEB processing model requires as native inputs.</t>
      <t>This document specifies the CCS Receipt Schema, the Canonical
Configuration model, the nine binding mechanisms, key management,
transport requirements, verifier source classification, conformance
levels, and negative test cases.  It is intended to enable a reader
such as the author of the Action Evidence Boundary specification
to evaluate whether and how a CCS receipt can be mapped into
an Authorization Evidence Chain (AEC)
component.</t>
    </abstract>
  </front>
  <middle>
    <section anchor="sect-1" numbered="true" toc="default">
      <name>Introduction</name>
      <t>
   The Correctover Conformance Shape (CCS) is a receipt-and-binding
   specification that produces a single, tamper-evident,
   cryptographically bound artifact 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."</t>
      <t>
   CCS is not a policy language, an authorization framework, or a
   transport protocol.  It is the _shape_ of the evidence that a
   verifier produces when it finishes evaluating a command.  This
   evidence is intended to be consumed by downstream systems — including
   an executor-side Action Evidence Boundary (AEB) as defined in <xref target="I-D.schrock-action-evidence-boundary" format="default"/> —
   as one native artifact within a larger Authorization Evidence Chain
   (AEC).</t>
      <t>
   The initial reference implementation (ccs-verifier 0.2.0) 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 <xref target="I-D.schrock-action-evidence-boundary" format="default"/>
        <xref target="sect-5.2" format="default"/> requires from a native artifact: request binding,
   response binding, runtime context binding, issuer/audience,
   freshness, nonce, and canonical configuration.</t>
      <section anchor="sect-1.1" numbered="true" toc="default">
        <name>Scope</name>
        <t>
   CCS specifies:</t>
        <ul spacing="normal">
          <li>
            <t>A receipt schema (<xref target="sect-3" format="default"/>) that binds a verdict to the exact
      invocation context.</t>
          </li>
          <li>
            <t>A canonical configuration model (<xref target="sect-4" format="default"/>) that binds the receipt
      to the exact rule set and policy under which verification
      occurred.</t>
          </li>
          <li>
            <t>Nine binding mechanisms (<xref target="sect-5" format="default"/>) that connect the receipt to
      the request bytes, response bytes, runtime context, action
      identity, parameter identity, issuer, audience, nonce, and
      freshness window.</t>
          </li>
          <li>
            <t>Key management requirements (<xref target="sect-6" format="default"/>).</t>
          </li>
          <li>
            <t>Transport requirements (<xref target="sect-7" format="default"/>) including fail-closed
      semantics.</t>
          </li>
          <li>
            <t>Verifier source classification (<xref target="sect-8" format="default"/>).</t>
          </li>
          <li>
            <t>Conformance levels and test vectors (<xref target="sect-9" format="default"/> and <xref target="sect-10" format="default"/>).</t>
          </li>
        </ul>
        <t>
   CCS does NOT define:</t>
        <ul spacing="normal">
          <li>
            <t>A new authorization receipt format that replaces or competes with
      <xref target="I-D.schrock-ep-authorization-receipts" format="default"/>.  CCS is a _verifier-side decision record_, not an
      organizational authorization artifact.</t>
          </li>
          <li>
            <t>A policy language.  Rules are pluggable; CCS specifies how rule
      outcomes are recorded and bound, not what the rules are.</t>
          </li>
          <li>
            <t>A transport protocol for inter-process communication.  The
      reference implementation uses in-process function calls; a gRPC
      transport is specified at the interface level but not yet
      implemented.</t>
          </li>
          <li>
            <t>A universal evidence taxonomy.  CCS defines one artifact shape for
      one class of decision (tool-invocation governance).</t>
          </li>
        </ul>
      </section>
      <section anchor="sect-1.2" numbered="true" toc="default">
        <name>Relationship to CCS Formal Framework (DOI References)</name>
        <t>
   This Internet-Draft is a protocol specification derived from and
   complementary to the following academic publications:</t>
        <ul spacing="normal">
          <li>
            <t>*DOI: 10.5281/zenodo.21271910* — "CCS Formal Framework."  This
      paper defines the mathematical foundation: the Required(τ) ⊆
      Supported(τ) 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.</t>
          </li>
          <li>
            <t>*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.</t>
          </li>
          <li>
            <t>*DOI: 10.5281/zenodo.21234580* — "CCS Standard v1.0."  The
      overarching standard that defines the three pillars (Conformance,
      Reproducibility, Compliance) and the six-dimensional verification
      model (Structure, Schema, Latency, Cost, Identity, Integrity).</t>
          </li>
        </ul>
        <t>
   A PANW contributor cited CCS DOI in a fork of Palo-Alto-AI-Research-
   Lab/semantic-kernel (PR #14199, closed by Microsoft).  This citation
   is noted as evidence of external interest in the CCS DOI; it does not
   constitute adoption by Microsoft or the Semantic Kernel project.</t>
      </section>
    </section>
    <section anchor="sect-2" numbered="true" toc="default">
      <name>Terminology</name>
      <t>
   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 <xref target="RFC2119" format="default"/> <xref target="RFC8174" format="default"/> when, and only when, they appear in all
   capitals, as shown here.</t>
      <t>
   *CCS-specific terms:*</t>
      <t>
   *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.</t>
      <t>
   *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).</t>
      <t>
   *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.</t>
      <t>
   *VerificationResult*: The output of a verification.  Contains the
   verdict, signed receipt, rule results, and metadata.</t>
      <t>
   *Receipt*: A truncated HMAC-SHA256 (or Ed25519, per <xref target="sect-6" format="default"/>) digest
   that binds the verification context to a shared secret or signing
   key, providing tamper-evident audit.</t>
      <t>
   *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.</t>
      <t>
   *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).</t>
      <t>
   *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.</t>
    </section>
    <section anchor="sect-3" numbered="true" toc="default">
      <name>CCS Receipt Schema</name>
      <section anchor="sect-3.1" numbered="true" toc="default">
        <name>Field Definitions</name>
        <t>
   A conforming CCS Receipt is a JSON object conforming to I-JSON
   <xref target="RFC7493" format="default"/>.  The following table defines each field.  Fields marked
   "Implemented" are present in ccs-verifier 0.2.0.  Fields marked
   "Specified, not yet implemented" are defined by this specification
   but are not yet present in the 0.2.0 reference implementation.<!--
   draft-correctover-ccs-00-fixed.txt(480): Warning: Unexpected title: expected
   'Figure ...', found 'Table 1'.  This looks like a figure that has been
   entered as a texttable.  The generated XML will need adjustment.
   -->
        </t>
        <figure anchor="le-1">
          <artwork name="" type="" align="left" alt=""><![CDATA[
+====================+========+===========+===========+=================+
|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".  The |
|                    |        |           |           |governance       |
|                    |        |           |           |decision.        |
+--------------------+--------+-----------+-----------+-----------------+
|timestamp           |float   |Implemented|MUST       |Unix timestamp   |
|                    |        |           |           |(time.time()) at |
|                    |        |           |           |which the verdict|
|                    |        |           |           |was finalized.   |
|                    |        |           |           |Float precision. |
+--------------------+--------+-----------+-----------+-----------------+
|tool                |string  |Implemented|MUST       |The tool name    |
|                    |        |           |           |from the Command.|
|                    |        |           |           |Identifies which |
|                    |        |           |           |tool invocation  |
|                    |        |           |           |was verified.    |
+--------------------+--------+-----------+-----------+-----------------+
|params_hash         |string  |Implemented|MUST       |SHA-256 of       |
|                    |(16-char|           |           |canonical JSON of|
|                    |hex)    |           |           |the              |
|                    |        |           |           |Command.params   |
|                    |        |           |           |dict, truncated  |
|                    |        |           |           |to 16 hex chars. |
+--------------------+--------+-----------+-----------+-----------------+
|rule_summary        |string  |Implemented|MUST       |Pipe-delimited   |
|                    |        |           |           |list of          |
|                    |        |           |           |rule_name=verdict|
|                    |        |           |           |pairs, in rule   |
|                    |        |           |           |evaluation order.|
+--------------------+--------+-----------+-----------+-----------------+
|receipt             |string  |Implemented|MUST       |HMAC-SHA256 over |
|                    |(32-char|           |           |the signed bytes |
|                    |hex)    |           |           |(Section 3.2),   |
|                    |        |           |           |truncated to 32  |
|                    |        |           |           |hex chars.       |
+--------------------+--------+-----------+-----------+-----------------+
|verified_at         |float   |Implemented|MUST       |Unix timestamp of|
|                    |        |           |           |verification.    |
|                    |        |           |           |May differ from  |
|                    |        |           |           |timestamp if     |
|                    |        |           |           |post-processing  |
|                    |        |           |           |occurred.        |
+--------------------+--------+-----------+-----------+-----------------+
|block_reason        |string  |Implemented|Conditional|Human-readable   |
|                    |        |           |           |reason for deny/ |
|                    |        |           |           |escalate         |
|                    |        |           |           |verdicts.  MUST  |
|                    |        |           |           |be empty string  |
|                    |        |           |           |for allow.       |
+--------------------+--------+-----------+-----------+-----------------+
|request_hash        |string  |Specified, |MUST       |SHA-256 of the   |
|                    |(hex)   |not yet    |           |canonical bytes  |
|                    |        |implemented|           |of the original  |
|                    |        |           |           |request as       |
|                    |        |           |           |received by the  |
|                    |        |           |           |verifier.  Binds |
|                    |        |           |           |receipt to exact |
|                    |        |           |           |request bytes    |
|                    |        |           |           |(Section 5.1).   |
+--------------------+--------+-----------+-----------+-----------------+
|response_hash       |string  |Specified, |SHOULD     |SHA-256 of the   |
|                    |(hex)   |not yet    |           |tool response    |
|                    |        |implemented|           |bytes, computed  |
|                    |        |           |           |after invocation.|
|                    |        |           |           |Binds receipt to |
|                    |        |           |           |exact response   |
|                    |        |           |           |(Section 5.2).   |
+--------------------+--------+-----------+-----------+-----------------+
|runtime_context_hash|string  |Specified, |SHOULD     |SHA-256 of       |
|                    |(hex)   |not yet    |           |canonical JSON of|
|                    |        |implemented|           |runtime context  |
|                    |        |           |           |(caller identity,|
|                    |        |           |           |tenant,          |
|                    |        |           |           |environment).    |
|                    |        |           |           |Binds receipt to |
|                    |        |           |           |execution        |
|                    |        |           |           |environment      |
|                    |        |           |           |(Section 5.3).   |
+--------------------+--------+-----------+-----------+-----------------+
|action              |string  |Specified, |MUST       |The CAID-        |
|                    |        |not yet    |           |compatible action|
|                    |        |implemented|           |identifier for   |
|                    |        |           |           |this invocation. |
|                    |        |           |           |Enables AEC      |
|                    |        |           |           |matching         |
|                    |        |           |           |(Section 5.4).   |
+--------------------+--------+-----------+-----------+-----------------+
|config_hash         |string  |Specified, |MUST       |SHA-256 of the   |
|                    |(hex)   |not yet    |           |canonical        |
|                    |        |implemented|           |configuration    |
|                    |        |           |           |(Section 4).     |
|                    |        |           |           |Binds receipt to |
|                    |        |           |           |exact rule set.  |
+--------------------+--------+-----------+-----------+-----------------+
|issuer              |string  |Specified, |MUST       |Identifier of the|
|                    |(URI)   |not yet    |           |verifier that    |
|                    |        |implemented|           |produced this    |
|                    |        |           |           |receipt          |
|                    |        |           |           |(Section 5.6).   |
+--------------------+--------+-----------+-----------+-----------------+
|audience            |string  |Specified, |MUST       |Identifier of the|
|                    |(URI)   |not yet    |           |intended consumer|
|                    |        |implemented|           |of this receipt  |
|                    |        |           |           |(Section 5.7).   |
+--------------------+--------+-----------+-----------+-----------------+
|nonce               |string  |Specified, |MUST       |Unpredictable,   |
|                    |(hex)   |not yet    |           |single-use value |
|                    |        |implemented|           |for replay       |
|                    |        |           |           |prevention       |
|                    |        |           |           |(Section 5.8).   |
+--------------------+--------+-----------+-----------+-----------------+
|sequence            |integer |Specified, |SHOULD     |Monotonically    |
|                    |        |not yet    |           |increasing       |
|                    |        |implemented|           |counter per      |
|                    |        |           |           |issuer for       |
|                    |        |           |           |ordering         |
|                    |        |           |           |(Section 5.8).   |
+--------------------+--------+-----------+-----------+-----------------+
|issued_at           |float   |Specified, |MUST       |Unix timestamp   |
|                    |        |not yet    |           |when the receipt |
|                    |        |implemented|           |was issued       |
|                    |        |           |           |(distinct from   |
|                    |        |           |           |timestamp).      |
|                    |        |           |           |(Section 5.9).   |
+--------------------+--------+-----------+-----------+-----------------+
|expires_at          |float   |Specified, |MUST       |Unix timestamp   |
|                    |        |not yet    |           |after which this |
|                    |        |implemented|           |receipt MUST NOT |
|                    |        |           |           |be accepted.     |
|                    |        |           |           |(Section 5.9).   |
+--------------------+--------+-----------+-----------+-----------------+
|max_clock_skew      |float   |Specified, |SHOULD     |Maximum          |
|                    |        |not yet    |           |acceptable clock |
|                    |        |implemented|           |skew in seconds. |
|                    |        |           |           |Default: 30.0.   |
|                    |        |           |           |(Section 5.9).   |
+--------------------+--------+-----------+-----------+-----------------+
]]></artwork>
        </figure>
      </section>
      <section anchor="sect-3.2" numbered="true" toc="default">
        <name>Signed Bytes</name>
        <t>
   The receipt field in the 0.2.0 reference implementation is computed
   by the sign_receipt function as follows:</t>
        <t>
   *Signing payload construction (0.2.0, Implemented):*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
payload = "{trace_id}:{verdict}:{timestamp}:{tool}:{params_hash}:{rule_summary}"
]]></artwork>
        <t>
   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.</t>
        <t>
   *Algorithm (0.2.0, Implemented):*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
receipt = HMAC-SHA256(secret, payload.encode("utf-8")).hexdigest()[:32]
]]></artwork>
        <t>
   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.</t>
        <t>
   *Extended signing payload (Specification, for AEC mapping):*
   When the extended fields (<xref target="sect-3.1" format="default"/>, "Specified, not yet implemented") are present, the signing payload MUST be extended to
   include them:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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}"
]]></artwork>
        <t>
   The extended receipt MUST use HMAC-SHA256 with the same truncation
   (32 hex chars) or MUST use Ed25519 as specified in <xref target="sect-6" format="default"/>, with
   the full 64-byte hex digest.</t>
        <t>
   *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.</t>
      </section>
      <section anchor="sect-3.3" numbered="true" toc="default">
        <name>Receipt JSON Example</name>
        <t>
   The following is an example CCS receipt as produced by ccs-verifier
   0.2.0 (Implemented fields only):</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
{
  "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: (rm\\s+-rf\\s+/)"
}
]]></artwork>
        <t>
   The following example shows the extended receipt with AEC-mappable
   fields (Specified, not yet implemented):</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
{
  "trace_id": "a1b2c3d4e5f67890",
  "verdict": "allow",
  "timestamp": 1722384000.123456,
  "tool": "search_web",
  "params_hash": "1234567890abcdef",
  "rule_summary": "ssrf_protection=allow|rce_protection=allow|credential_leak=allow",
  "receipt": "fedcba9876543210fedcba9876543210",
  "verified_at": 1722384000.123890,
  "block_reason": "",
  "request_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "response_hash": "",
  "runtime_context_hash": "sha256:7d865e959b2466918c180089997d8745e10a3ef4b8d17e2a6c7b9f3e2d1a0c8b",
  "action": "ccs:tool-invoke:search_web:sha256:1234567890abcdef",
  "config_hash": "sha256:aabbccdd00112233445566778899aabbccdd00112233445566778899aabbccdd",
  "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
}
]]></artwork>
      </section>
    </section>
    <section anchor="sect-4" numbered="true" toc="default">
      <name>Canonical Configuration</name>
      <section anchor="sect-4.1" numbered="true" toc="default">
        <name>Canonical Config Fields</name>
        <t>
   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:<!--
   draft-correctover-ccs-00-fixed.txt(616): Warning: Unexpected title: expected
   'Figure ...', found 'Table 2'.  This looks like a figure that has been
   entered as a texttable.  The generated XML will need adjustment.
   -->
        </t>
        <figure anchor="le-2">
          <artwork name="" type="" align="left" alt=""><![CDATA[
 +==================+==========+==========+=======================+
 | 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.2").               |
 +------------------+----------+----------+-----------------------+
 | deployment_mode  | string   | MUST     | "in-process" or "out- |
 |                  | enum     |          | of-process".          |
 +------------------+----------+----------+-----------------------+
]]></artwork>
        </figure>
        <t>
   Example canonical config:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
{
  "rules": ["ssrf_protection", "rce_protection", "credential_leak"],
  "policy_version": "2026-07-01",
  "signing_key_id": "key-2026-07-01-primary",
  "verifier_version": "0.2.0",
  "schema_version": "1.2",
  "deployment_mode": "in-process"
}
]]></artwork>
      </section>
      <section anchor="sect-4.2" numbered="true" toc="default">
        <name>Canonicalization Algorithm</name>
        <t>
   The canonical configuration MUST be serialized using JSON
   Canonicalization Scheme (JCS) as defined in <xref target="RFC8785" format="default"/>:</t>
        <t>
   import json</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
def canonical_config(config: dict) -> bytes:
    """Serialize config to canonical JSON per RFC 8785 (JCS)."""
    return json.dumps(
        config,
        sort_keys=True,
        separators=(",", ":"),
        ensure_ascii=True,
        allow_nan=False
    ).encode("utf-8")
]]></artwork>
        <t>
   Key properties:</t>
        <ul spacing="normal">
          <li>
            <t>Keys are sorted lexicographically.</t>
          </li>
          <li>
            <t>No whitespace between tokens (compact form).</t>
          </li>
          <li>
            <t>ASCII-only encoding with \uXXXX escapes for non-ASCII.</t>
          </li>
          <li>
            <t>NaN and Infinity are not permitted (must serialize as strings or
      reject).</t>
          </li>
        </ul>
      </section>
      <section anchor="sect-4.3" numbered="true" toc="default">
        <name>Config Hash</name>
        <t>
   The config_hash is computed as:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
config_hash = hashlib.sha256(canonical_config(config)).hexdigest()
]]></artwork>
        <t>
   The config_hash MUST be included in the extended signing payload
   (<xref target="sect-3.2" format="default"/>) 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.</t>
      </section>
    </section>
    <section anchor="sect-5" numbered="true" toc="default">
      <name>Bindings</name>
      <t>
   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 <xref target="I-D.schrock-action-evidence-boundary" format="default"/> <xref target="sect-5.2" format="default"/>, enabling the CCS receipt to
   participate in CAID matching (Section 5.3 of <xref target="I-D.schrock-action-evidence-boundary" format="default"/>) and AEC evidence
   satisfaction (Section 5.4 of <xref target="I-D.schrock-action-evidence-boundary" format="default"/>).</t>
      <section anchor="sect-5.1" numbered="true" toc="default">
        <name>request_hash Binding</name>
        <t>
   *Purpose:* Bind the receipt to the exact request bytes as received by
   the verifier.</t>
        <t>
   *Mechanism:*</t>
        <t>
   The verifier MUST compute request_hash as the SHA-256 digest of the
   canonical bytes of the request that triggered verification.  The
   canonical bytes are defined as the Command.canonical_bytes() output
   from the reference implementation:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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()
]]></artwork>
        <t>
   *Binding strength:* The request_hash changes if any material field of
   the request changes — the tool name, the parameters, the agent_id, or
   the trace_id.  An attacker who modifies the request after
   verification will produce a different request_hash, breaking the
   binding.</t>
        <t>
   *Status:* Specified, not yet implemented in ccs-verifier 0.2.0.  The
   0.2.0 implementation binds via params_hash (16-char truncation) which
   covers only the parameters, not the full request context.</t>
      </section>
      <section anchor="sect-5.2" numbered="true" toc="default">
        <name>response_hash Binding</name>
        <t>
   *Purpose:* Bind the receipt to the exact response produced by the
   tool invocation.</t>
        <t>
   *Mechanism:*</t>
        <t>
   After the tool is invoked and a response is produced, the verifier
   (or a post-invocation hook) MUST compute:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
response_hash = "sha256:" + hashlib.sha256(response_bytes).hexdigest()
]]></artwork>
        <t>
   Where response_bytes is the exact byte representation of the tool's
   response.  For string responses, this is the UTF-8 encoding.  For
   structured responses, the canonical JSON serialization MUST be used.</t>
        <t>
   *Binding strength:* The response_hash enables a downstream consumer
   to verify that the response they received is the same response that
   was observed at verification time.  If the response is modified in
   transit, the hash will not match.</t>
        <t>
   *Timing:* The response_hash MUST be computed after the tool
   invocation completes.  It is appended to the receipt as a post-hoc
   binding, not part of the pre-invocation signed bytes.  If the
   response_hash is absent, the receipt still covers the pre-invocation
   decision but does not bind to the response.</t>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.3" numbered="true" toc="default">
        <name>runtime_context_hash Binding</name>
        <t>
   *Purpose:* Bind the receipt to the runtime execution environment.</t>
        <t>
   *Mechanism:*</t>
        <t>
   The runtime context is a JSON object containing environment-scoped
   fields:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
{
  "caller_identity": "agent-001",
  "tenant": "acme-corp",
  "environment": "production",
  "process_id": 12345,
  "hostname": "worker-42",
  "network_namespace": "ns-prod"
}

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()
]]></artwork>
        <t>
   *Binding strength:* Prevents a receipt issued in one environment
   (e.g., staging) from being replayed in another (e.g., production).
   The receipt is bound to the exact runtime context in which
   verification occurred.</t>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.4" numbered="true" toc="default">
        <name>action Binding</name>
        <t>
   *Purpose:* Provide a CAID-compatible action identifier that enables
   the CCS receipt to participate in the AEB's CAID matching process
   (<xref target="I-D.schrock-action-evidence-boundary" format="default"/> <xref target="sect-5.3" format="default"/>).</t>
        <t>
   *Mechanism:*</t>
        <t>
   The action field is a string identifier constructed as:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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.
]]></artwork>
        <t>
   For AEC integration, this action identifier MUST be mappable to a
   CAID action-type definition via a relying-party-pinned mapping
   profile (<xref target="I-D.schrock-action-evidence-boundary" format="default"/> <xref target="sect-5.3" format="default"/>).  The mapping profile MUST specify:</t>
        <ul spacing="normal">
          <li>
            <t>The source media type: "application/x-ccs-receipt+json"</t>
          </li>
          <li>
            <t>The target action type: the CAID action-type definition</t>
          </li>
          <li>
            <t>The material fields: tool, params (via params_hash), and any
      additional fields the relying party considers material.</t>
          </li>
        </ul>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.5" numbered="true" toc="default">
        <name>params_hash Binding</name>
        <t>
   *Purpose:* Bind the receipt to the exact tool parameters.</t>
        <t>
   *Mechanism (Implemented in 0.2.0):*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
def params_hash(params: dict) -> str:
    return hashlib.sha256(
        json.dumps(params, sort_keys=True, separators=(",", ":")).encode()
    ).hexdigest()[:16]
]]></artwork>
        <t>
   The 0.2.0 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.</t>
        <t>
   *Recommendation:* Implementations targeting conformance Level 2 or
   above (<xref target="sect-9" format="default"/>) SHOULD use the full 64-character SHA-256 hex digest
   for params_hash.</t>
        <t>
   *Status:* Implemented (16-char truncation) in 0.2.0.</t>
      </section>
      <section anchor="sect-5.6" numbered="true" toc="default">
        <name>issuer Binding</name>
        <t>
   *Purpose:* Identify the verifier that produced the receipt, enabling
   the AEB to dispatch verification to the correct native verifier
   adapter (<xref target="I-D.schrock-action-evidence-boundary" format="default"/> <xref target="sect-5.2" format="default"/>).</t>
        <t>
   *Mechanism:*</t>
        <t>
   The issuer field is a URI that identifies the verifier instance:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
issuer = "https://verifier.example.com/ccs/v1"
]]></artwork>
        <t>
   The URI MUST be stable across key rotations.  The issuer identifier
   MUST be covered by the signing mechanism (HMAC or Ed25519) so that an
   attacker cannot substitute a different issuer without invalidating
   the receipt.</t>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.7" numbered="true" toc="default">
        <name>audience Binding</name>
        <t>
   *Purpose:* Identify the intended consumer of the receipt, preventing
   receipt misdirection (<xref target="I-D.schrock-action-evidence-boundary" format="default"/> <xref target="sect-4" format="default"/>).</t>
        <t>
   *Mechanism:*</t>
        <t>
   The audience field is a URI that identifies the intended recipient:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
audience = "https://executor.example.com/aeb"
]]></artwork>
        <t>
   A receipt whose audience does not match the consumer MUST be
   rejected.  This prevents an attacker from presenting a receipt
   intended for one executor to a different executor.</t>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.8" numbered="true" toc="default">
        <name>nonce/sequence Binding</name>
        <t>
   *Purpose:* Prevent replay of previously-issued receipts.</t>
        <t>
   *Mechanism:*</t>
        <t>
   The nonce field is a cryptographically random, single-use value:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
nonce = secrets.token_hex(16)
]]></artwork>
        <t>
   The sequence field is a monotonically increasing integer counter
   scoped to the issuer:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
sequence = atomic_increment(per_issuer_counter)

*Anti-replay enforcement:*
]]></artwork>
        <ul spacing="normal">
          <li>
            <t>The consumer MUST maintain a replay cache of seen (issuer, nonce)
      pairs.</t>
          </li>
          <li>
            <t>A receipt with a previously-seen nonce MUST be rejected.</t>
          </li>
          <li>
            <t>The replay cache MUST persist across process restarts.</t>
          </li>
          <li>
            <t>The sequence field provides an additional ordering guarantee: a
      receipt with sequence number N MUST NOT be accepted if a receipt
      with sequence number N+1 has already been accepted from the same
      issuer.</t>
          </li>
        </ul>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
      <section anchor="sect-5.9" numbered="true" toc="default">
        <name>freshness Binding (issued_at + expires_at + max_clock_skew)</name>
        <t>
   *Purpose:* Bound the temporal validity of the receipt.</t>
        <t>
   *Mechanism:*</t>
        <t>
   Three fields work together to enforce freshness:</t>
        <ul spacing="normal">
          <li>
            <t>issued_at: Unix timestamp when the receipt was issued.</t>
          </li>
          <li>
            <t>expires_at: Unix timestamp after which the receipt MUST NOT be
      accepted.</t>
          </li>
          <li>
            <t>max_clock_skew: Maximum acceptable clock skew in seconds between
      the issuer and the consumer.  Default: 30.0 seconds.</t>
          </li>
        </ul>
        <t>
   *Validation algorithm:*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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
]]></artwork>
        <t>
   *Recommended expiry:* For tool-invocation governance, an expires_at
   of issued_at + 60 seconds is RECOMMENDED.  The exact window depends
   on the risk profile of the action being governed.</t>
        <t>
   *Status:* Specified, not yet implemented.</t>
      </section>
    </section>
    <section anchor="sect-6" numbered="true" toc="default">
      <name>Key Management</name>
      <section anchor="sect-6.1" numbered="true" toc="default">
        <name>Key Generation</name>
        <t>
   *HMAC-SHA256 (Implemented in 0.2.0):*</t>
        <t>
   The signing key MUST be generated using a cryptographically secure
   random number generator:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
signing_key = secrets.token_bytes(32)
]]></artwork>
        <t>
   The key MUST NOT be derived from predictable sources (timestamps,
   process IDs, weak PRNGs).</t>
        <t>
   *Ed25519 (Specification, for cross-domain verification):*</t>
        <t>
   For deployments where the verifier and consumer are in separate trust
   domains (e.g., out-of-process verification across organizational
   boundaries), Ed25519 key pairs SHOULD be used:</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
    Ed25519PrivateKey, Ed25519PublicKey
)
private_key = Ed25519PrivateKey.generate()
public_key = private_key.public_key()
]]></artwork>
        <t>
   The private key signs the receipt; the public key is distributed out-
   of-band for verification.  This prevents the consumer from forging
   receipts.</t>
      </section>
      <section anchor="sect-6.2" numbered="true" toc="default">
        <name>Key Enrollment</name>
        <t>
   Before a CCS receipt can be verified by a consumer, the consumer MUST
   have enrolled the verifier's key:</t>
        <ul spacing="normal">
          <li>
            <t>*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.</t>
          </li>
          <li>
            <t>*Ed25519:* The public key MUST be pinned by the consumer via an
      out-of-band mechanism (configuration file, environment variable,
      or trust anchor store).  The key fingerprint (SHA-256 of the raw
      public key bytes) SHOULD be used as the signing_key_id.</t>
          </li>
        </ul>
      </section>
      <section anchor="sect-6.3" numbered="true" toc="default">
        <name>Key Rotation</name>
        <t>
   Key rotation MUST follow a phased approach:</t>
        <ol spacing="normal" type="1"><li>
            <t>*Announce:* The verifier publishes the new key identifier and
       effective date.</t>
          </li>
          <li>
            <t>*Dual-sign:* During the overlap window, receipts are signed with
       both old and new keys.  The receipt field contains the signature
       under the current key; an additional receipt_prev field contains
       the signature under the previous key.</t>
          </li>
          <li>
            <t>*Cutover:* After the overlap window, only the new key is used.</t>
          </li>
          <li>
            <t>*Retire:* The old key is retired after all receipts signed under
       it have expired (per expires_at).</t>
          </li>
        </ol>
        <t>
   The overlap window MUST be at least 2x the maximum receipt lifetime
   (expires_at - issued_at) to ensure no unexpired receipts are left
   without a valid key.</t>
      </section>
      <section anchor="sect-6.4" numbered="true" toc="default">
        <name>Verifier-Operator Separation</name>
        <t>
   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).</t>
        <t>
   *In-process (0.2.0):* 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 documented in the reference implementation.</t>
        <t>
   *Out-of-process (Specified):* 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.</t>
      </section>
    </section>
    <section anchor="sect-7" numbered="true" toc="default">
      <name>Transport Requirements</name>
      <section anchor="sect-7.1" numbered="true" toc="default">
        <name>Fail-Closed Semantics</name>
        <t>
   CCS enforcement MUST be fail-closed.  This is the core security
   guarantee, derived from the formal framework <xref target="CCS-FORMAL" format="default"/> and
   implemented in the reference implementation.</t>
        <t>
   *Requirement:* If the verifier raises an exception, times out,
   returns an ambiguous result, or is unreachable, the tool invocation
   MUST be blocked.</t>
        <t>
   *Implementation in 0.2.0:*</t>
        <t>
   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.</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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
]]></artwork>
        <t>
   This is the fundamental difference from observer-pattern hooks (which
   fail-open when the observer crashes, as documented in <xref target="CWE-636" format="default"/>).</t>
      </section>
      <section anchor="sect-7.2" numbered="true" toc="default">
        <name>Timeout Behavior</name>
        <t>
   *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.</t>
        <t>
   *Receipt timeout:* The expires_at field (<xref target="sect-5.9" format="default"/>) provides an
   upper bound on receipt validity.  A receipt that has expired MUST be
   rejected even if all other fields are valid.</t>
        <t>
   *Combined:* Both timeouts are independent and both MUST be enforced.
   A receipt that was issued quickly but has since expired MUST still be
   rejected.</t>
      </section>
      <section anchor="sect-7.3" numbered="true" toc="default">
        <name>In-Process vs Out-of-Process Deployment</name>
        <!--
   draft-correctover-ccs-00-fixed.txt(1135): Warning: Unexpected title: expected
   'Figure ...', found 'Table 3'.  This looks like a figure that has been
   entered as a texttable.  The generated XML will need adjustment.
   -->
        <figure anchor="le-3">
          <artwork name="" type="" align="left" alt=""><![CDATA[
   +==================+==================+=======================+
   | 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          | ~6µs P50         | ~100µs P50 (gRPC      |
   |                  | (Python)         | overhead)             |
   +------------------+------------------+-----------------------+
   | Implementation   | Implemented      | Stub (gRPC transport  |
   | status           | (0.2.0)          | not yet implemented)  |
   +------------------+------------------+-----------------------+
   | Conformance      | Level 0-1        | Level 2-3             |
   | level            |                  |                       |
   +------------------+------------------+-----------------------+
]]></artwork>
        </figure>
        <t>
   *Note on performance:* The P50/P99 latency figures above are
   informational measurements from the reference implementation and
   early deployments.  They are not normative requirements.  Actual
   latency depends on hardware, operating system, rule complexity, and
   deployment configuration.</t>
      </section>
    </section>
    <section anchor="sect-8" numbered="true" toc="default">
      <name>Verifier Source Classification</name>
      <t>
   This section classifies verifiers by their provenance and the level
   of assurance a relying party SHOULD place in their verdicts.</t>
      <section anchor="sect-8.1" numbered="true" toc="default">
        <name>Class A: Self-Built Verifier</name>
        <t>
   A verifier whose rule set, signing key, and configuration are built,
   operated, and audited entirely by the relying party's own
   organization.</t>
        <t>
   *Properties:*</t>
        <ul spacing="normal">
          <li>
            <t>The relying party has full visibility into the source code, rule
      definitions, and signing key management.</t>
          </li>
          <li>
            <t>The relying party can independently verify that the verifier
      implements fail-closed semantics correctly.</t>
          </li>
          <li>
            <t>The signing key is generated and held entirely within the relying
      party's infrastructure.</t>
          </li>
        </ul>
        <t>
   *Assurance:* Highest.  The relying party trusts the verifier because
   they built it and control every aspect of its operation.</t>
        <t>
   *AEC mapping:* A Class A verifier's receipt is directly consumable as
   a policy-permit evidence component in an AEC, subject to CAID
   matching.</t>
      </section>
      <section anchor="sect-8.2" numbered="true" toc="default">
        <name>Class B: Audited Verifier</name>
        <t>
   A verifier built by a third party but subjected to independent audit
   by the relying party or a trusted auditor.</t>
        <t>
   *Properties:*</t>
        <ul spacing="normal">
          <li>
            <t>The source code is available for review (may be open source or
      provided under NDA).</t>
          </li>
          <li>
            <t>An independent audit has verified that the implementation matches
      the specification, particularly fail-closed semantics and signing
      correctness.</t>
          </li>
          <li>
            <t>The audit report is available to the relying party.</t>
          </li>
        </ul>
        <t>
   *Assurance:* High, contingent on audit scope and freshness.  The
   relying party trusts the verifier because a trusted auditor has
   verified it.</t>
        <t>
   *AEC mapping:* A Class B verifier's receipt is consumable as a
   policy-permit evidence component, but the relying party SHOULD
   document the audit basis and any residual risk.</t>
      </section>
      <section anchor="sect-8.3" numbered="true" toc="default">
        <name>Class C: Self-Declared Verifier</name>
        <t>
   A verifier whose operator claims conformance to CCS but has not
   submitted to independent audit.</t>
        <t>
   *Properties:*</t>
        <ul spacing="normal">
          <li>
            <t>The operator declares that the verifier implements CCS correctly.</t>
          </li>
          <li>
            <t>No independent verification of the implementation has occurred.</t>
          </li>
          <li>
            <t>The relying party has no direct evidence that fail-closed
      semantics are correctly implemented.</t>
          </li>
        </ul>
        <t>
   *Assurance:* Low.  The relying party trusts the verifier based on the
   operator's assertion alone.</t>
        <t>
   *AEC mapping:* A Class C verifier's receipt MAY be consumed as
   evidence, but the relying party's AEC requirement SHOULD weight it
   lower than Class A or Class B.  A relying party with a strict
   evidence policy MAY reject Class C receipts entirely.</t>
      </section>
    </section>
    <section anchor="sect-9" numbered="true" toc="default">
      <name>Conformance Levels</name>
      <section anchor="sect-9.1" numbered="true" toc="default">
        <name>Level 0-3 Definitions</name>
        <t>
   *Level 0 — Baseline (Implemented in 0.2.0):*</t>
        <t>
   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.</t>
        <t>
   A Level 0 implementation satisfies the core tamper-evident audit
   requirement but does not provide the bindings necessary for AEC
   composition.</t>
        <t>
   *Level 1 — Extended Bindings:*</t>
        <t>
   In addition to Level 0, the verifier produces receipts with:
   request_hash, config_hash.  The receipt binds to the exact request
   bytes and the exact configuration.</t>
        <t>
   A Level 1 implementation provides stronger replay resistance (via
   request_hash) and configuration binding (via config_hash).</t>
        <t>
   *Level 2 — Full AEC Readiness:*</t>
        <t>
   In addition to Level 1, the verifier produces receipts with:
   response_hash, runtime_context_hash, action (CAID-compatible),
   issuer, audience, nonce, sequence, issued_at, expires_at,
   max_clock_skew.  Ed25519 signing is supported as an alternative to
   HMAC.</t>
        <t>
   A Level 2 implementation provides all bindings necessary for the
   receipt to be consumed as a native artifact by an AEB per <xref target="I-D.schrock-action-evidence-boundary" format="default"/>.</t>
        <t>
   *Level 3 — Out-of-Process with Full Isolation:*</t>
        <t>
   In addition to Level 2, the verifier operates out-of-process with a
   fully implemented gRPC transport.  The signing key is held only in
   the verifier process.  Cross-replica replay prevention is implemented
   via shared durable state.</t>
        <t>
   A Level 3 implementation provides the strongest security guarantees:
   process isolation, key protection, and distributed replay prevention.</t>
      </section>
      <section anchor="sect-9.2" numbered="true" toc="default">
        <name>Conformance Vectors (Positive Test Cases)</name>
        <t>
   Each conformance vector is a self-contained test case that a
   conforming implementation MUST pass.</t>
        <t>
   *CV-001: Basic ALLOW receipt (Level 0)*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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
]]></artwork>
        <t>
   *CV-002: Basic DENY receipt (Level 0)*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
Input Command:
  agent_id: "test-agent"
  tool: "shell_exec"
  params: {"command": "rm -rf /"}
]]></artwork>
        <dl newline="true" spacing="normal" indent="2">
          <dt>Expected:</dt>
          <dd>
	verdict: "deny"
     block_reason: non-empty, describes the RCE pattern
     receipt: valid HMAC-SHA256 covering the deny verdict
     rule_summary: contains rce_protection=deny
	</dd>
        </dl>
        <t>
   *CV-003: Config hash binding (Level 1)*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
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.
]]></artwork>
      </section>
    </section>
    <section anchor="sect-10" numbered="true" toc="default">
      <name>Negative Test Cases</name>
      <t>
   This section defines eight 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.</t>
      <t>
   These negative cases are designed to demonstrate that the bindings
   defined in <xref target="sect-5" format="default"/> are load-bearing: removing any one binding
   reopens the corresponding attack.</t>
      <section anchor="sect-10.1" numbered="true" toc="default">
        <name>Changed Arguments</name>
        <t>
   *Scenario:* An attacker intercepts a valid receipt for tool
   invocation A, modifies the tool parameters, and presents the modified
   command with the original receipt.</t>
        <t>
   *Expected receipt change:* The params_hash (and request_hash at Level
   1+) in the receipt no longer matches the modified parameters.  The
   HMAC verification fails because the signed bytes have changed.</t>
        <t>
   *Expected verdict:* DENY.</t>
        <t>
   *Detection condition:*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
recomputed_params_hash = compute_params_hash(modified_params)
assert recomputed_params_hash != receipt.params_hash

*Binding defeated if removed:* params_hash binding (Section 5.5).
]]></artwork>
      </section>
      <section anchor="sect-10.2" numbered="true" toc="default">
        <name>Changed Response</name>
        <t>
   *Scenario:* An attacker intercepts the tool's response, modifies it,
   and presents the modified response to the downstream consumer along
   with the original receipt.</t>
        <t>
   *Expected receipt change:* The response_hash in the receipt no longer
   matches the modified response bytes.</t>
        <t>
   *Expected verdict:* The receipt is still valid for the pre-invocation
   decision (the verifier did allow the invocation), but the downstream
   consumer detects the response mismatch and rejects the response.</t>
        <t>
   *Detection condition:*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
recomputed_response_hash = hashlib.sha256(modified_response_bytes).hexdigest()
assert recomputed_response_hash != receipt.response_hash
*Binding defeated if removed:* response_hash binding (Section 5.2).
]]></artwork>
        <t>
   *Note:* This is a post-invocation binding.  The 0.2.0 implementation
   does not include response_hash; the attack is undetectable at Level
   0.</t>
      </section>
      <section anchor="sect-10.3" numbered="true" toc="default">
        <name>Replay</name>
        <t>
   *Scenario:* An attacker captures a valid receipt for a one-time
   action (e.g., "transfer $100 to account X") and replays it to
   authorize a second invocation of the same action.</t>
        <t>
   *Expected receipt change:* The nonce in the receipt has already been
   recorded in the consumer's replay cache.</t>
        <t>
   *Expected verdict:* DENY (replay detected).</t>
        <t>
   *Detection condition:*</t>
        <dl newline="true" spacing="normal" indent="4">
          <dt>if (receipt.issuer, receipt.nonce) in replay_cache:</dt>
          <dd>
	return DENY
	</dd>
        </dl>
        <t>
   *Binding defeated if removed:* nonce/sequence binding (<xref target="sect-5.8" format="default"/>).</t>
        <t>
   *Note:* The 0.2.0 implementation does not include nonce; replay
   detection is not possible at Level 0 except via the in-process audit
   log (which is not durable across restarts).</t>
      </section>
      <section anchor="sect-10.4" numbered="true" toc="default">
        <name>Stale Evidence</name>
        <t>
   *Scenario:* An attacker presents a receipt that was issued 24 hours
   ago for a time-sensitive action (e.g., a stock trade).</t>
        <t>
   *Expected receipt change:* The expires_at field in the receipt has
   passed.</t>
        <t>
   *Expected verdict:* DENY (expired receipt).</t>
        <t>
   *Detection condition:*</t>
        <dl newline="true" spacing="normal" indent="4">
          <dt>if current_time &gt; receipt.expires_at + receipt.max_clock_skew:</dt>
          <dd>
	return DENY
	</dd>
        </dl>
        <t>
   *Binding defeated if removed:* freshness binding (<xref target="sect-5.9" format="default"/>).</t>
        <t>
   *Note:* The 0.2.0 implementation does not include expires_at; stale
   receipts are not detectable at Level 0.</t>
      </section>
      <section anchor="sect-10.5" numbered="true" toc="default">
        <name>Wrong Audience</name>
        <t>
   *Scenario:* An attacker obtains a receipt intended for Executor A and
   presents it to Executor B, hoping that Executor B will accept it as
   authorization for the same action.</t>
        <t>
   *Expected receipt change:* The audience field in the receipt
   identifies Executor A, not Executor B.</t>
        <t>
   *Expected verdict:* DENY (audience mismatch).</t>
        <t>
   *Detection condition:*</t>
        <dl newline="true" spacing="normal" indent="4">
          <dt>if receipt.audience != expected_audience:</dt>
          <dd>
	return DENY
	</dd>
        </dl>
        <t>
   *Binding defeated if removed:* audience binding (<xref target="sect-5.7" format="default"/>).</t>
        <t>
   *Note:* The 0.2.0 implementation does not include audience; cross-
   executor receipt misdirection is not detectable at Level 0.</t>
      </section>
      <section anchor="sect-10.6" numbered="true" toc="default">
        <name>Key Substitution</name>
        <t>
   *Scenario:* An attacker generates their own signing key, produces a
   forged receipt with a fraudulent verdict, and presents it along with
   the attacker's public key.</t>
        <t>
   *Expected receipt change:* The receipt's issuer field identifies the
   attacker's key, which is not in the consumer's trust anchor store.</t>
        <t>
   *Expected verdict:* DENY (untrusted key).</t>
        <t>
   *Detection condition:*</t>
        <dl newline="true" spacing="normal" indent="4">
          <dt>if receipt.issuer not in trusted_issuers:</dt>
          <dd>
	return DENY
	</dd>
          <dt>if not verify_signature(receipt, trusted_public_keys[receipt.issuer]):</dt>
          <dd>
	return DENY
	</dd>
        </dl>
        <t>
   *Binding defeated if removed:* issuer binding (<xref target="sect-5.6" format="default"/>).</t>
        <t>
   *Note:* At Level 0 with HMAC, key substitution requires the attacker
   to know the shared secret.  At Level 2+ with Ed25519, key
   substitution is detectable via the issuer/audience bindings.</t>
      </section>
      <section anchor="sect-10.7" numbered="true" toc="default">
        <name>Verifier/Runtime Collusion</name>
        <t>
   *Scenario:* The agent runtime and the verifier are operated by the
   same entity.  The verifier is configured to always return ALLOW,
   regardless of the command.  The receipts are technically valid
   (correct HMAC, correct bindings) but the governance is meaningless.</t>
        <t>
   *Expected receipt change:* No change to the receipt itself.  The
   receipt is valid but the rule evaluation is trivially permissive.</t>
        <t>
   *Expected verdict:* The receipt passes cryptographic verification,
   but the AEB SHOULD flag the verifier as Class C (self-declared) and
   apply additional scrutiny.</t>
        <t>
   *Detection condition:*</t>
        <dl newline="true" spacing="normal" indent="4">
          <dt>if verifier.deny_rate &lt; 0.001 and verifier.total_receipts &gt; 10000:</dt>
          <dd>
	flag_for_review(verifier)
	</dd>
        </dl>
        <t>
   *Mitigation:* This is not a cryptographic attack; it is a governance
   failure.  Mitigation requires:</t>
        <ul spacing="normal">
          <li>
            <t>Independent auditing of the verifier's rule set (Class B).</t>
          </li>
          <li>
            <t>Out-of-process deployment with the verifier operated by a
      different organization than the runtime (Class A, independent
      operator).</t>
          </li>
          <li>
            <t>Statistical monitoring of verifier verdict distributions.</t>
          </li>
        </ul>
      </section>
      <section anchor="sect-10.8" numbered="true" toc="default">
        <name>Missing Transport</name>
        <t>
   *Scenario:* The verifier is configured for out-of-process deployment,
   but the gRPC transport is not implemented or is unavailable.  The
   agent runtime falls back to allowing the tool invocation without
   verification.</t>
        <t>
   *Expected receipt change:* No receipt is produced.  The tool
   invocation proceeds without governance.</t>
        <t>
   *Expected verdict:* This is a fail-open condition.  A conforming
   implementation MUST block the tool invocation (fail-closed).</t>
        <t>
   *Detection condition:*</t>
        <artwork name="" type="" align="left" alt=""><![CDATA[
try:
    result = await verifier_client.verify(command)
except ConnectionError:
    raise PermissionError("Verifier unreachable")
]]></artwork>
        <t>
   *Binding defeated if removed:* Fail-closed semantics (<xref target="sect-7.1" format="default"/>).</t>
        <t>
   *Note:* The 0.2.0 reference implementation correctly implements fail-
   closed for in-process verification.  The out-of-process transport is
   a stub that raises NotImplementedError, which is caught by the fail-
   closed wrapper and blocks the invocation.  However, a deployment that
   patches around the NotImplementedError would create a fail-open
   vulnerability.</t>
      </section>
    </section>
    <section anchor="sect-11" numbered="true" toc="default">
      <name>Security Considerations</name>
      <t>
   *Truncated hashes.* The 0.2.0 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.</t>
      <t>
   *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 but is not yet fully implemented.</t>
      <t>
   *Float timestamp precision.* The timestamp and verified_at fields use
   Python floats (IEEE 754 double precision).  This provides
   approximately 15-17 significant decimal digits, which is sufficient
   for microsecond precision at current epoch values but MAY lose
   precision for comparisons between very close timestamps.
   Implementations SHOULD use monotonic clocks (time.perf_counter()) for
   latency measurement and wall clocks (time.time()) only for issuance
   timestamps.</t>
      <t>
   *JSON canonicalization.* The params_hash and config_hash depend on
   deterministic JSON serialization.  Any difference in serialization
   (different key ordering, different whitespace handling, different
   Unicode encoding) produces a different hash.  Implementations MUST
   use the canonical serialization specified in <xref target="sect-3.2" format="default"/> and
   <xref target="sect-4.2" format="default"/>.</t>
      <t>
   *Rule evaluation ordering.* The rule_summary field records rules in
   evaluation order.  Because DENY short-circuits evaluation, two
   receipts for the same command MAY have different rule_summary values
   if the rule set changes between invocations.  The config_hash binding
   (Level 1+) addresses this by binding the receipt to the exact rule
   set.</t>
      <t>
   *TOCTOU in out-of-process mode.* Between the time the verifier
   evaluates a command and the time the agent executes the tool, the
   runtime environment may change.  The runtime_context_hash (Level 2)
   mitigates this by binding the receipt to the context at verification
   time; any subsequent context change invalidates the binding.</t>
      <t>
   *Receipt chaining.* This specification does not define receipt
   chaining (each receipt is independent).  Implementations that require
   ordered audit trails SHOULD use the sequence field (<xref target="sect-5.8" format="default"/>) or
   implement external chaining (e.g., hash-chain the receipts in the
   audit log).</t>
    </section>
    <section anchor="sect-12" numbered="true" toc="default">
      <name>IANA Considerations</name>
      <t>
   This document has no IANA actions.</t>
    </section>
  </middle>
  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="RFC2119" target="https://www.rfc-editor.org/info/rfc2119" xml:base="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml">
          <front>
            <title>Key words for use in RFCs to Indicate Requirement Levels</title>
            <author fullname="S. Bradner" initials="S." surname="Bradner"/>
            <date month="March" year="1997"/>
            <abstract>
              <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="2119"/>
          <seriesInfo name="DOI" value="10.17487/RFC2119"/>
        </reference>
        <reference anchor="RFC7493" target="https://www.rfc-editor.org/info/rfc7493" xml:base="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7493.xml">
          <front>
            <title>The I-JSON Message Format</title>
            <author fullname="T. Bray" initials="T." role="editor" surname="Bray"/>
            <date month="March" year="2015"/>
            <abstract>
              <t>I-JSON (short for "Internet JSON") is a restricted profile of JSON designed to maximize interoperability and increase confidence that software can process it successfully with predictable results.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="7493"/>
          <seriesInfo name="DOI" value="10.17487/RFC7493"/>
        </reference>
        <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174" xml:base="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml">
          <front>
            <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
            <author fullname="B. Leiba" initials="B." surname="Leiba"/>
            <date month="May" year="2017"/>
            <abstract>
              <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
            </abstract>
          </front>
          <seriesInfo name="BCP" value="14"/>
          <seriesInfo name="RFC" value="8174"/>
          <seriesInfo name="DOI" value="10.17487/RFC8174"/>
        </reference>
        <reference anchor="RFC8785" target="https://www.rfc-editor.org/info/rfc8785" xml:base="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8785.xml">
          <front>
            <title>JSON Canonicalization Scheme (JCS)</title>
            <author fullname="A. Rundgren" initials="A." surname="Rundgren"/>
            <author fullname="B. Jordan" initials="B." surname="Jordan"/>
            <author fullname="S. Erdtman" initials="S." surname="Erdtman"/>
            <date month="June" year="2020"/>
            <abstract>
              <t>Cryptographic operations like hashing and signing need the data to be expressed in an invariant format so that the operations are reliably repeatable. One way to address this is to create a canonical representation of the data. Canonicalization also permits data to be exchanged in its original form on the "wire" while cryptographic operations performed on the canonicalized counterpart of the data in the producer and consumer endpoints generate consistent results.</t>
              <t>This document describes the JSON Canonicalization Scheme (JCS). This specification defines how to create a canonical representation of JSON data by building on the strict serialization methods for JSON primitives defined by ECMAScript, constraining JSON data to the Internet JSON (I-JSON) subset, and by using deterministic property sorting.</t>
            </abstract>
          </front>
          <seriesInfo name="RFC" value="8785"/>
          <seriesInfo name="DOI" value="10.17487/RFC8785"/>
        </reference>
      </references>
      <references>
        <name>Informative References</name>
        <reference anchor="I-D.schrock-action-evidence-boundary" target="https://datatracker.ietf.org/doc/html/draft-schrock-action-evidence-boundary-03" xml:base="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.schrock-action-evidence-boundary.xml">
          <front>
            <title>The Action Evidence Boundary for Consequential Agent Effects</title>
            <author fullname="Iman Schrock" initials="I." surname="Schrock">
              <organization>EMILIA Protocol, Inc.</organization>
            </author>
            <date day="2" month="August" year="2026"/>
            <abstract>
              <t>Consequential agent actions can cross identity, transport, authorization, policy, and execution systems. Each system can produce a valid artifact while the executor still lacks a safe rule for joining the artifacts to the exact effect, consuming one-time authority, and handling an uncertain outcome. This document defines the Action Evidence Boundary (AEB), an executor-side processing model for that lifecycle. AEB requires native artifact verification, Canonical Action Identifier (CAID) matching, Authorization Evidence Chain (AEC) satisfaction, a separate local authorization decision, durable atomic consumption or reservation, invocation, closed effect outcomes, and authenticated reconciliation. It defines no receipt or token format, no policy language, no universal evidence taxonomy, and no new registry. Native workload credentials, message signatures, attested per-action tokens, permit records, authorization receipts, and status mechanisms retain their own semantics and verifiers.</t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-schrock-action-evidence-boundary-03"/>
        </reference>
        <reference anchor="CCS-FORMAL" target="https://doi.org/10.5281/zenodo.21271910">
          <front>
            <title>CCS Formal Framework</title>
            <author initials="G." surname="Wang" fullname="G. Wang">
	</author>
            <date year="2026"/>
          </front>
          <seriesInfo name="DOI" value="10.5281/zenodo.21271910"/>
        </reference>
        <reference anchor="CWE-636" target="https://cwe.mitre.org/data/definitions/636.html">
          <front>
            <title>CWE-636: Not Failing Securely (Fail Open)</title>
            <author initials="M." surname="MITRE" fullname="M. MITRE">
	</author>
            <date year="2026"/>
          </front>
        </reference>
        <reference anchor="I-D.schrock-ep-authorization-receipts" target="https://datatracker.ietf.org/doc/html/draft-schrock-ep-authorization-receipts-08" xml:base="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.schrock-ep-authorization-receipts.xml">
          <front>
            <title>Authorization Receipts for High-Risk Agent Actions</title>
            <author fullname="Iman Schrock" initials="I." surname="Schrock">
              <organization>EMILIA Protocol, Inc.</organization>
            </author>
            <date day="21" month="July" year="2026"/>
            <abstract>
              <t>This document defines the EMILIA Protocol (EP) authorization receipt, an evidence artifact binding an enrolled approver key to one canonical action before execution. An approver-held key signs an Authorization Context containing the action hash, policy reference, nonce, audience, and validity window. A Trust Receipt carries the signed contexts, terminal consumption record, and Merkle inclusion material so a relying party can verify the recorded event offline under independently selected log, directory, policy, and approver trust inputs. The receipt establishes only the guarantees of the selected verification profile. The mapping from an enrolled approver identifier to a natural person is asserted by the directory authority. Offline verification does not establish current revocation status, global non-replay, comprehension, legality, safety, or execution. Replay prevention requires an online atomic consumption store at the executor. The state-machine invariants are machine-checked under the assumptions stated in this document.</t>
            </abstract>
          </front>
          <seriesInfo name="Internet-Draft" value="draft-schrock-ep-authorization-receipts-08"/>
        </reference>
      </references>
    </references>
    <section anchor="sect-a" numbered="true" toc="default">
      <name>CCS v1.2 Receipt Field Summary Table</name>
      <!--
   draft-correctover-ccs-00-fixed.txt(1761): Warning: Unexpected title: expected
   'Figure ...', found 'Table 4'.  This looks like a figure that has been
   entered as a texttable.  The generated XML will need adjustment.
   -->
      <figure anchor="le-4">
        <artwork name="" type="" align="left" alt=""><![CDATA[
+==+====================+=======+=====+===========+======+============+
|# |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    |Specified  |Yes   |SHA-256 of  |
|  |                    |string |     |           |      |full request|
+--+--------------------+-------+-----+-----------+------+------------+
|11|config_hash         |hex    |1    |Specified  |Yes   |SHA-256 of  |
|  |                    |string |     |           |      |canonical   |
|  |                    |       |     |           |      |config      |
+--+--------------------+-------+-----+-----------+------+------------+
|12|response_hash       |hex    |2    |Specified  |Post- |SHA-256 of  |
|  |                    |string |     |           |hoc   |response    |
+--+--------------------+-------+-----+-----------+------+------------+
|13|runtime_context_hash|hex    |2    |Specified  |Yes   |SHA-256 of  |
|  |                    |string |     |           |      |runtime     |
|  |                    |       |     |           |      |context     |
+--+--------------------+-------+-----+-----------+------+------------+
|14|action              |string |2    |Specified  |Yes   |CAID-       |
|  |                    |       |     |           |      |compatible  |
|  |                    |       |     |           |      |action ID   |
+--+--------------------+-------+-----+-----------+------+------------+
|15|issuer              |URI    |2    |Specified  |Yes   |Verifier    |
|  |                    |string |     |           |      |identifier  |
+--+--------------------+-------+-----+-----------+------+------------+
|16|audience            |URI    |2    |Specified  |Yes   |Intended    |
|  |                    |string |     |           |      |consumer    |
+--+--------------------+-------+-----+-----------+------+------------+
|17|nonce               |hex    |2    |Specified  |Yes   |Single-use  |
|  |                    |string |     |           |      |anti-replay |
+--+--------------------+-------+-----+-----------+------+------------+
|18|sequence            |integer|2    |Specified  |Yes   |Monotonic   |
|  |                    |       |     |           |      |counter     |
+--+--------------------+-------+-----+-----------+------+------------+
|19|issued_at           |float  |2    |Specified  |Yes   |Issue       |
|  |                    |       |     |           |      |timestamp   |
+--+--------------------+-------+-----+-----------+------+------------+
|20|expires_at          |float  |2    |Specified  |Yes   |Expiry      |
|  |                    |       |     |           |      |timestamp   |
+--+--------------------+-------+-----+-----------+------+------------+
|21|max_clock_skew      |float  |2    |Specified  |Yes   |Clock       |
|  |                    |       |     |           |      |tolerance   |
|  |                    |       |     |           |      |(seconds)   |
+--+--------------------+-------+-----+-----------+------+------------+
]]></artwork>
      </figure>
    </section>
    <section anchor="sect-b" numbered="true" toc="default">
      <name>Conformance Test Vector Format</name>
      <t>
   Each conformance test vector is encoded as a JSON object:</t>
      <artwork name="" type="" align="left" alt=""><![CDATA[
{
  "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 5.5)"
  }
}
]]></artwork>
    </section>
  </back>
</rfc>
