← Machine Authority Protocol
Machine Authority Protocol v1.0 Canonical Action Representation (CAR) v1.0 Status: STABLE Published: 2026-05-03

Abstract

The Canonical Action Representation (CAR) is a byte-deterministic, schema-validated, vendor-neutral wire format for the proposed AI-agent tool call before it executes. A CAR encodes the tool name, typed arguments, actor identity (with proof-of-possession), delegation chain, accumulated context, session, and timestamp. CAR is the input to the Action Authorization Boundary (AAB); the AAB returns a Decision Envelope carrying one of the five AARM v1 §R4 decisions (ALLOW, DENY, DEFER, MODIFY, STEP_UP) or the MAP-defined REVOKE extension that aborts an in-flight ALLOW.

CAR is the wire-format binding of AARM v1 §R1 (pre-execution interception) and §R2 (accumulated context), and provides the action-bound identity surface used by R6 (cryptographic identity binding). The normative artifact for validation is schema/car-v1.json (JSON Schema Draft 2020-12).

1. Introduction

When an AI agent decides to invoke a tool — call an API, write a file, execute a query — the reasoning that produced the decision is probabilistic. The action itself is deterministic. Identity protocols (OAuth, SPIFFE, DID) verify who the agent is. Policy engines (OPA, Cedar, OpenFGA) evaluate whether a rule says allow. Neither defines a common, byte-deterministic format for representing the proposed action itself, nor a standard decision envelope for deferring it to asynchronous human approval.

The Canonical Action Representation closes that gap. CAR is the lingua franca of the Action Authorization Boundary: produced by any agent framework, evaluated by any policy engine, logged by any audit system, rendered by any approval interface — without vendor-specific schemas or proprietary wire formats.

Two design properties are non-negotiable:

  1. Byte-determinism. Two implementations MUST produce identical bytes for the same logical CAR. Without this, no signature is portable. See §6.
  2. Implementability without folklore. Every normative requirement is pinned to a cited primitive — RFC 8785 for canonicalization, RFC 9421 for HTTP message signatures, RFC 7515 for JWS, RFC 9470 for ACR step-up. No “a signature over the relevant fields.”

2. Conventions

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 RFC 2119.

3. Schema

The normative schema is schema/car-v1.json. This document is editorial; the schema is authoritative. Worked examples live in examples/.

3.1 Top-level fields

FieldRequiredTypeNotes
car_versionYESenum "1.0"Schema version.
action_idYESUUIDv4Unique per proposed action. MUST NOT be reused.
tool_nameYESstringRegex ^[a-zA-Z0-9._/-]+$, maxLength: 256. See §3.2.
argumentsYESobjectTyped arguments. See §3.3.
actorYESobjectIdentity, delegation, agent version. See §3.4.
contextYESobjectAccumulated context (AARM R2). See §3.5.
session_idYESstringStable across all CARs in one agent run.
timestampYESRFC 3339 (UTC)When the CAR was generated.
task_idNOstringA2A task ID, when applicable.
mcp_tool_call_idNOstringMCP tool_call_id, when applicable.

3.2 tool_name

tool_name MUST match the regex ^[a-zA-Z0-9._/-]+$ and MUST NOT exceed 256 characters. The regex is intentionally restrictive to prevent denial-of-service against naive policy engines and log indexers. Implementations MUST reject CARs whose tool_name violates either constraint.

For MCP-sourced actions, tool_name MUST equal the tool name in the MCP server manifest. For A2A actions, tool_name SHOULD use the a2a/<task-skill> convention.

3.3 arguments

arguments is the typed argument object that will be passed to the tool unmodified at execution time. Producers MUST NOT redact, summarize, or re-order arguments after CAR generation. The object MAY contain nested objects and arrays. Producers SHOULD declare the expected JSON Schema for the tool's arguments out-of-band; verifiers MAY validate arguments against that out-of-band schema as part of policy evaluation.

additionalProperties is permitted on arguments because tool argument shapes are tool-specific. This is the one place CAR allows open-shape data; all other CAR objects are closed-by-default (see §3.5).

3.4 actor

