| Internet-Draft | Acta Signed Receipts | March 2026 |
| Farley | Expires 27 September 2026 | [Page] |
This document defines a portable, cryptographically signed receipt format for recording machine-to-machine access control decisions. Each receipt captures the identity of the decision maker, the tool or resource being accessed, the policy evaluation result, and a timestamp — all signed with Ed25519 [RFC8032] and serialized using deterministic JSON canonicalization [RFC8785].¶
The format is designed for environments where AI agents invoke tools on behalf of human operators, particularly the Model Context Protocol (MCP) ecosystem. Receipts are independently verifiable without contacting the issuer, enabling offline audit, regulatory compliance, and cross-organizational trust federation.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 27 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
As AI agents increasingly act autonomously — invoking tools, accessing APIs, and modifying state — there is a growing need for cryptographic evidence of what decisions were made, by whom, and under what policy.¶
Current approaches rely on centralized logging (e.g., CloudWatch, SIEM ingestion), which requires trust in the log operator and provides no independent verifiability. A compromised or malicious operator can silently alter or omit log entries.¶
This specification defines a Signed Decision Receipt format that provides:¶
Portable evidence: Receipts are self-contained JSON objects that can be stored, transmitted, and verified independently.¶
Cryptographic integrity: Each receipt is signed using Ed25519 (RFC 8032), ensuring tamper detection without PKI infrastructure.¶
Offline verification: Any party with the issuer's public key can verify a receipt without network access or API calls.¶
Minimal disclosure: Receipts capture the decision metadata (tool name, decision, tier, timestamp) without logging raw request payloads, prompts, or sensitive parameters.¶
The Model Context Protocol [MCP] defines a JSON-RPC transport for AI tool invocation but provides no built-in access control, auditing, or accountability mechanism. This specification is designed to be deployed at the MCP transport layer (typically as a stdio proxy) without modifications to the MCP protocol itself.¶
This specification is complementary to [I-D.serra-mcp-discovery-uri], which defines the mcp:// URI scheme and server discovery mechanism. Discovery (how an agent finds a server) and accountability (what gets recorded after the agent uses it) are independently deployable layers. A server's discovery manifest MAY declare a trust_class that informs receipt-generating proxies of the server's operating context (e.g., "regulated"), enabling jurisdiction-aware policy evaluation without encoding legal regime information in the receipt itself.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
A Signed Decision Receipt is a JSON object with two top-level fields:
payload and signature.¶
{
"payload": { ... },
"signature": {
"alg": "EdDSA",
"kid": "<issuer-identifier>",
"sig": "<hex-encoded-ed25519-signature>"
}
}
¶
The signature algorithm. MUST be "EdDSA" for Ed25519 as defined in RFC 8032.¶
The key identifier of the signing key. This is an opaque string
that SHOULD resolve to a public key via a well-known endpoint
or out-of-band distribution. The RECOMMENDED format is
sb:issuer:<base58-fingerprint> where the fingerprint is the
first 12 characters of the Base58-encoded Ed25519 public key.¶
The Ed25519 signature over the canonicalized payload, encoded as a lowercase hexadecimal string (128 characters for 64 bytes).¶
The payload is a JSON object whose schema depends on the receipt type. All payload types share the following common fields:¶
A namespaced string identifying the receipt type.
Examples: "protectmcp:decision", "protectmcp:restraint",
"blindllm:arena-battle".¶
ISO 8601 timestamp [RFC3339] of when the receipt was created. MUST include timezone designator (typically "Z" for UTC).¶
The identifier of the entity that issued and signed the receipt.
MUST match the kid field in the signature object.¶
This specification defines four receipt types. Implementations MAY
define additional types using the namespaced type field.¶
Type: protectmcp:decision¶
Records the outcome of a policy evaluation for a tool invocation.¶
{
"type": "protectmcp:decision",
"tool_name": "delete_database",
"decision": "deny",
"reason": "tier_insufficient",
"agent_tier": "signed-known",
"required_tier": "privileged",
"policy_digest": "sha256:a8f3...c91e",
"session_id": "ses_7f8a2b",
"issued_at": "2026-03-22T14:32:04.102Z",
"issuer_id": "sb:issuer:4Kpm7Q3wXx2b"
}
¶
The name of the tool being invoked, as declared in the MCP
tools/list response.¶
The policy evaluation result. One of: "allow", "deny",
"rate_limit".¶
A machine-readable reason code for the decision.
Examples: "tier_insufficient", "rate_exceeded",
"policy_block", "agent_refusal".¶
The trust tier of the requesting agent at the time of the
decision. One of: "unknown", "signed-known",
"evidenced", "privileged".¶
The minimum trust tier required by the policy for this tool.¶
A content-addressable hash of the policy document in effect
at the time of the decision. Format: "sha256:<hex>".¶
An opaque identifier for the MCP session. MUST NOT contain PII or be correlatable across sessions unless the operator explicitly configures session binding.¶
Type: protectmcp:restraint¶
Records an agent's interaction with a policy boundary, specifically whether the agent attempted to use a restricted tool and whether the restriction was enforced by an external policy or self-imposed.¶
{
"type": "protectmcp:restraint",
"agent_id": "sb:agent:8xKm3Qw2Yb1c",
"agent_manifest_version": "1.2.0",
"tool_name": "rm_rf",
"decision": "deny",
"denial_type": "policy-block",
"issued_at": "2026-03-22T14:35:12.441Z",
"issuer_id": "sb:issuer:4Kpm7Q3wXx2b"
}
¶
The identifier of the agent whose tool call was evaluated.¶
The semantic version of the agent's manifest at the time of the decision.¶
The tool that was called or attempted.¶
One of: "allow", "deny".¶
If the decision is "deny", indicates whether the denial was
imposed by external policy ("policy-block") or self-imposed
by the agent ("agent-refusal").¶
Type: blindllm:arena-battle¶
Records the outcome of a competitive evaluation between two AI agents, as conducted by a neutral arena platform.¶
{
"type": "blindllm:arena-battle",
"battle_id": "bat_9x8f7a2b",
"lane_id": "lane_creative_writing",
"agent_a": {
"id": "sb:agent:3mK9pQ7wXx2b",
"manifest_version": "2.1.0"
},
"agent_b": {
"id": "sb:agent:8xKm3Qw2Yb1c",
"manifest_version": "1.4.0"
},
"winner": "A",
"issued_at": "2026-03-22T15:00:00.000Z",
"issuer_id": "sb:issuer:4Kpm7Q3wXx2b"
}
¶
Unique identifier for the battle instance.¶
The evaluation category or "lane" in which the battle occurred.¶
Objects identifying each participant, containing:
- id: The agent's passport identifier.
- manifest_version: The agent's manifest version at battle time.¶
One of: "A", "B", "tie".¶
Type: blindllm:formal-debate¶
Records the outcome of a structured debate with audience and judge scoring. Extends the arena battle format with additional governance fields.¶
{
"type": "blindllm:formal-debate",
"debate_id": "dbt_4f2a8c",
"spec_id": "spec_ai_safety_v2",
"lane_id": "lane_policy",
"artifact_hash": "sha256:b3c4d5e6...",
"resolution_hash": "sha256:f7a8b9c0...",
"pro": {
"id": "sb:agent:3mK9pQ7wXx2b",
"manifest_version": "2.1.0"
},
"con": {
"id": "sb:agent:8xKm3Qw2Yb1c",
"manifest_version": "1.4.0"
},
"audience_winner": "pro",
"judge_winner": "pro",
"restraint_result": "clean",
"issued_at": "2026-03-22T16:30:00.000Z",
"issuer_id": "sb:issuer:4Kpm7Q3wXx2b"
}
¶
Construct the payload object with all required fields.¶
Canonicalize the payload using JCS [RFC8785]. The canonical form is a deterministic JSON serialization with sorted keys and no whitespace.¶
Convert the canonical JSON string to a UTF-8 byte sequence.¶
Sign the byte sequence using Ed25519 [RFC8032] with the issuer's secret key.¶
Encode the signature as a lowercase hexadecimal string.¶
Construct the signed envelope by wrapping the original (non-canonicalized) payload with the signature object.¶
Extract the payload and signature from the envelope.¶
Convert the canonical JSON to a UTF-8 byte sequence.¶
Resolve the public key using the kid field in the signature.
The RECOMMENDED resolution mechanism is a JWK Set endpoint
[RFC7517] at /.well-known/acta-keys.json.¶
Verify the Ed25519 signature over the canonical bytes using the resolved public key.¶
If verification succeeds, the receipt is authentic and has not been tampered with. If verification fails, the receipt MUST be rejected.¶
Issuers SHOULD publish their public keys as a JWK Set [RFC7517] at a well-known endpoint:¶
GET /.well-known/acta-keys.json
{
"keys": [{
"kty": "OKP",
"crv": "Ed25519",
"kid": "sb:issuer:4Kpm7Q3wXx2b",
"x": "<base64url-encoded-public-key>",
"use": "sig"
}]
}
¶
The x parameter MUST be the base64url-encoded Ed25519 public key
as specified in [RFC8037].¶
For offline verification, public keys MAY be distributed out-of-band (e.g., embedded in configuration files, published in DNS TXT records, or included in agent manifests).¶
This specification defines a four-level trust hierarchy for agent identity. Trust tiers are used by policy engines to gate tool access.¶
No identity presented. Default tier for anonymous connections.¶
Agent presents a valid signed manifest with a verifiable Ed25519 public key. Identity is pseudonymous but consistent.¶
Agent has accumulated verifiable evidence receipts (e.g., arena battle outcomes, successful restraint records) that demonstrate a track record of trustworthy behavior.¶
Operator has explicitly granted elevated access. Typically requires out-of-band verification (e.g., organization membership, contractual agreement).¶
Trust tier transitions are unidirectional within a session but MAY be re-evaluated across sessions based on accumulated evidence.¶
An agent's identity is expressed as a signed manifest:¶
{
"type": "scopeblind:agent-manifest",
"id": "sb:agent:3mK9pQ7wXx2b",
"version": "2.1.0",
"previous_version": "2.0.0",
"created_at": "2026-03-20T10:00:00Z",
"public_key": "<base58-ed25519-public-key>"
}
¶
Manifests are IMMUTABLE once signed. Version changes create new
manifests that reference their predecessor via previous_version,
forming a verifiable version chain.¶
For remote MCP transports (HTTP/SSE), agent identity MAY be bound
to per-request proof-of-possession using DPoP [RFC9449]. The
auth_key_bindings field in the manifest links the agent's Ed25519
identity key to one or more P-256 DPoP keys.¶
This binding creates a verifiable delegation chain:¶
Operator → Agent Identity (Ed25519) → Request Auth (P-256 DPoP)¶
Receipts include an issued_at timestamp but do not include a nonce
or sequence number. Verifiers SHOULD reject receipts with timestamps
that are unreasonably old (implementation-defined; 24 hours is
RECOMMENDED as a default).¶
For environments requiring stronger replay protection, implementations
MAY add a nonce field to the payload.¶
If an issuer's signing key is compromised, all receipts signed with
that key become suspect. Issuers SHOULD implement key rotation by
publishing new keys at the well-known endpoint and including a
valid_from / valid_until window in the JWK metadata.¶
Verifiers SHOULD check key validity windows when available.¶
Receipts are designed to capture decision metadata, NOT request content. Implementations MUST NOT include raw prompts, tool arguments, API keys, or other sensitive parameters in receipt payloads.¶
The tool_name and decision fields are considered non-sensitive.
The session_id field SHOULD be an opaque identifier that is not
correlatable across sessions.¶
The signing process relies on JCS [RFC8785] for deterministic serialization. Implementations MUST use a conformant JCS implementation to prevent canonicalization divergence attacks where the signed bytes differ from the verified bytes.¶
This document has no IANA actions.¶
Future versions of this specification MAY request registration of:¶
This section records the status of known implementations of the protocol defined by this specification at the time of publication.¶
Organization: ScopeBlind¶
Implementation: https://www.npmjs.com/package/protect-mcp¶
Description: A stdio proxy for MCP servers that generates signed decision receipts. Supports shadow mode (log-only) and enforce mode (policy evaluation).¶
Coverage: Access Decision Receipts, Restraint Receipts.¶
Licensing: FSL-1.1-MIT (converts to MIT after 2 years).¶
Organization: ScopeBlind¶
Implementation: https://www.npmjs.com/package/@scopeblind/passport¶
Description: Agent identity SDK for generating manifests, signing receipts, and managing trust tier evidence bundles.¶
Coverage: All receipt types, manifest signing, key management.¶
Licensing: FSL-1.1-MIT.¶
The following example demonstrates end-to-end receipt creation and verification.¶
import { generateIssuerKey } from '@scopeblind/passport';
const issuer = generateIssuerKey();
// issuer.issuerId = "sb:issuer:4Kpm7Q3wXx2b"
¶
import { signReceipt } from '@scopeblind/passport';
const receipt = signReceipt(
{
type: "protectmcp:decision",
tool_name: "deploy",
decision: "allow",
agent_tier: "privileged",
policy_digest: "sha256:a8f3...c91e",
issued_at: "2026-03-22T14:32:06.551Z",
issuer_id: issuer.issuerId
},
issuer.secretKeyHex,
issuer.issuerId
);
¶
$ npx @veritasacta/verify receipt.json --key issuer-public.json ✓ Signature valid ✓ Issuer: sb:issuer:4Kpm7Q3wXx2b ✓ Decision: allow (deploy) ✓ Issued: 2026-03-22T14:32:06.551Z¶
The Acta receipt format was developed as part of the Veritas Acta protocol, an infrastructure for verifiable machine decision-making. The design draws on work in the IETF OAuth and Web Authentication communities, particularly RFC 9449 (DPoP) for proof-of-possession patterns and RFC 8785 (JCS) for deterministic serialization.¶