A2A Extension: UCAN Security Scheme (Maya v1)
Status: Draft v1 — 2026-05-18
Extension URI: https://projectmaya.deoscomputing.io/a2a/ext/ucan/v1
Maintainer: DEOS Computing (did:web:deos.computing)
Depends on: A2A v1.0 (a2a-protocol.org), UCAN v0.10+ (ucan-wg/spec)
1. Motivation
A2A v1.0 ships five SecurityScheme types: APIKey, HTTPAuth, OAuth2, OIDC, mTLS. All five are identity-rooted — the credential proves "I am Alice" and the server looks up an internal policy to decide what Alice may do. In agent-to-agent flows the principal is a chain (user → personal agent → service agent), so identity-rooted auth pushes per-user policy state into every service.
UCAN (ucan-wg/spec) is capability-rooted: the credential names the authorized action inline, signed and delegated down a chain. The server verifies the chain offline and acts. There is no per-user policy lookup at the service.
This extension defines a UcanSecurityScheme type so A2A clients can authenticate with UCAN chains using A2A's existing scheme-negotiation surface.
2. Wire format
2.1 Agent Card declaration
A server advertises UCAN support by including a UcanSecurityScheme entry in its Agent Card securitySchemes:
{
"securitySchemes": [
{
"type": "https://projectmaya.deoscomputing.io/a2a/ext/ucan/v1#UcanSecurityScheme",
"scheme_id": "maya-ucan",
"audience_did": "did:web:amazon.com",
"accepted_capabilities": [
{ "with": "commerce.purchase", "max_amount_usd": 200, "constraints": "amazon.com" },
{ "with": "commerce.return", "constraints": "amazon.com" }
],
"chain_max_depth": 4,
"revocation_check_url": "https://amazon.com/a2a/ucan/revocations"
}
],
"security": ["maya-ucan"],
"extensions": ["https://projectmaya.deoscomputing.io/a2a/ext/ucan/v1"]
}
Fields:
type— fully-qualified extension URI plus#UcanSecurityScheme. Required.scheme_id— local identifier thesecurity[]array references. Required.audience_did— DID this server expects in the leaf UCAN'saudfield. Required. SHOULD matchdid:web:<the serving domain>.accepted_capabilities— array of capability templates the server will accept. Each entry haswith(action_can string), optional caveats. Required, MAY be empty (means "any capability with this audience").chain_max_depth— server's policy limit on delegation chain length. Default 8.revocation_check_url— optional. If present, server consults this URL for revocation tombstones (CID list) before accepting any UCAN.
2.2 Client request
The client presents the UCAN delegation chain via the Authorization HTTP header (per A2A's per-scheme credential carriage):
Authorization: UCAN <comma-separated-token-chain>
A2A-Version: 1.0
Where <comma-separated-token-chain> is the chain of UCAN tokens, leaf-first (the leaf is the UCAN the bearer is invoking; preceding entries are its proof chain back to a root). Each token is base64url-nopad(JCS(token-json)) where token-json is the inline-signature UCAN shape shown below:
{
"iss": "did:deos:...",
"aud": "did:deos:...",
"att": [{"with": "<resource>", "can": "<verb>", ...caveats}],
"exp": 1746201600,
"nbf": 1746000000,
"nnc": "<nonce>",
"prf": ["<parent-hash-hex>"],
"s": { "alg": "ed25519", "kid": "did:deos:...#sig-1", "sig": "<base64url-nopad 64 bytes>" }
}
The signature payload is "deos-ucan-v1:" || JCS(token-without-s). The shape is translatable to ucan-wg/spec v1.0 JWTs but is carried inline so a substrate receipt can embed the UCAN in capability.chain[] without re-canonicalization.
Clients MAY alternatively place the chain in the Message.metadata map under https://projectmaya.deoscomputing.io/a2a/ext/ucan/v1#chain as an array of base64url-encoded token strings. Header form is preferred for HTTP; metadata form is required for gRPC.
2.3 Server verification
A conforming server MUST, for every incoming SendMessage/SendStreamingMessage/SubscribeToTask:
- Parse the chain. Reject with
UNAUTHENTICATED(401) if malformed. - Verify each token's Ed25519 signature against the issuer's DID document, with signature payload computed as
"deos-ucan-v1:" || JCS(token-without-s). Reject withUNAUTHENTICATEDif any signature is invalid. - Walk the chain leaf-to-root:
- Each token's
audMUST equal the next token'siss(delegation continuity). - Each token's
att[]MUST be a subset of the prior token'satt[](capability attenuation). - Each token's
nbf/expMUST cover the current time.
- Each token's
- The leaf's
audMUST equal the server'saudience_did. - The leaf's
att[]MUST contain at least one capability matching one entry inaccepted_capabilities(matching uses prefix onwithand AND-conjunction on caveats). - If
revocation_check_urlis configured, fetch the current revocation list and reject if any token's CID appears. - If chain length >
chain_max_depth, reject withPERMISSION_DENIED(403).
Any failure MUST return one of UNAUTHENTICATED (401), PERMISSION_DENIED (403), or INVALID_ARGUMENT (400) per A2A v1.0 §7.5. Servers MUST NOT reveal which step failed beyond the category — the error message field is opaque diagnostic only.
2.4 Mid-task re-auth
If the server determines mid-task that the current chain doesn't cover a follow-on action (e.g., the task escalated to require a wider scope), it MUST set the task to AUTH_REQUIRED state (per A2A §7.6) and include a Message with metadata containing:
{
"https://projectmaya.deoscomputing.io/a2a/ext/ucan/v1#required_capability": {
"with": "commerce.refund",
"max_amount_usd": 500
}
}
The client responds via the standard A2A mid-task message flow, including a new UCAN chain that covers the requested capability.
3. Conformance
A v1 server MUST:
- Advertise
UcanSecuritySchemein its Agent Card if it accepts UCAN auth. - Verify the full chain offline (no callbacks except the optional revocation check).
- Enforce
audience_did,accepted_capabilities, time bounds, and chain depth. - Return only the three error categories above on auth failure.
A v1 server SHOULD:
- Cache verified chains for the duration of the leaf's
exp. - Publish a revocation endpoint as documented.
- Include the chain CIDs in any Receipt extension response (see
a2a-ext/substrate-receipts/v1) so audit logs preserve which authorization was used.
A v1 client MUST:
- Build chains leaf-first.
- Include
A2A-Version: 1.0and the extension URI inA2A-Extensions. - Respect the server's
chain_max_depth.
4. Security considerations
- Bearer-token caveat. A chain in the
Authorizationheader is replayable for the lifetime of the leaf'sexp. Production deployments SHOULD pair this with mTLS, or use a shortexp(≤ 5 min) plus a nonce caveat to shrink the replay window. - Revocation latency. Pull-based revocation (fetch list before each verify) is acceptable; push-based revocation (events on the substrate) is preferred for high-stakes capabilities. The Maya reference implementation uses substrate-anchored revocation tombstones.
- Audience pinning.
aud = did:web:amazon.comcannot be replayed againstdid:web:walmart.com— the primary mitigation against credential theft at delivery. - Chain depth as DoS surface. Servers MUST enforce
chain_max_depth. Without it, an attacker submits oversized chains to burn verification CPU.
5. Reference implementations
- Rust host-side verifier — validates
Authorization: UCAN <chain>per §2.3 (~400 lines, used bymaya-runtime; lifts cleanly into any A2A server). - TypeScript client — constructs and signs chains.
- Rust server helper — drop-in module for non-Maya A2A agents accepting UCAN without writing the verifier from scratch.
All three live in DEOS Computing's working repo, access-controlled during v1 alpha. A public crate / npm package will follow once the wire format stabilizes. §2 is self-contained for implementers who want to start now.
6. Versioning
…/ucan/v1 is immutable. Breaking changes land at …/ucan/v2; v1 stays alive per A2A's backward-compatibility rules.