actor: {
  identity: <Identity discriminated union>,    // §3.4.1
  delegation_chain: [<Identity>, ...],         // OPTIONAL, §3.4.2
  agent_version: string                        // OPTIONAL semver
}

3.4.1 Identity discriminated union

actor.identity is a discriminated union keyed on type:

  • { "type": "spiffe", "uri": "spiffe://..." } — SPIFFE SVID URI.
  • { "type": "did", "did": "did:..." } — W3C Decentralized Identifier.
  • { "type": "url", "url": "https://..." } — HTTPS URL resolving to an Agent Identity Document (see §4.3).

Implementations MUST reject CARs with unknown type values. Identity binding (proof that the CAR producer holds the private key for the asserted identity) is normative and is specified in §4.

3.4.2 Delegation chain

actor.delegation_chain is an OPTIONAL ordered array of identities describing the principals that delegated authority to actor.identity.

Ordering (NORMATIVE): Head-first. The root delegator is index 0; the immediate caller is the last entry. The CAR producer (the entity identified by actor.identity) is not in the chain.

Length: maxItems: 8. Implementations MUST reject longer chains.

Per-entry expiration: Each entry MAY include a not_after RFC 3339 timestamp. If present, not_after MUST be ≥ the CAR timestamp. Verifiers MUST treat any chain entry with not_after < CAR timestamp as making the entire CAR invalid.

Cryptographic delegation (roadmap): A future minor revision will add a parallel delegation_proofs[] field, each entry being a compact JWS token signed by the delegator's identity key, with aud equal to the delegate's identity. v1.0 entries are declarative only and MUST be treated by verifiers as advisory; signed delegation is the post-v1.0 hardening path.

3.5 context — accumulated context (AARM R2)

context is REQUIRED. It satisfies AARM v1 R2: deterministic policy evaluation on accumulated execution context. The schema is closed by default; vendor extensions go in context.extensions under a vendor namespace (see §3.5.7). Each subsection below is OPTIONAL unless marked.

3.5.1 context.env (REQUIRED)

env: "prod" | "staging" | "dev" | "test"

The deployment environment of the AAB issuing the CAR. Required for fail-closed defaults — a policy engine MAY choose to deny on env="prod" with elevated risk.

3.5.2 context.time

time: {
  now: <RFC 3339>,                  // REQUIRED if context.time present
  freeze_active: <bool>,            // OPTIONAL
  freeze_reason: <string>           // OPTIONAL; required if freeze_active=true
}

time.now is the authoritative wall-clock the producer believes is true. Verifiers MUST compare time.now to their own clock and reject if skew exceeds the verifier's configured maximum (RECOMMENDED default: 60s).

freeze_active indicates an organization-wide freeze (release window, incident response). Policies typically deny non-emergency actions when freeze_active=true.

3.5.3 context.geo

geo: {
  actor_region:  <ISO 3166-2>,      // OPTIONAL
  target_region: <ISO 3166-2>       // OPTIONAL
}

For data-residency policies. ISO 3166-2 (e.g., US-CA, DE-BY).

3.5.4 context.risk_tier

risk_tier: "low" | "elevated" | "high" | "critical"

