UCAN and Caveats
This chapter specifies how authority is delegated, attenuated, and revoked, and the caveat vocabulary that narrows a delegation. The protocol uses User-Controlled Authorization Networks (UCAN) as the underlying delegation primitive, and extends UCAN’s policy slot with a domain-specific caveat set.
1. UCAN version
v0.1 of this specification builds on UCAN v0.10, the last JWT-shaped revision of the UCAN format. A UCAN token is a detached JWS over a JSON payload with the standard fields:
| Field | Purpose |
|---|---|
iss | Issuer DID. The party delegating authority. |
aud | Audience DID. The party receiving authority. |
att | Attestation array. Each entry is a (resource, action, caveats) capability. |
nbf | Not-before time (Unix seconds). |
exp | Expiry time (Unix seconds). |
prf | Proof chain. Array of parent UCAN content hashes. |
The full UCAN v0.10 format is specified externally; the canonical reference is the UCAN working group repository.
1.1 v1.0 migration
The UCAN working group has moved on to v1.0, which uses a DAG-CBOR plus Varsig envelope and CIDv1 references. Likewise’s v0.1 specifies v0.10 because that is what the reference implementation uses. The v0.10 → v1.0 migration is a known open issue and is expected to land as part of the next major version.
2. Capabilities in v0.10’s att field
Every entry in a UCAN’s att array is a Likewise
capability. The protocol places its capability schema directly
in the UCAN policy slot:
{
"resource": "<Resource enum value>",
"action": "<Action enum value>",
"caveats": { ... }
}
The set of legal resource and action values, and the legal
caveats schema, are specified in
Capabilities. This chapter covers how
delegations are linked, attenuated, and revoked; the next
chapter covers what they can authorize.
3. The delegation graph
A capability flows through the mesh as a chain of UCAN delegations rooted at the user. The user issues their root delegation to one or more nodes (typically the phone), authorizing those nodes to author further delegations.
When a delegation D_b cites a parent D_a in its prf
array, the receiving node MUST:
- Resolve
D_afrom the op log (or refuse the delegation if it cannot). - Verify
D_awas issued by the DID thatD_b’s issuer holds delegation under, transitively up to the user. - Verify
D_b’s capabilities are an attenuation ofD_a’s (Section 4). - Verify the time bounds on
D_bare withinD_a’s (D_b.nbf >= D_a.nbf,D_b.exp <= D_a.exp).
A delegation that fails any of these checks MUST be rejected.
flowchart LR
Root["User<br/>(root DID)<br/>* (no caveats)"]
Phone["Phone<br/>* on local data"]
Cloud["Cloud node<br/>calendar + photos"]
Analytics["Analytics peer<br/>calendar only<br/>+ time_range"]
Root -->|DelegateUcan| Phone
Phone -->|attenuates| Cloud
Cloud -->|further attenuates| Analytics
4. Strict attenuation
A child delegation’s capability set MUST be a subset of its
parent’s. Attenuation is checked per-(resource, action) pair:
the child MAY include any pair the parent includes (or any pair
strictly narrowed by additional caveats), and MUST NOT include
pairs the parent does not.
For each capability in the child:
- The
(resource, action)pair MUST appear in the parent (possibly with broader caveats). - The child’s
caveatsMUST be at least as restrictive as the parent’s (Section 5).
A delegation that broadens any caveat compared to its parent MUST be rejected by every receiving node, regardless of whether the broadened delegation was signed correctly.
5. Caveats
Every caveat is optional, meaning “no restriction along this
axis.” A delegation with no caveats authorizes the full scope
of the (resource, action) pair (subject to any restrictions
inherited from its parent).
Caveat narrowing rules: a child caveat is at least as restrictive as a parent caveat if and only if every operation that satisfies the child’s caveat would also satisfy the parent’s.
The v0.1 caveat vocabulary comprises six fields. Future minor versions MAY add caveats; an unknown caveat field MUST be treated as an absolute restriction (a delegation carrying an unknown caveat is admitted, but no operation can satisfy the unknown caveat — effectively granting the empty capability).
5.1 source_types
Restricts the capability to evidence whose source_type
matches one of the listed values.
| Form | Meaning |
|---|---|
| absent | No restriction. |
["calendar"] | Only operations on calendar-source evidence. |
["calendar", "contact"] | Either calendar or contact. |
Narrowing: child’s set MUST be a subset of parent’s.
5.2 predicates
Restricts the capability to claim operations whose predicate matches one of the listed values.
| Form | Meaning |
|---|---|
| absent | No restriction. |
["located_at"] | Only claim ops with predicate "located_at". |
Narrowing: child’s set MUST be a subset of parent’s.
5.3 kind_prefix
Restricts the capability to job operations whose kind field
starts with one of the listed prefixes.
| Form | Meaning |
|---|---|
| absent | No restriction. |
["cortex.synthesize."] | Only synthesize-class jobs. |
["cortex."] | Any reverse-DNS-prefixed cortex job kind. |
Narrowing: each child prefix MUST be a prefix of (or equal to) some parent prefix.
5.4 time_range
Restricts the capability to operations whose timestamp.wall_ms
falls within the given range.
| Form | Meaning |
|---|---|
| absent | No restriction. |
[start_ms, end_ms] | Inclusive lower bound, exclusive upper bound. |
Narrowing: child’s range MUST be contained within parent’s.
5.5 sanitize
Specifies field-level redactions that MUST be applied to operations crossing this delegation. Sanitization is unique among caveats in that it does not block an op; it modifies it in flight.
The v0.1 sanitization rules are:
| Rule | Effect |
|---|---|
StripGeo | Remove latitude, longitude, altitude, and any other geographic coordinates from evidence metadata, claim objects, and artifact bodies. |
RedactParticipants | Replace participant identifiers with anonymized placeholders consistent within the operation but not linkable to the original entities. |
TruncateContent(N) | Truncate any content body to at most N bytes. |
StripCustomMetadata | Remove any custom-metadata fields not specified by the protocol. |
A delegation MAY specify multiple sanitize rules; they are applied in the order listed.
Narrowing: a child delegation’s sanitize rule list MUST be a
superset of its parent’s (sanitization strengthens at each hop).
5.5.1 The sanitization marker
When an op is sanitized on the wire, the sanitizer MUST clear
the op’s signature field (per
Signatures) AND attach a sanitization
marker. The marker is a payload-internal field whose presence
both:
- tells the receiver the op was deliberately filtered, not corrupted, and
- records the chain of sanitize rules applied (so the receiver can audit that the rules match a delegation the sender held).
The exact wire shape of the marker is specified in Wire Format; the contract here is that the marker is a structurally-required part of any unsigned, deliberately-modified op.
A receiver MUST verify that the marker’s claimed sanitize chain is admitted by some delegation the sender holds reaching back to the user. A marker that does not match an authorized chain MUST cause the op to be rejected.
5.6 audit_inference
Requires the delegated node to emit
likewise.inference.snapshot artifacts (see
Inference Audit)
for every model call performed against data covered by this
delegation.
| Form | Meaning |
|---|---|
absent or false | No requirement. The delegated node MAY emit snapshots for its own bookkeeping but is not obliged to. |
true | The delegated node MUST emit a likewise.inference.snapshot artifact for every inference call performed against ops admitted by this delegation. |
Narrowing: a child caveat with audit_inference: true is
admissible under any parent (the parent did not require audit;
the child voluntarily promises it). A child caveat with
audit_inference: false is admissible only if the parent also
permits audit-free operation. In other words, audit
requirements strengthen down the chain; they cannot be
relaxed.
This caveat is the mechanism by which the user requires
auditable inference from a delegated party — for example, an
organization running a Likewise node under a scoped
delegation. When a delegation carries audit_inference: true,
the snapshots that the delegated node emits become themselves
operations on the user’s log (subject to the user’s read
capability on the artifact ops the delegated node produces),
completing the audit loop across the delegation boundary.
The corresponding invariant is specified in Invariants §I-9.
6. Revocation
A RevokeUcan op authored by a delegation’s issuer (or by a
node holding write authority over the issuer’s DID under a
still-valid parent) retires the delegation. Receiving nodes
MUST:
- Mark the delegation’s content hash as revoked in the local UCAN view.
- Recursively mark any delegations whose
prfcites the revoked one as revoked (transitive cascade). - Re-evaluate the authorization of every operation whose authority chain depended on a now-revoked delegation. Such operations are NOT removed from the log, but they MUST NOT be applied to projections.
The on-revoke rebuild is one of the more expensive operations in the protocol; implementations SHOULD batch revocations and defer the rebuild to the next idle window when latency permits.
A revoked delegation cannot be un-revoked. To restore the authority, the issuer issues a new delegation.
7. The authorize-and-filter pipeline
When a node receives operations (whether from its own scheduler authoring them locally or from a remote peer), it MUST run the following pipeline before applying them to projections:
- Verify signatures per Signatures.
- Authorize each op against the authoring node’s effective
capability set — the union of capabilities derived from
delegations rooted at the user, restricted by all caveats
in the chain. An op is authorized iff its
ActionandResourceare admitted and its caveats are satisfied. - Apply transitive cascades: re-evaluate ops whose authority depended on now-revoked delegations.
- Sanitize outbound ops crossing delegations with
sanitizecaveats.
Steps 1-3 run on receive; step 4 runs on send. The pipeline is specified in detail in Capabilities.
8. The user’s root delegation
The mesh is bootstrapped by the user issuing a root UCAN to the first node (typically the user’s phone). The root delegation:
- Has the user’s DID as
iss. - Has the first node’s
NodeId-bound DID asaud. - Carries the maximal capability set (
(*, *)with no caveats). - Has no
prf; it is the chain root.
Subsequent delegations cite the root (or a descendant of it) as their proof. The user holds the keypair backing their DID; an implementation MUST provide the user with a mechanism to authorize root re-issuance and to revoke the existing root.
The protocol does not specify the user-interface for this authorization; that is implementation-defined. The protocol specifies only the wire format of the resulting UCANs.