DEOS UCAN-shaped Capability Tokens — v1 Specification
Status: v1.0 — published 2026-05-02
Spec URI: https://projectmaya.deoscomputing.io/ucan/tokens/v1
Maintainer: DEOS Computing (did:web:deos.computing)
Reference impl: Rust crate in DEOS Computing's working repo (access-controlled during v1 alpha).
A capability token in v1 is a UCAN-shaped JSON object carried inline on a receipt's capability.chain array. It targets UCAN v1.0 semantics (2024) — iss, aud, att, prf, exp, nbf, nnc — but uses a different encoding (inline canonical JSON instead of JWT base64) so a receipt remains a single canonical-JSON object end to end. See §7 for v0.10.0 ↔ v1.0 alignment.
This document is normative for v1.
1. Token shape
{
"iss": "did:deos:user-maya",
"aud": "did:deos:agent-maya-2026",
"att": [
{"with": "wallet://maya/usd", "can": "spend", "max": 200000}
],
"exp": 1746201600,
"nbf": 1746000000,
"nnc": "vec02-root",
"prf": ["<hex>", ...],
"s": {
"alg": "ed25519",
"kid": "did:deos:user-maya#sig-1",
"sig": "<base64url-nopad of 64 bytes>"
}
}
| Field | Required | Notes |
|---|---|---|
iss |
yes | DID of the issuer (the entity granting/delegating). |
aud |
yes | DID of the audience (the entity being granted to). |
att |
yes | Non-empty array of attenuations (capabilities granted). |
exp |
yes | Expiry, integer unix seconds. |
nbf |
optional | Not-before, integer unix seconds. |
nnc |
optional | Nonce. RECOMMENDED (replay-resistance). |
prf |
optional | Hashes (BLAKE3 hex) of parent tokens, when not embedded inline. v1 chains are inline so prf is informational. |
s |
yes | Inline signature object — see §3. |
1.1 Attenuation shape
Each entry in att is an object:
{
"with": "<resource URI>",
"can": "<verb>",
"<caveat-name>": <caveat-value>,
...
}
withis an opaque URI identifying the resource the capability applies to (e.g.mcp://weather,wallet://maya/usd,policy://acme/mcpd-default).canis a verb naming the action allowed on the resource (e.g.tool/call,spend,evaluate,send,attest).- Additional fields are caveats narrowing the capability.
Caveat semantics in v1:
- Integer caveats with names like
maxare upper bounds that narrow as they decrease. Example:{"max": 200000}means "spend at most 200,000 cents." - All other caveat values are pass-through equality constraints.
A full caveat-evaluator with richer semantics ships in M6.
2. Wire form
Tokens are embedded directly into a receipt's capability.chain array (in
order, root-first). They are NOT JWT-encoded.
A v1 chain has at least one token; common shapes:
- 1-deep: principal directly authorizes the agent.
- 2-deep: principal → org-agent → leaf-agent.
- 3-deep+: principal → org-agent → ... → leaf-agent (typical for sub-delegation in agent meshes).
3. Signature payload
The signature in s covers the full token body with the s field
removed, prefixed with a fixed domain-separation tag:
sig_payload = "deos-ucan-v1:" || JCS(token minus { s })
s.sig = base64url-nopad(Ed25519_sign(secret_key, sig_payload))
Where JCS is the same canonicalization as receipt format
v1 §4.1 (RFC 8785-compatible for the
integer/string/bool/null/array/object subset).
4. Validity rules (normative)
A capability chain C[0..n] is valid for a receipt R iff all of the
following hold:
4.1 Per-token signature
For each C[i]:
C[i].s.alg == "ed25519".C[i].s.kidMUST start with<C[i].iss>#. (The kid is owned by the token's issuer.)- The verifying key resolved from
C[i].s.kid(via DID document, or in reference impl test mode via deterministic derivation — see §6) verifiesC[i].s.sigagainstsig_payload(C[i]).
4.2 Chain continuity
C[0].iss == R.principal. The root issuer is the receipt's principal.- For each
0 < i < n:C[i-1].aud == C[i].iss. Each delegation hands off to the next issuer. C[n].aud == R.subject. The leaf audience is the agent acting.
4.3 Time bounds
For each C[i], with t = R.issued_at_ns / 1_000_000_000:
t <= C[i].exp(token not expired at receipt-issue time).- If
C[i].nbfpresent:t >= C[i].nbf.
4.4 Attenuation narrowing
For each non-root token C[i] (i > 0):
- For each child attenuation
ca ∈ C[i].att, there MUST exist a parent attenuationpa ∈ composed[i-1]such thatcanarrows-or-equal topa:ca.with == pa.withca.can == pa.can- For every key
k ∉ {with, can}inpa:- If
pa[k]is an integer:ca[k]MUST exist and be<= pa[k]. - Else:
ca[k]MUST exist and equalpa[k].
- If
caMAY introduce additional caveats not inpa(further narrowing).
composed[i] is the list of attenuations from C[i].att that survive
narrowing against composed[i-1]. composed[0] == C[0].att.
If any child attenuation cannot be narrowed from at least one parent attenuation, the chain is invalid with an attenuation-mismatch error.
4.5 Action satisfaction
The receipt's action MUST be satisfied by composed[n] (the leaf scope).
v1 satisfaction is shape-level except for one specific rule:
- For
action.type == "payment_intent":metadata.amount(parsed as a decimal-cents integer) MUST be<=themaxcaveat of some attenuation incomposed[n]whosecan == "spend". Or, if nomaxcaveat is set on any matching attenuation, payment is unbounded (still valid).
Other action types pass satisfaction as long as the leaf scope is non-empty. M6 introduces full caveat evaluation for all action types.
4.6 Revocation
M7 adds a check that no token in the chain appears in the substrate's revocation log. v1 implementations omit this check; the verifier MUST tolerate its absence.
5. Receipt linkage
Receipts produced by a v1 substrate MUST carry a capability.chain with at
least one token. The attestation API (/attestation/api/v1)
MUST validate the chain (§4) on POST /append and reject receipts whose
chain fails validation with HTTP 400.
An external verifier MUST validate the chain (§4) on every GET /receipt/{id}
flow before returning VALID. A chain-validation failure SHOULD be reported as
INVALID:capability:<reason>.
6. Test-key derivation (REFERENCE IMPL ONLY)
For unit tests and demos, signing keys for test DIDs are derived deterministically:
secret_seed = BLAKE3("deos-test-key-v1:" || did)[..32]
signing_key = Ed25519::from_seed(secret_seed)
This makes the M1 test vectors fully reproducible without committing private keys to the repo. Production substrates MUST NOT use this derivation — production keys come from HSMs and are published via DID documents.
The reference test resolver returns derived keys for any kid of shape
<did:deos:*>#sig-1. Production DID resolvers use the underlying DID method
(/did/deos/v1).
7. Wire compatibility note (UCAN v1.0)
Updated 2026-05-05 per UCAN1.1-7. This document targets the UCAN v1.0 model (2024) — the WG-stable line that supersedes v0.10.0. Semantic field names match v1.0:
iss,aud,att,exp,nbf,nnc,prfall carry their UCAN v1.0 semantics.- v0.10.0's
capfield is namedatthere (matching v1.0 nomenclature). - v1.0's
with(resource URI) andcan(action verb) within anattentry mirror our shape; we additionally allowwitharrays per CAV1.1-6 and OR-composition per CAV1.1-5, both compatible extensions.
A v1 DEOS token is semantically wire-compatible-via-translation with a UCAN v1.0 token:
- Field names are aligned; field semantics are identical for the v1.0 subset.
- The signature can be re-encoded between our inline-JSON form and UCAN v1.0's JWT form: the body bytes are different (canonical JSON vs JWT base64-of-canonical-JSON) so re-signing is required, but the attenuations and delegation chain round-trip.
The decision to use inline JSON instead of JWT is deliberate:
- Receipts are already canonical JSON; embedding tokens inline means the whole receipt is a single canonical-bytes commitment.
- No double-canonicalization (JWT base64 over canonical JSON over canonical-JSON-embedded UCAN).
- Less overhead, smaller receipts, simpler verifier code paths.
A future v1.2 MAY add a JWT-import / JWT-export translation layer for interop with non-DEOS UCAN v1.0 ecosystems (tracked as UCAN1.1-1).
v0.10.0 → v1.0 migration deltas (informational)
Implementations that previously targeted UCAN v0.10.0 should note these v1.0 changes:
caparray renamedatt. (Already aligned — DEOS v1 usedattfrom inception.)att[i].rscrenamedatt[i].with. (Already aligned.)- Top-level
prf(proofs) accepts the same chained-token semantics as v0.10.0. - v1.0 normatively requires the
nnc(nonce) be unique per (iss, aud) pair within a token's validity window. DEOS substrates enforce nonce uniqueness via the UCAN1.1-6 revocation index keyed by(iss, nnc).
8. Open questions deferred
- JWT import/export. Cross-ecosystem UCAN interop. v1.1.
- Caveat algebra. Beyond integer-max and equality — set membership, hierarchical resource matching, time ranges. Tracked separately as the capability caveats spec.
- Multi-sig caveats. A capability requiring N-of-M signatures from named co-issuers. v1.1+.
- Resource hierarchy.
mcp://*matchingmcp://weather. v1 requires exact match. - Token batching. One signature over many tokens issued in the same delegation. v1.1.
- Revocation. See
/revocation/v1.
9. Maintainer
DEOS Computing — design questions, conformance reports, and proposed additions to github.com/DEOS-Computing.
License: CC BY 4.0 (text), Apache-2.0 (reference code).