Producer-asserted risk level. Producers SHOULD derive this deterministically (e.g., from the tool's risk classification in the MCP manifest). Verifiers MUST treat risk_tier as advisory — they MAY override based on policy.

3.5.5 context.organizational

organizational: {
  mcp_server_id: <string>,          // OPTIONAL
  project_id:    <string>,          // OPTIONAL
  tenant_id:     <string>           // OPTIONAL
}

Non-identity organizational scoping. Identity goes in actor (§3.4), not here.

3.5.6 context.accumulated

accumulated: {
  prior_action_ids:    [<UUIDv4>, ...],   // OPTIONAL, max 32
  session_token_hash:  <hex-64>           // OPTIONAL, SHA-256
}

Cross-action linkage. prior_action_ids lists action_id values for prior actions in the same session that the producer claims as causal predecessors; policies MAY rate-limit or deny based on patterns. session_token_hash binds the CAR to a specific upstream session credential without exposing it.

3.5.7 context.extensions

extensions: {
  "<vendor-namespace>": <object>
}

Vendor-specific context. Each top-level key MUST be a reverse-DNS namespace (com.example.foo). Verifiers MUST NOT treat unknown extensions as failure but SHOULD log them. Extensions are not part of the canonicalization-stable surface for cross-vendor signature interop — implementations relying on vendor-specific extensions sacrifice portability.

4. Identity binding

actor.identity in the CAR is a claim until bound by a proof-of-possession (PoP) at the transport layer. Without PoP a compromised orchestrator forges any actor identity and elicits favorable policy decisions. AABs MUST require PoP via at least one of the following mechanisms; if none is present the AAB MUST reject the CAR with reason_code: "actor_pop_missing".

4.1 mTLS with workload-identity certificate

When the CAR producer connects to the AAB over TLS with a client certificate whose subject (or SAN) matches actor.identity — a SPIFFE-SVID X.509-SVID for type=spiffe, or a DID-attested certificate for type=did — the TLS handshake itself is the PoP. The AAB MUST verify:

  1. The TLS connection terminated with mutual authentication.
  2. The certificate chains to a trust anchor for the asserted trust domain (SPIFFE) or matches a verificationMethod in the resolved DID document (DID).
  3. The certificate is unexpired.

mTLS is the preferred PoP mechanism for service-to-service deployments.

4.2 RFC 9421 HTTP Message Signatures

When mTLS is unavailable (browser-originated CARs, signed-but-non-mutual TLS), the producer MUST sign the request via RFC 9421 with a signature covering, at minimum:

  • The @request-target derived component.
  • The actor.identity value (as a serialized header or parameter).
  • The action_id field.
  • The car_hash (see §6.4).

The signing key MUST be resolvable from actor.identity:

  • type=spiffe: SPIFFE Workload API → JWT-SVID public key.
  • type=did: DID document verificationMethod matching the signature keyid.
  • type=url: Agent Identity Document JWKS (§4.3) matching keyid.

4.3 Agent Identity Document (URL-typed identity)

When actor.identity.type = "url", the URL MUST resolve via HTTPS GET to an Agent Identity Document at the well-known path:

<url>/.well-known/map-agent-identity.json

The document MUST be JSON conforming to:

{
  "controller":     <string>,             // REQUIRED. URL of the controlling org.
  "created":        <RFC 3339>,           // REQUIRED.
  "revoked":        <RFC 3339>,           // OPTIONAL. If present, identity is invalid.
  "keys":           <JWKS>,               // REQUIRED. JWKS per RFC 7517.
  "supported_pop":  ["mtls" | "rfc9421" | "did-jws"]
}

The verifier MUST:

  1. Fetch over HTTPS with a clean trust chain.
  2. Reject if revoked is present and ≤ CAR timestamp.
  3. Reject if no key in keys matches the keyid used for PoP.
  4. Cache the document subject to standard HTTP caching rules; respect Cache-Control and Expires.

A worked example lives at examples/agent-identity-document.json.

5. Decision Envelope

The AAB's response to a submitted CAR is a Decision Envelope carrying one of the five AARM v1 §R4 decisions or the MAP-defined REVOKE extension:

DecisionSemantics
ALLOWProceed before expires_at; expired ALLOW MUST be treated as DENY.
DENYDo not execute; surface reason_code to the agent.
DEFERSuspend execution and follow the Elicitation Loop.
MODIFYThe AAB has produced a new CAR with modified_arguments; original CAR is denied.
STEP_UPRe-authenticate the actor at the required_acr per RFC 9470, then resubmit.
REVOKEAbort in-progress execution; do not retry without a new CAR.

Full schema and verifier algorithm: spec/decision-envelope and schema/decision-envelope-v1.json.

6. Canonicalization

6.1 Canonicalization is normative

A CAR's bytes are canonicalized before any hash, signature, or comparison. Two implementations producing different canonical bytes for the same logical CAR breaks every signature, every receipt, and every audit trail. This section is the single most operationally load-bearing part of the specification.

6.2 Algorithm

The canonical form of a CAR is computed as follows:

  1. Validate the CAR against schema/car-v1.json. Reject on validation failure.
  2. NFC normalization: Every JSON string value (keys and values) MUST be Unicode-normalized to NFC (Unicode Standard Annex #15) before serialization.
  3. Empty-key strip: Any object key equal to the empty string "" MUST be rejected (causes the canonicalization to fail). RFC 8785 permits empty keys; MAP forbids them to eliminate a known ambiguity surface.
  4. JCS: Apply RFC 8785 JSON Canonicalization Scheme. This pins:
    • UTF-8 output.
    • Object members sorted by key, where keys are ordered as sequences of UTF-16 code units (RFC 8785 §3.2.3).
    • Numbers in shortest round-trippable form per RFC 8785 §3.2.2.5 (which references ECMA-262 7.1.12.1).
    • No whitespace.
  5. The output bytes are car_canonical.

6.3 Implementer note on RFC 8785 surprises

RFC 8785's UTF-16 key ordering is unintuitive and produces results inconsistent with naive UTF-8 byte comparison for keys involving non-BMP characters and surrogate pairs. Implementations using naive string comparison on UTF-8 bytes will produce different output. Use a JCS-compliant library; do not roll your own.

6.4 car_hash

car_hash = lowercase_hex(SHA-256(car_canonical))

A 64-character lowercase hex string. car_hash is what every downstream artifact (Decision Envelope aab_signature, CAC car_hash, audit log entries) refers to.

6.5 Reference canonicalizer

A reference canonicalizer in JavaScript and Python is published under tools/canonicalize/; a Go port is on the post-v1.0 roadmap. Cross-language test vectors at test-vectors/vectors/canonicalize/ include Unicode-edge, empty-key-rejection, and large-array cases. Conforming implementations MUST pass every vector.

7. Limitations

What MAP-CAR does not solve. Cornell-grade specs publish their non-coverage. Implementers MUST NOT assume the spec defends against adversaries listed here.

  1. Compromised LLM provider. If the model provider is adversarial, it can refuse to call CAR-aware tools, fabricate arguments, or collude with the agent. CAR makes the action visible at the authorization boundary; it does not authenticate the model.
  2. Compromised approver host. If the human approver's signing key is exfiltrated from the approver's device, MAP cannot detect it in-spec. Detection is via key-rotation, transparency-log inclusion (post-v1.0 roadmap), and HSM-backed keys. See CAC §6.
  3. Prompt injection on the input side. CAR authorizes the output side — what the agent decided to do, not what the agent was told. Indirect prompt injection that causes the agent to issue a policy-permitted but undesired action is out of scope; defense is a property of the agent, the system prompt, and the policy.
  4. Policy authoring. MAP does not specify a policy language. Compose with OPA, Cedar, OpenFGA, AWS Cedar, or any rule engine.
  5. Identity issuance. MAP does not specify how identities are issued. Compose with SPIFFE/SPIRE, DID methods, or OIDC.
  6. Side-channel observation. A network observer who can read CAR payloads in transit learns what the agent intended to do. Use TLS for transport (this is a prerequisite); CAR does not specify payload encryption at rest.
  7. Race conditions on the resource side. MAP authorizes the intent to call a tool. If the tool's underlying resource is modified between authorization and execution, that is a tool-side concern (file locking, optimistic concurrency).

7.1 Stability commitment

Breaking changes to the v1 wire format require a 12-month deprecation window and ratification by the founding council before a v2 cutover. Conforming v1 implementations producing or consuming a CAR with another v1 implementation MUST interoperate without negotiation.

8. Security considerations

This section is non-normative; it summarizes properties that follow from the normative requirements above.

  1. action_id MUST be unique per proposed action. Enforcement points MAY reject replays based on action_id cache.
  2. AABs MUST verify action_id in the Decision Envelope matches the submitted CAR.
  3. arguments MUST be passed to the tool without modification after authorization. The only exception is a MODIFY decision, which produces a new CAR with modified_arguments and a fresh action_id.
  4. Expired ALLOW and expired DEFER both resolve to DENY.
  5. actor.identity is bound by §4 PoP.
  6. context.time.now is producer-asserted; verifiers MUST validate against their own clock with bounded skew.

9. References

Normative references