Evidence Carrier Contract
The Evidence Carrier Contract defines a universal interface for carrying verifiable evidence across different transports. Instead of coupling receipt delivery to a single protocol, the contract provides a common type (PeacEvidenceCarrier) and adapter pattern (CarrierAdapter) that work across MCP, A2A, ACP, UCP, x402, and HTTP.
The problem
Verifiable interaction records need to travel across many different protocols. An MCP tool response carries data differently from an A2A task status, which differs from an HTTP header. Without a common interface, every transport needs custom receipt handling logic, and evidence becomes non-portable.
The Evidence Carrier Contract solves this by defining one type that all transports can produce and consume.
PeacEvidenceCarrier
The core type that represents portable evidence. Defined in @peac/kernel (Layer 0, zero runtime dependencies).
interface PeacEvidenceCarrier {
receipt_ref: string; // sha256(receipt_jws) -- integrity anchor
receipt_jws: string; // Compact JWS (EdDSA, Ed25519)
receipt_url?: string; // Optional HTTPS-only locator hint (max 2048 chars)
policy_binding?: string; // Policy hash at issuance time
issued_at?: string; // ISO 8601 timestamp
carrier_version?: string; // Carrier format version
}
Key properties
receipt_refis alwayssha256(receipt_jws). Verifiers MUST check this integrity constraint at extraction time.receipt_jwsis the signed JWS artifact -- the actual receipt.receipt_urlis a locator hint only. It MUST NOT trigger implicit fetch (SSRF prevention). Callers who fetch the URL MUST verifysha256(fetched_jws) === receipt_ref.policy_bindingrecords which policy was in effect when the receipt was issued. Full policy binding evaluation is a Wire 0.2 feature.
CarrierAdapter pattern
Each transport implements a CarrierAdapter<TInput, TOutput> with three operations:
interface CarrierAdapter<TInput, TOutput> {
extract(input: TInput): PeacEvidenceCarrier | null;
attach(output: TOutput, carrier: PeacEvidenceCarrier): TOutput;
validateConstraints?(carrier: PeacEvidenceCarrier): boolean;
}
extractpulls a carrier from a protocol-specific container (e.g., MCP_meta, A2Ametadata)attachplaces a carrier into a protocol-specific containervalidateConstraintschecks transport-specific limits (e.g., 64 KB embed for MCP)
Transport implementations
Each transport places evidence in a protocol-appropriate location:
| Transport | Placement | Size limit | Package |
|---|---|---|---|
| MCP | _meta["org.peacprotocol/receipt_ref"] + _meta["org.peacprotocol/receipt_jws"] | 64 KB embed | @peac/mappings-mcp |
| A2A | metadata[extensionURI].carriers[] | 64 KB embed | @peac/mappings-a2a |
| ACP | Custom headers | 8 KB header | @peac/mappings-acp |
| UCP | Webhook payload | 64 KB embed | @peac/mappings-ucp |
| x402 | Response headers/body | 8 KB header | @peac/adapter-x402 |
| HTTP | PEAC-Receipt header (compact JWS) | 8 KB header | @peac/middleware-express |
The same receipt can be carried across any of these transports without modification. The carrier adapter handles placement and extraction; the receipt itself is identical regardless of transport.
Receipt reference integrity
The receipt_ref field is the SHA-256 hash of receipt_jws. This is verified at extraction time:
import { computeReceiptRef } from '@peac/schema';
const carrier = extractMcpCarrier(toolResponse);
const expectedRef = computeReceiptRef(carrier.receipt_jws);
if (carrier.receipt_ref !== expectedRef) {
throw new Error('Receipt reference integrity check failed');
}
This prevents tampering: if a transport modifies the JWS during delivery, the reference check will fail.
Discovery
Agents and servers discover PEAC support through a 3-step process:
- Agent Card (A2A): check
capabilities.extensions[]forhttps://www.peacprotocol.org/ext/traceability/v1 - Well-known: check
/.well-known/peac-issuer.jsonfor issuer configuration - Header probe: check for
PEAC-Receiptheader in HTTP responses