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:
- Byte-determinism. Two implementations MUST produce identical bytes for the same logical CAR. Without this, no signature is portable. See §6.
- 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
| Field | Required | Type | Notes |
|---|---|---|---|
car_version | YES | enum "1.0" | Schema version. |
action_id | YES | UUIDv4 | Unique per proposed action. MUST NOT be reused. |
tool_name | YES | string | Regex ^[a-zA-Z0-9._/-]+$, maxLength: 256. See §3.2. |
arguments | YES | object | Typed arguments. See §3.3. |
actor | YES | object | Identity, delegation, agent version. See §3.4. |
context | YES | object | Accumulated context (AARM R2). See §3.5. |
session_id | YES | string | Stable across all CARs in one agent run. |
timestamp | YES | RFC 3339 (UTC) | When the CAR was generated. |
task_id | NO | string | A2A task ID, when applicable. |
mcp_tool_call_id | NO | string | MCP 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:
- The TLS connection terminated with mutual authentication.
- The certificate chains to a trust anchor for the asserted trust domain (SPIFFE) or matches a
verificationMethodin the resolved DID document (DID). - 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-targetderived component. - The
actor.identityvalue (as a serialized header or parameter). - The
action_idfield. - 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 documentverificationMethodmatching the signaturekeyid.type=url: Agent Identity Document JWKS (§4.3) matchingkeyid.
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:
- Fetch over HTTPS with a clean trust chain.
- Reject if
revokedis present and ≤ CARtimestamp. - Reject if no key in
keysmatches thekeyidused for PoP. - Cache the document subject to standard HTTP caching rules; respect
Cache-ControlandExpires.
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:
| Decision | Semantics |
|---|---|
ALLOW | Proceed before expires_at; expired ALLOW MUST be treated as DENY. |
DENY | Do not execute; surface reason_code to the agent. |
DEFER | Suspend execution and follow the Elicitation Loop. |
MODIFY | The AAB has produced a new CAR with modified_arguments; original CAR is denied. |
STEP_UP | Re-authenticate the actor at the required_acr per RFC 9470, then resubmit. |
REVOKE | Abort 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:
- Validate the CAR against
schema/car-v1.json. Reject on validation failure. - NFC normalization: Every JSON string value (keys and values) MUST be Unicode-normalized to NFC (Unicode Standard Annex #15) before serialization.
- 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. - 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.
- 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.
- 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. - 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.
- 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.
- Policy authoring. MAP does not specify a policy language. Compose with OPA, Cedar, OpenFGA, AWS Cedar, or any rule engine.
- Identity issuance. MAP does not specify how identities are issued. Compose with SPIFFE/SPIRE, DID methods, or OIDC.
- 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.
- 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.
action_idMUST be unique per proposed action. Enforcement points MAY reject replays based onaction_idcache.- AABs MUST verify
action_idin the Decision Envelope matches the submitted CAR. argumentsMUST be passed to the tool without modification after authorization. The only exception is aMODIFYdecision, which produces a new CAR withmodified_argumentsand a freshaction_id.- Expired ALLOW and expired DEFER both resolve to DENY.
actor.identityis bound by §4 PoP.context.time.nowis producer-asserted; verifiers MUST validate against their own clock with bounded skew.
9. References
- Source spec: github.com/PlawIO/machineauthority-protocol/spec/car.md
- Schema:
schema/car-v1.json - Examples:
examples/ - Canonicalizer:
tools/canonicalize/
Normative references
- RFC 2119 — Key words for use in RFCs.
- RFC 7517 — JSON Web Key (JWK).
- RFC 8785 — JSON Canonicalization Scheme (JCS).
- RFC 9421 — HTTP Message Signatures.
- RFC 9470 — OAuth 2.0 Step-up Authentication.
- Unicode Standard Annex #15 — Unicode Normalization Forms.
- JSON Schema Draft 2020-12.