This is the full developer documentation for UCAN
# UCAN container Specification
> Documentation for UCAN container Specification
# 0 Abstract
[Section titled “0 Abstract”](#0-abstract)
[User-Controlled Authorization Network (UCAN)](/specification/) is a trustless, secure, local-first, user-originated authorization and revocation scheme. This document describes a container format for transmitting one or more UCAN tokens as bytes, regardless of the transport.
# 1 Introduction
[Section titled “1 Introduction”](#1-introduction)
The UCAN spec itself is transport agnostic. This specification describes how to transfer one or more [UCAN](/specification/) tokens bundled together, regardless of the transport.
# 2 Container format
[Section titled “2 Container format”](#2-container-format)
## 2.1 Inner structure
[Section titled “2.1 Inner structure”](#21-inner-structure)
UCAN tokens, regardless of their kind ([Delegation](/delegation/), [Invocation](/invocation/), [Revocation](/revocation/), [Promise](https://github.com/ucan-wg/promise/tree/v1-rc1)) MUST be first signed and serialized into DAG-CBOR bytes according to their respective specification. As the token’s CID is not part of the serialized container, any CID returned by this operation is to be ignored.
All the tokens’ bytes MUST be assembled in a [CBOR](https://www.rfc-editor.org/rfc/rfc8949.html) array. The ordering of tokens in the array MUST NOT matter. This array SHOULD NOT have duplicate entries and MUST be ordered bytewise to ensure a deterministic encoding.
That array is then inserted as the value under the `ctn-v1` string key, in a CBOR map. There MUST NOT be other keys.
For clarity, the CBOR shape is given below:
```json
{
"ctn-v1": [
,
,
,
]
}
```
## 2.2 Serialization
[Section titled “2.2 Serialization”](#22-serialization)
To serialize the container into bytes, the inner CBOR structure MUST then be serialized into bytes according to the CBOR specification. The resulting bytes MAY be compressed by a supported algorithm, then MAY be encoded with a supported base encoding.
The following compression algorithms are REQUIRED to be supported:
* [GZIP](https://datatracker.ietf.org/doc/html/rfc1952)
The following base encoding combinations are REQUIRED to be supported:
* base64, standard alphabet, padding
* base64, URL alphabet, no padding
The CBOR bytes MUST be prepended by a single byte header to indicate the selected combination of base encoding and compression. This header value MUST be set according to the following table:
| Header as hex | Header as ASCII | Base encoding | Compression |
| ------------- | --------------- | ----------------------- | -------------- |
| 0x40 | @ | raw bytes | no compression |
| 0x42 | B | base64 std padding | no compression |
| 0x43 | C | base64 url (no padding) | no compression |
| 0x4D | M | raw bytes | gzip |
| 0x4F | O | base64 std padding | gzip |
| 0x50 | P | base64 url (no padding) | gzip |
For clarity, the resulting serialization is in the form of ``.
# 3 FAQ
[Section titled “3 FAQ”](#3-faq)
## 3.1 Why not include the UCAN CIDs?
[Section titled “3.1 Why not include the UCAN CIDs?”](#31-why-not-include-the-ucan-cids)
Several attacks are possible if UCAN tokens aren’t validated. If CIDs aren’t validated, at least two attacks are possible: \[privilege escalation] and [cache poisoning](https://en.wikipedia.org/wiki/Cache_poisoning), as UCAN delegation proofs depends on a correct hash-linked structure.
By not including the CID in the container, the recipient is forced to hash (and thus validate) the CIDs for each token. If presented with a claimed CID paired with the token bytes, implementers could ignore CID validation, breaking a core part of the proof chain security model. Hash functions are very fast on a couple kilobytes of data so the overhead is still very low. It also significantly reduces the size of the container.
## 3.2 Why compress? Why not always compress?
[Section titled “3.2 Why compress? Why not always compress?”](#32-why-compress-why-not-always-compress)
Compression is a relatively demanding operation. As such, using it is a trade-off between size on the wire and CPU/memory usage, both when writing and reading a container. The transport itself can make compression worthwhile or not: for example, HTTP/2 and HTTP/3 headers are already compressed, but HTTP/1 headers are not. This being highly contextual, the choice is left to the final implementer.
# 4 Implementation recommendations
[Section titled “4 Implementation recommendations”](#4-implementation-recommendations)
## 4.1 Dissociate reader and writer
[Section titled “4.1 Dissociate reader and writer”](#41-dissociate-reader-and-writer)
While it is tempting to write a single implementation to read and write a container, it is RECOMMENDED to separate the implementation into a reader and a writer. The writer can simply accept arbitrary tokens as bytes, while the reader provides a read-only view with convenient access functions.
# 5 Acknowledgments
[Section titled “5 Acknowledgments”](#5-acknowledgments)
Many thanks to all the Fission team and in particular to [Brooklyn Zelenka](https://github.com/expede) for creating and pushing [UCAN](/specification/) and other critical pieces like [WNFS](https://github.com/wnfs-wg), and generally being awesome and supportive people.
# UCAN Delegation Specification
> [Abstract]: #abstract...
# Abstract
[Section titled “Abstract”](#abstract)
This specification describes the representation and semantics for delegating attenuated authority between principals. UCAN Delegation provides a cryptographically verifiable container, batched capabilities, hierarchical authority, and a minimal syntactically-driven policy language.
# Introduction
[Section titled “Introduction”](#introduction)
UCAN Delegation is a delegable certificate capability system with runtime-extensibility, ad hoc conditions, cacheability, and focused on ease of use and interoperability. Delegations act as a proofs for [UCAN Invocation](/invocation/)s.
Delegation provides a way to “transfer authority without transferring cryptographic keys”. As an authorization system, it is more interested in “what can be done” than a list of “who can do what”. For more on how Delegation fits into UCAN, please refer to the [high level spec](/specification/).
# [UCAN Envelope](/specification/#envelope) Configuration
[Section titled “UCAN Envelope Configuration”](#ucan-envelope-configuration)
## Type Tag
[Section titled “Type Tag”](#type-tag)
The UCAN envelope tag for UCAN Delegation MUST be set to `ucan/dlg@1.0.0`.
## Delegation Payload
[Section titled “Delegation Payload”](#delegation-payload)
The Delegation payload MUST describe the authorization claims, who is involved, and its validity period.
| Field | Type | Required | Description |
| ------- | ------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------ |
| `iss` | `DID` | Yes | Issuer DID (sender). All [DID](https://www.w3.org/TR/did-core/)s are represented as string URLs. |
| `aud` | `DID` | Yes | Audience DID (receiver) |
| `sub` | `DID \| null` | Yes | Principal that the chain is about (the [Subject](#subject)) |
| `cmd` | `String` | Yes | The [Command](#command) to eventually invoke |
| `pol` | `Policy` | Yes | [Policy](#policy) |
| `nonce` | `Bytes` | Yes | Nonce |
| `meta` | `{String : Any}` | No | \[Meta] (asserted, signed data) — is not delegated authority |
| `nbf` | `Integer` (53-bits[1](#user-content-fn-js-num-size)) | No | “Not before” UTC Unix Timestamp in seconds (valid from) |
| `exp` | `Integer \| null` (53-bits[1](#user-content-fn-js-num-size)) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) |
# Capability
[Section titled “Capability”](#capability)
A capability is the semantically-relevant claim of a delegation. They MUST take the following form:
| Field | Type | Required | Description |
| ----- | ------------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `sub` | `DID \| null` | Yes | The [Subject](#subject) that this Capability is about |
| `cmd` | `Command` | Yes | The [Command](#command) of this Capability |
| `pol` | `Policy` | Yes | Additional constraints on eventual Invocation arguments, expressed in the [UCAN Policy Language](#policy) |
Here is an illustrative example:
```js
{
// ...
"sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
"cmd": "/blog/post/create",
"pol": [
["==", ".status", "draft"],
["all", ".reviewer", ["like", ".email", "*@example.com"]],
["any", ".tags",
["or",
["==", ".", "news"],
["==", ".", "press"]]]
]
}
```
## Subject
[Section titled “Subject”](#subject)
The Subject MUST be the DID that initiated the delegation chain, or an explicit `null`. Declaring a DID is RECOMMENDED. For more on the `null`, please see the [Powerline](#powerline) section.
```js
{
"sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
// ...
}
```
### Resource
[Section titled “Resource”](#resource)
Unlike [Subjects](#subject) and [Commands](#command), Resources are *semantic* rather than syntactic. The Resource is the “what” that a capability describes.
By default, the Resource of a capability is the Subject. This makes the delegation chain self-certifying.
```js
{
"sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp", // Subject
// ...
}
```
In the case where access to an [external resource](/specification/#wrapping-existing-systems) is delegated, the Subject MUST own the relationship to the Resource. The Resource SHOULD be referenced by a `uri` key in the relevant \[Conditions], except where it would be clearer to do otherwise. This MUST be defined by the Subject and understood by the executor.
```js
{
"sub": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
"cmd": "/crud/create",
"pol": [
["==", ".url", "https://example.com/blog/"], // Resource managed by the Subject
// ...
],
// ...
}
```
### Powerline
[Section titled “Powerline”](#powerline)
> \[!WARNING] Similar to `cmd: "/"` and `pol: []`, this feature (`sub: null`) is very powerful. Use with care.
A “Powerline”[2](#user-content-fn-powerbox) is a pattern for automatically delegating *all* future delegations to another agent regardless of [Subject](#subject). This is achieved by explicitly setting the [Subject](#subject) (`sub`) field to `null`. At [Validation](#validation) time, the [Subject](#subject) MUST be substituted for the directly prior Subject given in the delegation chain. All other fields MUST continue to validate as normal (e.g. [principal alignment](#principal-alignment), [time bounds](#time-bounds), and so on).
Powerline delegations MUST NOT be used as the root delegation to a resource. A priori there is no such thing as a `null` subject.
A very common use case for Powerline is providing a stable DID across multiple agents (e.g. representing a user with multiple devices). This enables the automatic sharing of authority across their devices without needing to share keys or set up a threshold scheme. It is also flexible, since a Powerline delegation MAY be [revoked](/revocation/).
```
sequenceDiagram
autonumber
participant Email Server
participant Alice Root
participant Alice's Phone
participant Alice's Tablet
participant Alice's Laptop
Alice Root ->> Alice's Phone: Delegate {sub: null, cmd: "/"}
Alice Root ->> Alice's Tablet: Delegate {sub: null, cmd: "/"}
Alice Root ->> Alice's Laptop: Delegate {sub: null, cmd: "/"}
Email Server ->> Alice Root: Delegate {sub: "did:example:email", cmd: "/msg/send"}
Alice's Tablet -->> Email Server: INVOKE! {sub: "did:example:email", cmd: "/msg/send", proofs: [❹,❷]}
```
Powerline MAY include other restrictions, such as [time bounds](#time-bounds), [Commands](#command), and [Policies](#policy). For example, the ability to automatically redelegate read-only access to arbitrary CRUD resources could be expressed as:
```js
{
"iss": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
"aud": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
"sub": null, // 👈 ⚡ Powerline
"cmd": "/crud/read",
"pol": [],
// ...
}
```
## Command
[Section titled “Command”](#command)
The [Command](/specification/#33-command) MUST be a `/` delimited path describing set of commands delegated. Delegation covers exact [Command](#command) specified and all the commands described by a paths nested under that specified command.
> \[!NOTE] The command path syntax is designed to support forward compatible protocol extensions. Backwards-compatible capabilities MAY be introduced as command subpaths.
> \[!WARNING] By definition `"/"` implies all of the commands available on a resource, and SHOULD be used with great care.
# Policy
[Section titled “Policy”](#policy)
UCAN Delegation uses predicate logic statements extended with [jq](https://jqlang.github.io/jq/)-inspired selectors as a policy language. Policies are syntactically driven, and MUST constrain the `args` field of an eventual [Invocation](/invocation/).
A Policy is always given as an array of predicates. This top-level array is implicitly treated as a logical `and`, where `args` MUST pass validation of every top-level predicate.
Policies are structured as trees. With the exception of subtrees under `any`, `or`, and `not`, every leaf MUST evaluate to `true`.
A Policy is an array of statements. Every statement MUST take the form `[operator, selector, argument]` except for connectives (`and`, `or`, `not`) which MUST take the form `[operator, argument]`.
```ipldsch
-- Statements
type Statement union {
| Equality
| Like
| Inequality
| Connective
| Negation
| Quantifier
}
-- Equality
type EqOp enum {
| Eq ("==")
| Neq ("!=")
}
type Equality struct {
op EqOp
sel Selector
val Any
} representation tuple
type LikeOp enum {
| Like ("like")
}
type Like struct {
op LikeOp
sel Selector
str Wildcard
} representation tuple
-- Inequality
type IneqOp enum {
| GT (">")
| GTE (">=")
| LT ("<")
| LTE ("<=")
}
type Inequality struct {
op IneqOp
sel Selector
val Number
} representation tuple
-- Connectives
type NegateOp {
| Not ("not")
}
type Negation struct {
op NegateOp
smt Statement
} representation tuple
type ConnectiveOp enum {
| And ("and")
| Or ("or")
}
type Connective struct {
op ConnectiveOp
smts [Statement]
} representation tuple
-- Quantification
type QuantifierOp enum {
| All ("all")
| Any ("any")
}
type Quantifier struct {
op QuantiefierOp
sel Selector
smt Statement
} representation tuple
-- Primitives
type Selector = string
type Number union {
| NumInt int
| NumFloat float
} representation kinded
type Wildcard = string
```
## Comparisons
[Section titled “Comparisons”](#comparisons)
| Operator | Arguments | Example |
| -------- | ------------------------------ | -------------------------------- |
| `==` | `Selector, IPLD` | `["==", ".a", [1, 2, {"b": 3}]]` |
| `!=` | `Selector, IPLD` | `["!=", ".a", [1, 2, {"b": 3}]]` |
| `<` | `Selector, (integer \| float)` | `["<", ".a", 1]` |
| `<=` | `Selector, (integer \| float)` | `["<=", ".a", 1]` |
| `>` | `Selector, (integer \| float)` | `[">", ".a", 1]` |
| `>=` | `Selector, (integer \| float)` | `[">=", ".a", 1]` |
Literal equality (`==`) MUST match the resolved selector to entire IPLD argument. This is a “deep comparison”.
Literal inequality (`!=`) is equivalent to `["not", ["==", selector, value]]`.
Numeric inequalities MUST be agnostic to numeric type. In other words, the decimal representation is considered equivalent to an integer (`1 == 1.0 == 1.00`). Attempting to compare a non-numeric type MUST return false and MUST NOT throw an exception.
## Glob Matching
[Section titled “Glob Matching”](#glob-matching)
| Operator | Arguments | Example |
| -------- | ------------------- | ------------------------------------- |
| `like` | `Selector, Pattern` | `["like", ".email", "*@example.com"]` |
Glob patterns MUST only include one special character: `*` (“wildcard”). There is no single character matcher. As many `*`s as desired MAY be used. Non-wildcard `*`-literals MUST be escaped (`"\*"`). Attempting to match on a non-string MUST return false and MUST NOT throw an exception.
The wildcard represents zero-or-more characters. The following string literals MUST pass validation for the pattern `"Alice\*, Bob*, Carol.`:
* `"Alice*, Bob, Carol."`
* `"Alice*, Bob, Dan, Erin, Carol."`
* `"Alice*, Bob , Carol."`
* `"Alice*, Bob*, Carol."`
The following MUST NOT pass validation for that same pattern:
* `"Alice*, Bob, Carol"` (missing the final `.`)
* `"Alice*, Bob*, Carol!"` (final `.` MUST NOT be treated as a wildcard)
* `"Alice, Bob, Carol."` (missing the `*` after `Alice`)
* `"Alice Cooper, Bob, Carol."` (the `*` after `Alice` is an escaped literal in the pattern)
* `" Alice*, Bob, Carol. "` (whitespace in the pattern is significant)
## Connectives
[Section titled “Connectives”](#connectives)
Connectives add context to their enclosed statement(s).
| Operator | Argument | Example |
| -------- | ------------- | ------------------------------------------ |
| `and` | `[Statement]` | `["and", [[">", ".a", 1], [">", ".b", 2]]` |
| `or` | `[Statement]` | `["or", [[">", ".a", 1], [">", ".b", 2]]` |
| `not` | `Statement` | `["not", [">", ".a", 1]]` |
### And
[Section titled “And”](#and)
`and` MUST take an arbitrarily long array of statements, and require that every inner statement be true. An empty array MUST be treated as true.
```js
// Data
{ name: "Katie", age: 35, nationalities: ["Canadian", "South African"] }
["and", []]
// ⬆️ true
["and", [
["==", ".name", "Katie"],
[">=", ".age", 21]
]]
// ⬆️ true
["and", [
["==", ".name", "Katie"],
[">=", ".age", 21],
["==", ".nationalities", ["American"]] // ️⬅️ false
]]
// ⬆️ false
```
### Or
[Section titled “Or”](#or)
`or` MUST take an arbitrarily long array of statements, and require that at least one inner statement be true. An empty array MUST be treated as true.
```js
// Data
{ name: "Katie", age: 35, nationalities: ["Canadian", "South African"] }
["or", []]
// ⬆️ true
["or", [
["==", ".name", "Katie"], // ⬅️ true
[">", ".age", 45]
]]
// ⬆️ true
```
### Not
[Section titled “Not”](#not)
`not` MUST invert the truth value of the inner statement. For example, if `["==", ".a", 1]` were false (`.a` is not 1), then `["not", ["==", ".a", 1]]` would be true.
```js
// Data
{ name: "Katie", nationalities: ["Canadian", "South African"] }
["not",
["and", [
["==", ".name", "Katie"],
["==", ".nationalities", ["American"]] // ⬅️ false
]]]
// ⬆️ true
```
## Quantification
[Section titled “Quantification”](#quantification)
When a selector resolves to a collection (an array or map), quantifiers provide a way to extend `and` and `or` to their contents. Attempting to quantify over a non-collection MUST return false and MUST NOT throw an exception.
Quantifying over an array is straightforward: it MUST apply the inner statement to each array value. Quantifying over a map MUST extract the values (discarding the keys), and then MUST proceed on the values the same as if it were an array.
| Operator | Argument(s) | Example |
| -------- | ----------------------- | ------------------------------ |
| `all` | `Selector, [Statement]` | `["all", ".a" [">", ".b", 1]]` |
| `any` | `Selector, [Statement]` | `["any", ".a" [">", ".b", 1]]` |
`all` extends `and` over collections. `any` extends `or` over collections. For example:
```js
const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}
const statement = ["all", ".a", [">", ".b", 0]]
// Outer Selector Substitution
["all", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], [">", ".b", 0]]
// Predicate Reduction
["and", [
[">", 1, 0],
[">", 2, 0],
[">", null, 0]
]]
["and", [
true,
true,
false // ⬅️
]]
false // ❌
```
```js
const args = {"a": [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}]}
const statement = ["any", ".a", ["==", ".b", 2]]
// Reduction
["any", [{"b": 1}, {"b": 2}, {"z": [7, 8, 9]}], ["==", ".b", 2]]
["or", [
["==", 1, 2],
["==", 2, 2],
["==", null, 2]
]]
["or", [
false,
true, // ⬅️
false
]]
true // ✅
```
### Nested Quantification
[Section titled “Nested Quantification”](#nested-quantification)
Quantified statements MAY be nested. For example, the below states that someone with the email `fraud@example.com` is required to be among the receipts of every newsletter.
```js
["all", ".newsletters",
["any", ".recipients",
["==", ".email", "fraud@example.com"]]]
```
## Selectors
[Section titled “Selectors”](#selectors)
Selector syntax is closely based on [jq](https://jqlang.github.io/jq/)’s “filters”. They operate on an [Invocation](/invocation/)’s `args` object.
Selectors MUST only include the following features:
| Selector Name | Examples | Notes |
| ---------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Identity | `.` | Take the entire argument |
| Dotted field name | `.foo`, `.bar0_` | Shorthand for selecting in a map by key (with exceptions, see below) |
| Unambiguous field name | `["."]`, `["$_*"], ["1"]` | Select in a map by arbitrary key |
| Collection values | `[]` | Expands out all of the children that match the remaining path. On lists this is a noop. On maps, this extracts values. |
| List index | `[0]`, `[42]` | The list element of a list by 0-index. |
| Negative list index | `[-1]`, `[-42]` | The list element by index from the end. `-1` is the index for the last element. |
| List slices | `[7:11]`, `[2:]`, `[:42]`, `[0:-2]` | The range of elements by their indices. |
| Optional | `.foo?`, `["nope"]?` | Returns `null` on what would otherwise fail |
Every selection MUST begin and/or end with a single dot. Multiple dots (e.g. `..`, `...`) MUST NOT be used anywhere in a selector.
The optional operator is idempotent, and repeated optionals (`.foo???`) MUST be treated as a single one.
For example, consider the following `args` from an `Invocation`:
```json
{
"args": {
"from": "alice@example.com",
"to": ["bob@example.com", "carol@not.example.com", "dan@example.com"],
"cc": ["fraud@example.com"],
"title": "Meeting Confirmation",
"body": "I'll see you on Tuesday"
}
}
```
| Selector | Returned Value |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ```
"."
``` | ```json
{
"from": "alice@example.com",
"to": ["bob@example.com", "carol@not.example.com", "dan@example.com"],
"cc": ["fraud@example.com"],
"title": "Meeting Confirmation",
"body": "I'll see you on Tuesday"
}
``` |
| ```
".title"
``` | ```json
"Meeting Confirmation"
``` |
| ```
".cc"
``` | ```json
["fraud@example.com"]
``` |
| ```
".to[1]"
``` | ```json
"carol@not.example.com"
``` |
| ```
".to[-1]"
``` | ```json
"dan@example.com"
``` |
| ```
".to[99]?"
``` | ```json
null
``` |
| ```
".to[99]"
``` | fail to resolve |
Selectors are resolved left to right, and MUST return early when a segment can’t be resolved, even if a later segment is optional.
### Selecting on Bytes
[Section titled “Selecting on Bytes”](#selecting-on-bytes)
Bytes MAY be selected into. When doing so, they MUST be treated as a byte array (`[u8]`), and MUST NOT be treated as a Base64 string or any other representation.
```js
// DAG-JSON
{ "/": { "bytes": "1qnBjPjE" } }
// Hexadecimal
0xd6 0xa9 0xc1 0x8c 0xf8 0xc4
// Selector
".[3]"
// ⬆️ 0x8c = 140
```
### Differences from jq
[Section titled “Differences from jq”](#differences-from-jq)
[jq](https://jqlang.github.io/jq/) is a much larger language than UCAN’s selectors. jq includes features like pipes, arithmetic, regexes, assignment, recursive descent, and so on which are not supported in the UCAN Policy language, and thus MUST NOT be implemented in UCAN.
jq produces streams of values (a distinct concept from arrays), in contrast to UCAN argument selectors which always return an IPLD value. This introduces the primary difference between jq and UCAN argument selectors is how to treat output of the optional (`?`) operator: UCAN’s optional selector operator MUST return `null` for the failure case.
## Validation
[Section titled “Validation”](#validation)
Validation involves substituting the values from the `args` field into the Policy, and evaluating the predicate. Since Policies are tree structured, selector substitution and predicate evaluation MAY proceed in any order.
If a selector cannot be resolved (there is no value at that path), the associated statement MUST return false, and MUST NOT throw an exception. Note that for consistent semantics, selecting a missing keys on a map MUST return `null` (but nested selectors without an optional MUST then fail the predicate).
Below is a step-by-step evaluation example:
```js
{ // Invocation
"cmd": "/msg/send",
"args": {
"from": "alice@example.com",
"to": ["bob@example.com", "carol@not.example.com"],
"title": "Coffee",
"body": "Still on for coffee"
},
// ...
}
{ // Delegation
"cmd": "/msg",
"pol": [
["==", ".from", "alice@example.com"],
["any", ".to", ["like", ".", "*@example.com"]]
],
// ...
}
```
```js
[ // Extract policy
["==", ".from", "alice@example.com"],
["any", ".to", ["like", ".", "*@example.com"]]
]
[ // Resolve selectors
["==", "alice@example.com", "alice@example.com"],
["any", ["bob@example.com", "carol@elsewhere.example.com"], ["like", ".", "*@example.com"]]
]
[ // Expand quantifier
["==", "alice@example.com", "alice@example.com"],
["or", [
["like", "bob@example.com", "*@example.com"]
["like", "carol@elsewhere.example.com", "*@example.com"]]
]
]
[ // Evaluate first predicate
true,
["or", [
["like", "bob@example.com", "*@example.com"]
["like", "carol@elsewhere.example.com", "*@example.com"]]]
]
[ // Evaluate second predicate's children
true,
["or", [true, false]]
]
[ // Evaluate second predicate
true,
true
]
// Evaluate top-level `and`
true
```
Any arguments MUST be taken verbatim and MUST NOT be further adjusted. For more flexible validation of Arguments, use \[Conditions].
Note that this also applies to arrays and objects. For example, the `to` array in this example is considered to be exact, so the Invocation fails validation in this case:
```js
// Delegation
{
"cmd": "/email/send",
"pol": [
["==", ".from", "alice@example.com"],
["any", ".to", ["like", ".", "*@example.com"]]
]
// ...
}
// VALID Invocation
{
"cmd": "/email/send",
"args": {
"from": "alice@example.com",
"to": ["bob@example.com", "carol@elsewhere.example.com"],
"title": "Coffee",
"body": "Still on for coffee"
},
// ...
}
// INVALID Invocation
{
"cmd": "/email/send",
"args": {
"from": "alice@example.com",
"to": ["carol@elsewhere.example.com"], // No match for `*@example.com`
"title": "Coffee",
"body": "Still on for coffee"
},
// ...
}
```
## Semantic Conditions
[Section titled “Semantic Conditions”](#semantic-conditions)
Other semantic conditions that are not possible to fully express syntactically (e.g. current day of week) MUST be handled as part of Invocation execution. This is considered out of scope of the UCAN Policy language. The RECOMMENDED strategy to express constrains that involve side effects (like day of week) is to include that information in the argument shape for that Command (i.e. have a `"day_of_week": "friday"` field).
# Token Validation
[Section titled “Token Validation”](#token-validation)
Validation of a UCAN chain MAY occur at any time, but MUST occur upon receipt of an [Invocation](/invocation/) *prior to execution*. While proof chains exist outside of a particular delegation (and are made concrete in [UCAN Invocation](/invocation/)s), each delegate MUST store one or more valid delegations chains for a particular claim.
Each capability has its own semantics, which needs to be interpretable by the [Executor](/specification/#31-roles). Therefore, a validator MUST NOT reject all capabilities when one that is not relevant to them is not understood. For example, if a Condition fails a delegation check at execution time, but is not relevant to the invocation, it MUST be ignored.
If *any* of the following criteria are not met, the UCAN Delegation MUST be considered invalid:
1. [Time Bounds](#time-bounds)
2. [Principal Alignment](#principal-alignment)
3. [Signature Validation](#signature-validation)
Additional constraints MAY be placed on Delegations by specs that use them (notably [UCAN Invocation](/invocation/)).
## Time Bounds
[Section titled “Time Bounds”](#time-bounds)
A UCAN’s time bounds MUST NOT be considered valid if the current system time is before the `nbf` field or after the `exp` field. This is called the “validity period.” Proofs in a chain MAY have different validity periods, but MUST all be valid at execution-time. This has the effect of making a delegation chain valid between the latest `nbf` and earliest `exp`.
```js
// Pseudocode
const ensureTime = (delegationChain, now) => {
delegationChain.forEach((ucan) => {
if (!!ucan.nbf && now < can.nbf) {
throw new Error(`Delegation is not yet valid, but will become valid at ${ucan.nbf}`)
}
if (ucan.exp !== null && now > ucan.exp) {
throw new Error(`Delegation expired at ${ucan.exp}`)
}
})
}
```
## Principal Alignment
[Section titled “Principal Alignment”](#principal-alignment)
In delegation, the `aud` field of every proof MUST match the `iss` field of the UCAN being delegated to. This alignment MUST form a chain back to the Subject for each resource.
This calculation MUST NOT take into account [DID fragment](https://www.w3.org/TR/did-core/#terminology)s. If present, fragments are only intended to clarify which of a DID’s keys was used to sign a particular UCAN, not to limit which specific key is delegated between. Use `did:key` if delegation to a specific key is desired.
```
flowchart RL
invoker((👨 Dan's DID))
subject((👩 Alice's DID))
subject -- controls --> resource[(Storage)]
rootCap -- references --> resource
subgraph Delegations
subgraph root [Root UCAN]
subgraph rooting [Root Issuer]
rootIss(iss: Alice)
rootSub(sub: Alice)
end
rootCap("cap: (Storage, crud/*)")
rootAud(aud: Bob)
end
subgraph del1 [Delegated UCAN]
del1Iss(iss: Bob) --> rootAud
del1Sub(sub: Alice)
del1Aud(aud: Carol)
del1Cap("cap: (Storage, crud/*)") --> rootCap
del1Sub --> rootSub
end
subgraph del2 [Delegated UCAN]
del2Iss(iss: Carol) --> del1Aud
del2Sub(sub: Alice)
del2Aud(aud: Dan)
del2Cap("cap: (Storage, crud/*)") --> del1Cap
del2Sub --> del1Sub
end
end
subgraph inv [Invocation]
invIss(iss: Dan)
args("args: [Storage, crud/update, (key, value)]")
invSub(sub: Alice)
prf("proofs")
end
invIss --> del2Aud
invoker --> invIss
args --> del2Cap
invSub --> del2Sub
rootIss --> subject
rootSub --> subject
prf --> Delegations
```
## Signature Validation
[Section titled “Signature Validation”](#signature-validation)
The \[Signature] field MUST validate against the `iss` DID from the \[Payload].
# Acknowledgments
[Section titled “Acknowledgments”](#acknowledgments)
Thank you to [Brendan O’Brien](https://github.com/b5) for real-world feedback, technical collaboration, and implementing the first Golang UCAN library.
Many thanks to [Hugo Dias](https://github.com/hugomrdias), [Mikael Rogers](https://github.com/mikeal/), and the entire DAG House team for the real world feedback, and finding inventive new use cases.
Thank you to [Blaine Cook](https://github.com/blaine) for the real-world feedback, ideas on future features, and lessons from other auth standards.
Many thanks to [Brian Ginsburg](https://github.com/bgins) and [Steven Vandevelde](https://github.com/icidasset) for their many copy edits, feedback from real world usage, maintenance of the TypeScript implementation, and tools such as [ucan.xyz](https://ucan.xyz).
Many thanks to [Christopher Joel](https://github.com/cdata) for his real-world feedback, raising many pragmatic considerations, and the Rust implementation and related crates.
Many thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for her handwritten(!) feedback on the design of UCAN, spearheading the [OCapN](https://github.com/ocapn/) initiative, and her related work on [ZCAP-LD](https://w3c-ccg.github.io/zcap-spec/).
Thanks to [Benjamin Goering](https://github.com/gobengo) for the many community threads and connections to [W3C](https://www.w3.org/) standards.
Thanks to [Michael Muré](https://github.com/MichaelMure) and [Steve Moyer](https://github.com/smoyer64) at [Infura](https://www.infura.io) for their detailed feedback on the selector design and thoughts on [ABNF](https://datatracker.ietf.org/doc/html/rfc5234) codegen, and an updated Golang UCAN implementation.
Thanks to [Juan Caballero](https://github.com/bumblefudge) for the numerous questions, clarifications, and general advice on putting together a comprehensible spec.
Thank you [Dan Finlay](https://github.com/danfinlay) for being sufficiently passionate about [OCAP](https://en.wikipedia.org/wiki/Object-capability_model) that we realized that capability systems had a real chance of adoption in an ACL-dominated world.
Thanks to the entire [SPKI WG](https://datatracker.ietf.org/wg/spki/about/) for their closely related pioneering work.
Many thanks to [Alan Karp](https://github.com/alanhkarp) for sharing his vast experience with capability-based authorization, patterns, and many right words for us to search for.
We want to especially recognize [Mark Miller](https://github.com/erights) for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large.
## Footnotes
[Section titled “Footnotes”](#footnote-label)
1. JavaScript has a single numeric type ([`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)) for both integers and floats. This representation is defined as a [IEEE-754](https://ieeexplore.ieee.org/document/8766229) double-precision floating point number, which has a 53-bit significand. [↩](#user-content-fnref-js-num-size) [↩2](#user-content-fnref-js-num-size-2)
2. For those familiar with design patterns for object capabilities, a “Powerline” is like a [Powerbox](https://sandstorm.io/how-it-works#powerbox) but adapted for the partition-tolerant, static token context of UCAN. [↩](#user-content-fnref-powerbox)
# UCAN Delegation Schema
> IPLD schema definition for UCAN Delegation
# UCAN Delegation Schema
[Section titled “UCAN Delegation Schema”](#ucan-delegation-schema)
This document contains the IPLD schema definition for UCAN Delegation.
```ipldsch
type Delegation struct {
p SignaturePayload
s Signature
}
type DelegationPayload struct {
iss DID
aud DID
sub DID
exp Integer
nbf Integer
can String
args {String : Any}
cond [{String : Any}]
}
type Signature union {
| batch BatchSig
| inline Bytes
} representation kinded
type BatchSig struct {
scp &[Any]
sig Bytes
}
```
# UCAN Examples
> Practical examples of using UCAN for authorization and delegation
This page contains practical examples of using UCAN for various authorization scenarios.
> **Note**: These examples use the v1.0.0-rc.1 UCAN specification and the `iso-ucan` JavaScript library. The examples show the current API patterns as of the latest specification version.
> **Implementation Note**: UCAN libraries for different languages may be at different specification versions. Always refer to your chosen library’s documentation for the exact API and supported features.
## File System Access
[Section titled “File System Access”](#file-system-access)
This example demonstrates how to delegate file read permissions using UCANs.
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
// Initialize delegation store for tracking capability chains
// In production, this might be backed by a database or persistent storage
const store = new Store(new MemoryDriver())
// Define file read capability with path validation schema
// The schema ensures all invocations include a valid file path
const FileReadCap = Capability.from({
schema: z.object({
path: z.string(), // Required: file path to read
}),
cmd: '/file/read', // UCAN v1 command identifier
})
// Create cryptographic identities for resource owner and accessor
// In a real system, these would be persistent identity keypairs
const alice = await EdDSASigner.generate() // Resource owner
const bob = await EdDSASigner.generate() // Requesting access
const nowInSeconds = Math.floor(Date.now() / 1000)
// Alice grants Bob permission to read files
// This delegation can be stored, transmitted, or embedded in applications
const delegation = await FileReadCap.delegate({
iss: alice, // Alice issues this capability
aud: bob, // Bob is authorized to use it
sub: alice, // Alice's resources are the subject
pol: [], // No additional policy constraints
exp: nowInSeconds + 3600, // Expires in 1 hour for security
})
// Store delegation to enable later invocation validation
// The store enables automatic delegation chain resolution
await store.set(delegation)
// Bob exercises the delegated capability to read a specific file
// This creates a cryptographically verifiable access request
const invocation = await FileReadCap.invoke({
iss: bob, // Bob is invoking the capability
sub: alice, // Alice's system will process the request
args: {
path: '/documents/report.pdf' // Specific file Bob wants to read
},
store, // Store containing the delegation proof
exp: nowInSeconds + 300, // Invocation expires in 5 minutes
})
```
## Example 2: API Rate Limiting
[Section titled “Example 2: API Rate Limiting”](#example-2-api-rate-limiting)
### Scenario
[Section titled “Scenario”](#scenario)
A service wants to delegate API access with rate limiting constraints.
### Implementation using iso-ucan
[Section titled “Implementation using iso-ucan”](#implementation-using-iso-ucan)
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
// Set up store for delegation management
const store = new Store(new MemoryDriver())
// Define the API read capability with rate limiting schema
const ApiReadCap = Capability.from({
schema: z.object({
rate_limit: z.object({
requests_per_hour: z.number(),
reset_time: z.string()
})
}),
cmd: '/api/users/read',
})
// Generate keypairs for service and client
const service = await EdDSASigner.generate()
const client = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
// Service delegates API access to client with rate limiting
const delegation = await ApiReadCap.delegate({
iss: service,
aud: client,
sub: service,
pol: [],
exp: nowInSeconds + 86400,
})
await store.set(delegation)
// Client can invoke the capability with rate limiting parameters
const invocation = await ApiReadCap.invoke({
iss: client,
sub: service,
args: {
rate_limit: {
requests_per_hour: 100,
reset_time: "hourly"
}
},
store,
exp: nowInSeconds + 300,
})
```
## Real-World Implementation Notes
[Section titled “Real-World Implementation Notes”](#real-world-implementation-notes)
### Complete Example with Account Creation
[Section titled “Complete Example with Account Creation”](#complete-example-with-account-creation)
Here’s a more comprehensive example following the pattern from the `iso-ucan` documentation:
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
const store = new Store(new MemoryDriver())
// Define account creation capability
const AccountCreateCap = Capability.from({
schema: z.object({
type: z.string(),
properties: z.object({
name: z.string(),
}).strict(),
}),
cmd: '/account/create',
})
// Define general account capability
const AccountCap = Capability.from({
schema: z.never(),
cmd: '/account',
})
// Generate keypairs for all parties
const owner = await EdDSASigner.generate()
const bob = await EdDSASigner.generate()
const invoker = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
// Owner delegates account capability to Bob
const ownerDelegation = await AccountCap.delegate({
iss: owner,
aud: bob,
sub: owner,
pol: [],
exp: nowInSeconds + 1000,
})
await store.set(ownerDelegation)
// Bob further delegates to invoker
const bobDelegation = await AccountCap.delegate({
iss: bob,
aud: invoker,
sub: owner,
pol: [],
exp: nowInSeconds + 1000,
})
await store.set(bobDelegation)
// Invoker can now create an account using the delegation chain
const invocation = await AccountCreateCap.invoke({
iss: invoker,
sub: owner,
args: {
type: 'account',
properties: {
name: 'John Doe',
},
},
store,
exp: nowInSeconds + 1000,
})
```
### Other UCAN Library Examples
[Section titled “Other UCAN Library Examples”](#other-ucan-library-examples)
While this guide focuses on `iso-ucan`, here are examples for other popular implementations:
#### JavaScript (iso-ucan)
[Section titled “JavaScript (iso-ucan)”](#javascript-iso-ucan)
The examples above demonstrate the current `iso-ucan` API. For the latest documentation, refer to the [`iso-ucan` package documentation](https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-ucan).
#### Rust (ucan)
[Section titled “Rust (ucan)”](#rust-ucan)
```rust
use ucan::builder::UcanBuilder;
use ucan::crypto::KeyMaterial;
let ucan = UcanBuilder::default()
.issued_by(&issuer_key)
.for_audience(&audience_did)
.with_lifetime(3600)
.claiming_capability(&capability)
.build()?;
```
#### Go (go-ucan)
[Section titled “Go (go-ucan)”](#go-go-ucan)
```go
import "github.com/ucan-wg/go-ucan"
token, err := ucan.NewBuilder().
IssuedBy(issuerKey).
ToAudience(audienceDID).
WithLifetime(time.Hour).
ClaimCapability(capability).
Build()
```
### Transport and Storage
[Section titled “Transport and Storage”](#transport-and-storage)
UCANs in `iso-ucan` are handled as structured objects that can be serialized for transport and storage as needed by your application.
## Best Practices
[Section titled “Best Practices”](#best-practices)
### Security Considerations
[Section titled “Security Considerations”](#security-considerations)
1. **Principle of Least Authority (PoLA)**: Only delegate the minimum necessary permissions
```javascript
// Good: Specific resource and capability
const FileReadCap = Capability.from({
schema: z.never(),
cmd: '/file/read',
})
// Avoid: Overly broad permissions
const AllFilesCap = Capability.from({
schema: z.never(),
cmd: '/file/*',
})
```
2. **Short Expiry Times**: Use the shortest practical expiration times
```javascript
// Good: Short-lived for temporary access
exp: nowInSeconds + 3600 // 1 hour
// Good: Longer for trusted devices
exp: nowInSeconds + 2592000 // 30 days
// Avoid: Very long expiration unless absolutely necessary
exp: nowInSeconds + 31536000 // 1 year
```
3. **Specific Capabilities**: Be as specific as possible in capability definitions
4. **Secure Key Management**: Keep private keys secure and never share them
### Implementation Guidelines
[Section titled “Implementation Guidelines”](#implementation-guidelines)
1. **Proper Error Handling**: Always handle UCAN verification failures gracefully
2. **Caching and Performance**: Cache verification results when appropriate
3. **Audit Logging**: Log UCAN usage for security auditing
## Resources and Further Reading
[Section titled “Resources and Further Reading”](#resources-and-further-reading)
### Specification Documents
[Section titled “Specification Documents”](#specification-documents)
* [UCAN Delegation Specification](/delegation/) - Core delegation mechanism
* [UCAN Invocation Specification](/invocation/) - How to exercise capabilities
* [UCAN Policy Language](/delegation/#policy) - Detailed policy syntax
* [UCAN Revocation Specification](/revocation/) - Capability revocation
### Implementation Libraries
[Section titled “Implementation Libraries”](#implementation-libraries)
* **JavaScript**: [`iso-ucan`](https://github.com/hugomrdias/iso-repo/tree/main/packages/iso-ucan) (NPM: `iso-ucan`)
* **Rust**: [`ucan`](/libraries/rust/)
* **Go**: [`go-ucan`](/libraries/go/)
* **Haskell**: [`hs-ucan`](https://github.com/fission-suite/fission)
### Community and Support
[Section titled “Community and Support”](#community-and-support)
* [UCAN Working Group](https://github.com/ucan-wg) - Main GitHub organization
* [Specification Repository](/specification/) - Latest specification updates
* [Community Discussions](https://github.com/ucan-wg/spec/discussions) - Ask questions and share ideas
> **Note**: UCAN is an evolving specification. Always refer to the latest version of the specifications and library documentation for the most current information.
# Getting Started with UCAN
> A beginner's guide to understanding and using User Controlled Authorization Network (UCAN)
This guide provides a quick introduction to UCAN (User Controlled Authorization Network) and walks you through building your first UCAN-enabled application.
> **Implementation Note**: This guide uses the JavaScript UCAN library (`iso-ucan`) for code examples. Different UCAN libraries may have varying APIs and support different specification versions. Always refer to your chosen library’s documentation for exact implementation details.
## What is UCAN?
[Section titled “What is UCAN?”](#what-is-ucan)
UCAN is a **trustless, secure, local-first, user-originated authorization scheme** that enables secure delegation of permissions without requiring centralized servers or sharing cryptographic keys.
### Core Benefits
[Section titled “Core Benefits”](#core-benefits)
* 🔑 **No shared secrets** - Delegate authority without sharing private keys
* 🌐 **Local-first** - Work without internet connectivity or central servers
* 🔗 **Chainable** - Create delegation chains across multiple parties
* 🛡️ **Cryptographically secure** - Built on proven public-key cryptography
* ⚡ **Locally verifiable** - No network calls needed for authorization
* 🔓 **Trustless** - No need to trust central authorities
### How UCAN Differs from Traditional Auth
[Section titled “How UCAN Differs from Traditional Auth”](#how-ucan-differs-from-traditional-auth)
| Traditional Auth (OAuth, etc.) | UCAN |
| -------------------------------- | ----------------------------------- |
| Centralized authorization server | Decentralized, peer-to-peer |
| Online verification required | Local verification possible |
| Shared secrets or tokens | Public-key cryptography |
| Revocation requires server | Revocation via cryptographic proofs |
## Core Concepts
[Section titled “Core Concepts”](#core-concepts)
### Capabilities vs Permissions
[Section titled “Capabilities vs Permissions”](#capabilities-vs-permissions)
**Traditional Access Control Lists (ACLs)** define who can do what:
```plaintext
Users Table:
- Alice: can read file.txt, write file.txt
- Bob: can read file.txt
- Charlie: can read file.txt, delete file.txt
```
UCAN uses **capabilities** - tokens that grant specific abilities:
```plaintext
Token A grants "read file.txt"
Token B grants "write file.txt"
```
### Delegation Chains
[Section titled “Delegation Chains”](#delegation-chains)
UCAN enables secure delegation without key sharing:
```
graph TD
A[Alice Root Authority] -->|delegates read| B[Bob Capability]
B -->|delegates read| C[Charlie Capability]
C -->|exercises capability| D[File System Grants Access]
```
Each delegation:
* ✅ Is cryptographically signed by the delegator
* ✅ Can be verified independently
* ✅ Can include additional restrictions (attenuation)
* ✅ Has built-in expiration
### Verification Without Servers
[Section titled “Verification Without Servers”](#verification-without-servers)
```
graph LR
A[Charlie's Request] --> B[Local Verification]
B --> C{Valid Chain?}
C -->|Yes| D[Grant Access]
C -->|No| E[Deny Access]
F[Alice's Public Key] --> B
G[Bob's Delegation] --> B
H[Charlie's Delegation] --> B
```
## Core Specifications
[Section titled “Core Specifications”](#core-specifications)
### [UCAN Delegation](/delegation/)
[Section titled “UCAN Delegation”](#ucan-delegation)
The foundation of UCAN - how to create and delegate capabilities. Delegation provides a way to “transfer authority without transferring cryptographic keys”.
**Key features:**
* Cryptographically verifiable container
* Batched capabilities with hierarchical authority
* Expiration times (`exp`) and optional “not before” (`nbf`)
* Policy language for fine-grained conditions
### [UCAN Invocation](/invocation/)
[Section titled “UCAN Invocation”](#ucan-invocation)
How to exercise the capabilities you’ve been delegated. An invocation expresses the intention to execute delegated capabilities.
**Key features:**
* Clear intention to act (command to perform)
* Proof of authorization via delegation chain
* Execution receipts
* Causal relationships between invocations
### [UCAN Revocation](/revocation/)
[Section titled “UCAN Revocation”](#ucan-revocation)
How to revoke capabilities after they’ve been issued.
**Key features:**
* Manual invalidation of delegations
* Revocation chains
* Last resort security mechanism
## Common Use Cases
[Section titled “Common Use Cases”](#common-use-cases)
### 1. File System Access
[Section titled “1. File System Access”](#1-file-system-access)
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
// Set up store for delegation management
// This store will track all delegations and resolve delegation chains
const store = new Store(new MemoryDriver())
// Define the file read capability with schema validation
// The schema ensures that invocations include a valid file path
const FileReadCap = Capability.from({
schema: z.object({
path: z.string(),
}),
cmd: '/file/read', // Command identifier following UCAN v1 spec
})
// Generate keypairs for Alice (resource owner) and Bob (delegatee)
// In production, these would be long-lived identity keypairs
const alice = await EdDSASigner.generate()
const bob = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
// Alice creates a delegation to Bob for file read access
// This grants Bob the authority to read files on Alice's behalf
const delegation = await FileReadCap.delegate({
iss: alice, // Alice issues this delegation
aud: bob, // Bob is the audience (recipient)
sub: alice, // Alice is the subject (resource owner)
pol: [], // No additional policy constraints
exp: nowInSeconds + 3600, // Expires in 1 hour for security
})
// Store the delegation for later lookup during invocation
// The store enables automatic delegation chain resolution
await store.set(delegation)
// Bob can now invoke this capability to read a specific file
// The invocation proves Bob's authority and specifies the action
const invocation = await FileReadCap.invoke({
iss: bob, // Bob is invoking the capability
sub: alice, // Alice's system will execute the action
args: {
path: '/documents/report.pdf' // Specific file to read
},
store, // Store containing the delegation proof
exp: nowInSeconds + 300, // Invocation expires in 5 minutes
})
```
### 2. API Access Control
[Section titled “2. API Access Control”](#2-api-access-control)
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
// Initialize store for managing delegations
const store = new Store(new MemoryDriver())
// Define API read capability with endpoint validation
// Schema ensures the endpoint parameter is a valid string
const ApiReadCap = Capability.from({
schema: z.object({
endpoint: z.string(),
}),
cmd: '/api/read', // Command for API read operations
})
// Create keypairs for service owner and client application
// Service owns the API, client needs read access to specific endpoints
const service = await EdDSASigner.generate()
const client = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
// Service grants client permission to read from API endpoints
// This could be part of an OAuth-like flow with UCAN tokens
const delegation = await ApiReadCap.delegate({
iss: service, // Service issues the delegation
aud: client, // Client receives the capability
sub: service, // Service owns the API resources
pol: [], // No additional constraints
exp: nowInSeconds + 86400, // Valid for 24 hours
})
// Store delegation to enable invocation validation
await store.set(delegation)
// Client invokes the capability to access a specific API endpoint
// This acts as an authorization proof for the API request
const invocation = await ApiReadCap.invoke({
iss: client, // Client is making the request
sub: service, // Service will process the request
args: {
endpoint: '/users/profile' // Specific API endpoint to access
},
store, // Store with delegation proof
exp: nowInSeconds + 300, // Request expires in 5 minutes
})
```
### 3. Document Collaboration
[Section titled “3. Document Collaboration”](#3-document-collaboration)
```javascript
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
// Set up store for delegation chain management
const store = new Store(new MemoryDriver())
// Define document edit capability with document ID validation
// Schema requires a valid document identifier for all operations
const DocEditCap = Capability.from({
schema: z.object({
docId: z.string(),
}),
cmd: '/doc/edit', // Command for document editing operations
})
// Generate keypairs for document owner and collaborator
// Owner controls document permissions, collaborator needs edit access
const owner = await EdDSASigner.generate()
const collaborator = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
// Owner delegates document editing rights to collaborator
// This enables secure document sharing without password sharing
const delegation = await DocEditCap.delegate({
iss: owner, // Document owner issues delegation
aud: collaborator, // Collaborator receives edit permission
sub: owner, // Owner maintains document ownership
pol: [], // No additional policy restrictions
exp: nowInSeconds + 7200, // Valid for 2 hours for secure session
})
// Store the delegation for validation during edit operations
await store.set(delegation)
// Collaborator can now edit the specific document
// This invocation serves as proof of authorization for the edit
const invocation = await DocEditCap.invoke({
iss: collaborator, // Collaborator is performing the edit
sub: owner, // Owner's system processes the edit
args: {
docId: 'doc-12345' // Specific document to edit
},
store, // Store containing the delegation proof
exp: nowInSeconds + 300, // Edit session expires in 5 minutes
})
```
## Next Steps
[Section titled “Next Steps”](#next-steps)
1. **Read the specifications** - Start with the [UCAN Delegation](/delegation/) spec
2. **Explore examples** - Check out the detailed [Examples](/guides/examples/) page
3. **Try an implementation** - Choose a UCAN library for your preferred language
4. **Join the community** - Participate in discussions on the UCAN GitHub
## Additional Resources
[Section titled “Additional Resources”](#additional-resources)
* [UCAN Website](https://ucan.xyz)
* [UCAN GitHub Working Group](https://github.com/ucan-wg/)
* [Implementation Libraries](/libraries/)
* **JavaScript**: [`iso-ucan`](/libraries/javascript/) (NPM: `iso-ucan`)
* **Rust**: [`ucan`](/libraries/rust/)
* **Go**: [`go-ucan`](/libraries/go/)
## Questions?
[Section titled “Questions?”](#questions)
Common questions about UCAN:
**Q: How is UCAN different from OAuth?** A: OAuth requires online authorization servers. UCAN is local-first and doesn’t need central authorities - verification happens locally using cryptographic proofs.
**Q: Can I revoke a UCAN after issuing it?** A: Yes, through the [UCAN Revocation](/revocation/) mechanism. Note that revocation requires the revocation message to be delivered to relevant parties, as UCAN is designed for partition tolerance.
**Q: Are UCANs secure?** A: UCANs use public-key cryptography and are designed with security best practices. However, they require proper implementation and key management.
**Q: Can I use UCAN with existing systems?** A: Yes! UCAN is designed to wrap existing authorization systems while adding its benefits.
# UCAN Invocation Specification
> [Abstract]: #abstract...
# Dependencies
[Section titled “Dependencies”](#dependencies)
* [UCAN Delegation](/delegation/)
# Abstract
[Section titled “Abstract”](#abstract)
UCAN Invocation defines a format for expressing the intention to execute delegated UCAN capabilities, and the attested receipts from an execution.
# Introduction
[Section titled “Introduction”](#introduction)
> Just because you can doesn’t mean that you should
>
> — Anonymous
> When authorization is communicated without such context, it’s like receiving a key in the mail with no hint about what to do with it \[…] After an object receives this message, she can invoke arg if she chooses, but why would she ever choose to do so?
>
> [Mark Miller](https://github.com/erights), E-lang Mailing List, 2000 Oct 18
UCAN is a chained-capability format. A UCAN contains all of the information that one would need to perform some task, and the provable authority to do so. This begs the question: can UCAN be used directly as an RPC language?
Some teams have had success with UCAN directly for RPC when the intention is clear from context. This can be successful when there is more information on the channel than the UCAN itself (such as an HTTP path that a UCAN is sent to). However, capability invocation contains strictly more information than delegation: all of the authority of UCAN, plus the command to perform the task.
## Intuition
[Section titled “Intuition”](#intuition)
### Car Keys
[Section titled “Car Keys”](#car-keys)
Consider the following fictitious scenario:
Akiko is going away for the weekend. Her good friend Boris is going to borrow her car while she’s away. They meet at a nearby cafe, and Akiko hands Boris her car keys. Boris now has the capability to drive Akiko’s car whenever he wants to. Depending on their plans for the rest of the day, Akiko may find Boris quite rude if he immediately leaves the cafe to go for a drive. On the other hand, if Akiko asks Boris to run some last minute pre-vacation errands for that require a car, she may expect Boris to immediately drive off.
To put this in terms closer to a UCAN flow:
```
sequenceDiagram
participant 🚗
actor Akiko
actor Boris
autonumber
Note over 🚗, Akiko: Akiko buys a car
🚗 -->> Akiko: Delegate(Drive 🚗)
Note over Akiko, Boris: Boris offers to run errands for Akiko
Boris -->> Akiko: Delegate(Boris to run errands)
Note over Akiko, Boris: Akiko gives Boris access to her car
Akiko -->> Boris: Delegate(Drive 🚗)
Note over 🚗, Boris: Akiko asks Boris to use her car to run errands
Akiko ->> Boris: Invoke!(Boris to run errands, using 🚗 (➌))
Boris ->> 🚗: Invoke!(Drive 🚗)
```
In the example above, steps ➌ and ➍ are qualitatively different:
* Step ➌ grants authority (to drive the car)
* Step ➍ is a *command* to do so
## Lazy vs Eager Evaluation
[Section titled “Lazy vs Eager Evaluation”](#lazy-vs-eager-evaluation)
In a referentially transparent setting, the description of a task is equivalent to having done so: a function and its results are interchangeable. [Programming languages with call-by-need semantics](https://en.wikipedia.org/wiki/Haskell) have shown that this can be an elegant programming model, especially for pure functions. However, *when* something will run can sometimes be unclear.
Most languages use eager evaluation. Eager languages must contend directly with the distinction between a reference to a function and a command to run it. For instance, in JavaScript, adding parentheses to a function will run it. Omitting them lets the program pass around a reference to the function without immediately invoking it.
```js
const message = () => alert("hello world")
message // Nothing happens
message() // A message interrupts the user
```
Delegating a capability is like the statement `message`. Task is akin to `message()`. It’s true that sometimes we know to run things from their surrounding context without the parentheses:
```js
[1, 2, 3].map(message) // Message runs 3 times
```
However, there is clearly a distinction between passing a function and invoking it. The same is true for capabilities: delegating the authority to do something is not the same as asking for it to be done immediately, even if sometimes it’s clear from context.
## Public Resources
[Section titled “Public Resources”](#public-resources)
A core part of UCAN’s design is interacting with the wider, non-UCAN world. Many resources are open to anyone to access, such as unauthenticated web endpoints. Unlike UCAN-controlled resources, an invocation on public resources is both possible, and a hard requirement for initiating a flow (e.g. sign up). These cases typically involve a reference passed out of band (such as a web link). Due to [designation with authorization](https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf), knowing the URI of a public resource is often sufficient for interacting with it. In these cases, the Executor MAY accept Invocations without having a “closed-loop” proof chain, but this SHOULD NOT be the default behavior.
## Promise Pipelining
[Section titled “Promise Pipelining”](#promise-pipelining)
[UCAN Promise](/promise/) extends UCAN Invocation with [distributed promise pipelines](http://erights.org/elib/distrib/pipeline.html). Promises are helpful in a wide variety of situations for efficiency and convenience. Implementations supporting UCAN Promises is RECOMMENDED.
# Concepts
[Section titled “Concepts”](#concepts)
## Roles
[Section titled “Roles”](#roles)
Task adds two new roles to UCAN: invoker and executor. The existing UCAN delegator and delegate principals MUST persist to the invocation.
| UCAN Field | Delegation | Invocation |
| ---------- | -------------------------------------- | -------------------------------------------------------------- |
| `iss` | Delegator: transfer authority (active) | Invoker: request task (active) |
| `sub` | — | Executor: perform task (default) |
| `aud` | Delegate: gain authority (passive) | Executor: perform task (if different from [Subject](#subject)) |
### Invoker
[Section titled “Invoker”](#invoker)
The invoker signals to the executor that a task associated with a UCAN SHOULD be performed.
The invoker MUST be the UCAN delegator. Their DID MUST be authenticated in the `iss` field of the contained UCAN.
### Executor
[Section titled “Executor”](#executor)
The executor is directed to perform some task described in the UCAN invocation by the invoker.
## Life Cycle
[Section titled “Life Cycle”](#life-cycle)
At a very high level:
* A [Task](#task) abstractly describes some Action to be run
* An Invocation attaches proven ([delegated](/delegation/)) authority to a [Task](#task), and requests it be run by a certain Agent
* A [Receipt](https://github.com/ucan-wg/receipt) MAY request that the Invoker enqueue more [Task](#task)s
```
erDiagram
Delegation }o--|{ Invocation: proves
Invocation }|--|| Task: requests
Invocation ||--|| Receipt: returns
Receipt |o--|{ Task: enqueues
```
## Anatomy
[Section titled “Anatomy”](#anatomy)
| Concept | Description |
| ------------------- | ------------------------------------------------------------------------------------ |
| [Command](#command) | Function application; a description of work to be performed |
| [Task](#task) | Contextual information for a [Command](#command), such as resource limits |
| Invocation | A request to perform some [Task](#task) based on [delegated](/delegation/) authority |
A request for some work to be done (or to “exercise your authority”) is an Invocation.
```
flowchart TD
subgraph Invocation
SignatureBytes["Signature (raw bytes)"]
subgraph SigPayload ["Signature Payload"]
VarsigHeader["Varsig Header"]
subgraph InvocationPayload ["Invocation Payload"]
iss
sub
cmd
args
prf
cause["cause (optional)"]
etc["..."]
end
end
end
cause -.->|CID| Receipt
```
As [noted in the introduction](#lazy-vs-eager-evaluation), there is a difference between a reference to a function and calling that function. The Invocation is a request to the [Executor](#executor) to perform the enclosed [Task](#task). [Invocation Payload](#invocation-payload)s are not executable until they have been signed and [Delegation](/delegation/) proofs validated.
Note that the Invocation MUST include the Signature envelope. An [Invocation Payload](#invocation-payload) on its own MUST NOT be considered a valid Invocation.
# [UCAN Envelope](https://github.com/ucan-wg/spec/blob/main/README.md#envelope) Configuration
[Section titled “UCAN Envelope Configuration”](#ucan-envelope-configuration)
## Type Tag
[Section titled “Type Tag”](#type-tag)
The UCAN envelope’s [payload tag](https://github.com/ucan-wg/spec/blob/main/README.md#envelope) MUST be `ucan/inv@1.0.0`.
## Invocation Payload
[Section titled “Invocation Payload”](#invocation-payload)
The Invocation Payload attaches sender, receiver, and provenance to the [Task](#task).
| Field | Type | Required | Description |
| ------- | --------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `iss` | `DID` | Yes | The DID of the [Invoker](#invoker) |
| `sub` | `DID` | Yes | The [Subject](#subject) being invoked |
| `aud` | `DID` | No | The DID of the intended [Executor](#executor) if different from the [Subject](#subject) |
| `cmd` | `String` | Yes | The [Command](#command) |
| `args` | `{String : Any}` | Yes | The [Command](#command)’s [Arguments](#arguments) |
| `prf` | `[&Delegation]` | Yes | [Delegation](/delegation/)s that prove the chain of authority |
| `meta` | `{String : Any}` | No | Arbitrary [Metadata](#metadata) |
| `nonce` | `Bytes` | Yes | A unique, random nonce |
| `exp` | `Integer \| null`[1](#user-content-fn-js-num) | Yes | The timestamp at which the Invocation becomes invalid |
| `iat` | `Integer`[1](#user-content-fn-js-num) | No | The timestamp at which the Invocation was created |
| `cause` | `&Receipt` | No | An OPTIONAL CID of the [Receipt](https://github.com/ucan-wg/receipt) that enqueued the [Task](#task) |
The `aud` field MUST be different from `sub`. If intended [Executor](#executor) is the [Subject](#subject) the `aud` field MUST be omitted which makes it implicitly equal to `sub`.
The `meta` field MUST be non empty map. If `meta` is empty map it MUST be omitted.
The shape of the `args` MUST be defined by the `cmd` field type. This is similar to how a method or message contain certain data shapes in object oriented or actor model languages respectively. Using the JavaScript analogy from the introduction, an Action is similar to wrapping a call in a closure:
```js
// Command
{
"cmd": "/msg/send",
"args": {
"from": "mailto:alice@example.com",
"to": [ "bob@example.com", "carol@example.com" ],
"subject": "hello",
"body": "world"
}
}
```
```js
// Pseudocode JS Analogy
() => msg.send({
from: "mailto:alice@example.com",
to: ["bob@example.com", "carol@example.com"],
subject: "hello",
body: "world"
})
```
### Agents
[Section titled “Agents”](#agents)
#### Issuer
[Section titled “Issuer”](#issuer)
The `iss` field MUST include the Issuer of the Invocation. This DID URL MUST dereference to the public key which proves the signature over the payload included in the envelope.
#### Subject
[Section titled “Subject”](#subject)
The REQUIRED `sub` field both parameterizes over a specific agent, and acts as a namespace for how to interpret the [Command](#command). This is especially critical for two parts of the life cycle:
1. Specifying a particular `sub` (and thus `aud`) when [enqueuing new Tasks](https://github.com/ucan-wg/receipt) in a Receipt
2. Indexing Receipts for reverse lookup and memoization
#### Audience
[Section titled “Audience”](#audience)
The OPTIONAL `aud` field specified the intended recipient of Invocation, otherwise the Audience MUST be assumed to be the [Subject](#subject). This is useful for message routing, command brokers, proxy execution, gateways, replicated state machines, and so on.
### Task
[Section titled “Task”](#task)
A Task is the subset of Invocation fields that uniquely determine the work to be performed. The nonce is important for distinguishing between non-idempotent executions of a Task by making the group together unique.
A Task MUST be uniquely defined by a Task ID that is the CID of the following fields as a keyed map:
* [Subject](#subject)
* [Command](#command)
* [Arguments](#arguments)
* [Nonce](#nonce)
Tasks that describe pure functions — or other strategies like fan-out racing — SHOULD have the same Task ID by using the same nonce.
#### Command
[Section titled “Command”](#command)
The REQUIRED Command (`cmd`) field MUST contain a concrete, dispatchable message that can be sent to the Executor. The Command MUST define the shape of the data in the [Arguments](#arguments).
### Arguments
[Section titled “Arguments”](#arguments)
The REQUIRED Arguments (`args`) field, MAY contain any parameters expected by the Command. The Subject MUST be considered the authority on the shape of this data. This field MUST be representable as a map or keyword list.
The Arguments MUST pass validation of the Policies on all of the [UCAN Delegations](/delegation/) in the [Proofs](#proofs) field. If any [Policy](/delegation/#policy) reports failure against the Invocation’s Arguments, the Invocation MUST be rejected.
#### Nonce
[Section titled “Nonce”](#nonce)
The REQUIRED `nonce` field MUST include a random nonce. This field ensures that multiple (non-idempotent) invocations are unique. The nonce SHOULD be empty (`0x`) for Commands that are idempotent (such as deterministic Wasm modules or standards-abiding HTTP PUT requests).
### Proofs
[Section titled “Proofs”](#proofs)
The `prf` field lists the path of authority from the [Subject](#subject) to the [Invoker](#invoker). This MUST be an array of CIDs pointing [Delegations](/delegation/) starting from the root Delegation (issued by the Subject), in strict sequence where the `aud` of the previous Delegation matches the `iss` of the next Delegation.
See [Proof Chains](#proof-chains) for more detail
#### Cause
[Section titled “Cause”](#cause)
The OPTIONAL `cause` field is a provenance claim describing which [Receipt](https://github.com/ucan-wg/receipt) requested it. This is helpful for tracking chains of Invocations.
#### Expiration
[Section titled “Expiration”](#expiration)
The REQUIRED nullable field `exp` defines when the Invocation SHOULD time out. Setting a timeout within a a few minutes is RECOMMENDED as it accounts for clock skew but limits the ability of an attacker to take advantage of an intercepted Invocation. In general, the smaller the time window the better. This is both expressive (defines a timeout, which is a best practice), and prevents replays.
#### Issued At
[Section titled “Issued At”](#issued-at)
The OPTIONAL `iat` field MAY contain an issuance timestamp. This time SHOULD NOT be trusted; it is only a claim by the Invoker of their system time. System clocks often have clock skew, or a Byzantine Invoker could claim an arbitrary time.
#### Metadata
[Section titled “Metadata”](#metadata)
The OPTIONAL `meta` field MAY include arbitrary metadata or extensible fields. For example, Wasm fuel, an internal job ID, references to GitHub Issues, and so on. This data MAY be used by the Executor.
## Attestation
[Section titled “Attestation”](#attestation)
An Invocation MAY be used to attest to some information. This is in effect a statement to the Issuer (without Audience) that never expires.
## Proof Chains
[Section titled “Proof Chains”](#proof-chains)
A Task MUST include the entire [UCAN Delegation](/delegation/) proof chain in the `prf` field. The chain MUST form a direct line of authority, starting from the root authority (`sub`) and ending at the invoker (`iss`). The `sub` throughout MUST match the `sub` of the Invocation.
```
flowchart RL
invoker((    Dan    ))
subject((    Alice    ))
subject -- controls --> resource[(Storage)]
rootCap -- references --> resource
subgraph Delegations
subgraph root [Root UCAN]
subgraph rooting [Root Issuer]
rootIss(iss: Alice)
rootSub(sub: Alice)
end
rootCap("(Storage, crud/*)")
rootAud(aud: Bob)
end
subgraph del1 [Delegated UCAN]
del1Iss(iss: Bob) --> rootAud
del1Sub(sub: Alice)
del1Aud(aud: Carol)
del1Cap("(Storage, crud/*)") --> rootCap
del1Sub --> rootSub
end
subgraph del2 [Delegated UCAN]
del2Iss(iss: Carol) --> del1Aud
del2Sub(sub: Alice)
del2Aud(aud: Dan)
del2Cap("(Storage, crud/*)") --> del1Cap
del2Sub --> del1Sub
end
end
subgraph inv [Invocation]
invIss(iss: Dan)
args("args: [Storage, crud/update, (key, value)]")
invSub(sub: Alice)
prf("proofs")
end
invIss --> del2Aud
invoker --> invIss
args --> del2Cap
invSub --> del2Sub
rootIss --> subject
rootSub --> subject
prf --> Delegations
```
## Examples
[Section titled “Examples”](#examples)
### Interacting with an HTTP API
[Section titled “Interacting with an HTTP API”](#interacting-with-an-http-api)
```js
// DAG-JSON
[
{"/": {"bytes": "bdNVZn+uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}},
{
"h": {"/": {"bytes": "NAHtAe0BE3E"}},
"ucan/inv@1.0.0": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"cmd": "/crud/create",
"args": {
"uri": "https://example.com/blog/posts",
"headers": {
"content-type": "application/json"
},
"payload": {
"title": "UCAN for Fun an Profit",
"body": "UCAN is great!",
"topics": ["authz", "journal"],
"draft": true
}
},
"nonce": {"/": {"bytes": "TWFueSBopvcs"}},
"meta": {
"env": "development",
"tags": ["blog", "post", "pr#123"]
},
"exp": 1697409438,
"prf": [
{"/": "zdpuAzx4sBrBCabrZZqXgvK3NDzh7Mf5mKbG11aBkkMCdLtCp"},
{"/": "zdpuApTCXfoKh2sB1KaUaVSGofCBNPUnXoBb6WiCeitXEibZy"},
{"/": "zdpuAoFdXRPw4n6TLcncoDhq1Mr6FGbpjAiEtqSBrTSaYMKkf"}
]
}
}
]
```
### Sending Email
[Section titled “Sending Email”](#sending-email)
```js
// DAG-JSON
[
{"/": {"bytes": "bdNVZn+uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}},
{
"h": {"/": {"bytes": "NAHtAe0BE3E"}},
"ucan/inv@1.0.0": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"cmd": "/msg/send",
"args": {
"from": "mailto:akiko@example.com",
"to": [ "boris@example.com", "carol@example.com" ],
"subject": "Coffee",
"body": "Let get coffee sometime and talk about UCAN Invocations!"
},
"nonce": {"/": {"bytes": "TWFueSBopZ2h0IHdvcs"}},
"prf": [{"/": "zdpuAzx4sBrBCabrZZqXgvK3NDzh7Mf5mKbG11aBkkMCdLtCp"}],
"exp": 1697409438
}
}
]
```
### Inline WebAssembly
[Section titled “Inline WebAssembly”](#inline-webassembly)
```js
[
{"/": {"bytes": "bdNVZn+uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}},
{
"h": {"/": {"bytes": "NAHtAe0BE3E"}},
"ucan/inv@1.0.0": {
"iss": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"aud": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"meta": {"fuel": 999999},
"nonce": {"/": {"bytes": ""}}, // NOTE: as stated above, idempotent Actions should always have the same nonce
"cmd": "/wasm/run",
"args": {
"mod": "data:application/wasm;base64,AHdhc21lci11bml2ZXJzYWwAAAAAAOAEAAAAAAAAAAD9e7+p/QMAkSAEABH9e8GowANf1uz///8UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////8AAAAACAAAACoAAAAIAAAABAAAACsAAAAMAAAACAAAANz///8AAAAA1P///wMAAAAlAAAALAAAAAAAAAAUAAAA/Xu/qf0DAJHzDx/44wMBqvMDAqphAkC5YAA/1mACALnzB0H4/XvBqMADX9bU////LAAAAAAAAAAAAAAAAAAAAAAAAAAvVXNlcnMvZXhwZWRlL0Rlc2t0b3AvdGVzdC53YXQAAGFkZF9vbmUHAAAAAAAAAAAAAAAAYWRkX29uZV9mAAAADAAAAAAAAAABAAAAAAAAAAkAAADk////AAAAAPz///8BAAAA9f///wEAAAAAAAAAAQAAAB4AAACM////pP///wAAAACc////AQAAAAAAAAAAAAAAnP///wAAAAAAAAAAlP7//wAAAACM/v//iP///wAAAAABAAAAiP///6D///8BAAAAqP///wEAAACk////AAAAAJz///8AAAAAlP///wAAAACM////AAAAAIT///8AAAAAAAAAAAAAAAAAAAAAAAAAAET+//8BAAAAWP7//wEAAABY/v//AQAAAID+//8BAAAAxP7//wEAAADU/v//AAAAAMz+//8AAAAAxP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU////pP///wAAAAAAAQEBAQAAAAAAAACQ////AAAAAIj///8AAAAAAAAAAAAAAADQAQAAAAAAAA==",
"fun": "add_one",
"params": [42]
}
}
}
]
```
# Prior Art
[Section titled “Prior Art”](#prior-art)
[ucanto RPC](https://github.com/web3-storage/ucanto) from [Storacha](https://github.com/storacha/ucanto) is a production system that uses UCAN as the basis for an RPC layer.
The Capability Transport Protocol ([CapTP](http://erights.org/elib/distrib/captp/index.html)) is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list.
The Object Capability Network ([OCapN](https://github.com/ocapn/)) protocol extends [CapTP](http://erights.org/elib/distrib/captp/index.html) with a generalized networking layer. It has implementations from the [Spritely Institute](https://spritely.institute/news/introducing-a-distributed-debugger-for-goblins-with-time-travel.html) and [Agoric](https://agoric.com/). At time of writing, it is in the process of being standardized.
[Electronic Rights Transfer Protocol (ERTP)](https://docs.agoric.com/guides/ertp/) builds on top of [CapTP](http://erights.org/elib/distrib/captp/index.html) concepts for blockchain & digital asset use cases.
[Cap ’n Proto RPC](https://capnproto.org/) is an influential RPC framework based on concepts from [CapTP](http://erights.org/elib/distrib/captp/index.html).
# Acknowledgements
[Section titled “Acknowledgements”](#acknowledgements)
Many thanks to [Mark Miller](https://github.com/erights) for his [trail blazing work](https://erights.org) on [capability systems](https://en.wikipedia.org/wiki/Capability-based_security).
Many thanks to [Luke Marsen](https://github.com/lukemarsden) and [Simon Worthington](https://github.com/simonwo) for their feedback on invocation model from their work on [Bacalhau](https://www.bacalhau.org/) and [IPVM](https://github.com/ipvm-wg).
Thanks to [Marc-Antoine Parent](https://github.com/maparent) for his discussions of the distinction between declarations and directives both in and out of a UCAN context.
Many thanks to [Quinn Wilton](https://github.com/QuinnWilton) for her discussion of speech acts, the dangers of signing canonicalized data, and ergonomics.
Thanks to [Blaine Cook](https://github.com/blaine) for sharing their experiences with [OAuth 1](https://oauth.net/1/), irreversible design decisions, and advocating for keeping the spec simple-but-evolvable.
Thanks to [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding.
Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capability systems and the programming models that they enable.
Thanks to [Rod Vagg](https://github.com/rvagg/) for the clarifications on IPLD Schema implicits and the general IPLD worldview
Many thanks to [Juan Caballero](https://github.com/bumblefudge) for his detailed questions and comments to help polish the spec.
## Footnotes
[Section titled “Footnotes”](#footnote-label)
1. JavaScript has a single numeric type ([`Number`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)) for both integers and floats. This representation is defined as a [IEEE-754](https://ieeexplore.ieee.org/document/8766229) double-precision floating point number, which has a 53-bit significand. [↩](#user-content-fnref-js-num) [↩2](#user-content-fnref-js-num-2)
# UCAN Invocation Schema
> IPLD schema definition for UCAN Invocation
# UCAN Invocation Schema
[Section titled “UCAN Invocation Schema”](#ucan-invocation-schema)
This document contains the IPLD schema definition for UCAN Invocation.
```ipldsch
type Invocation struct {
p SignaturePayload
s Signature
}
type SignaturePayload {
h VarsigHeader
i InvocationV1Payload
}
type InvocationV1Payload struct {
iss DID
sub DID
aud optional DID
cmd Command
args {String : Any}
nonce String
meta {String : Any}
prf [&Delegation]
exp optional Integer
cause optional &Receipt
}
```
# Go Implementation
> Documentation for Go Implementation
[ucan-wg/go-ucan](https://github.com/ucan-wg/go-ucan)
[](https://github.com/ucan-wg/go-ucan)
# go-ucan
[](https://github.com/ucan-wg/go-ucan/tags)[](https://github.com/ucan-wg/go-ucan/actions?query=)[](https://ucan-wg.github.io/go-ucan/dev/bench/)[](https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md)[](https://pkg.go.dev/github.com/ucan-wg/go-ucan)[](https://discord.gg/JSyFG6XgVM)
This is a go library to help the next generation of web and decentralized applications make use of UCANs in their authorization flows.
User Controlled Authorization Networks (UCANs) are a way of doing authorization where users are fully in control. OAuth is designed for a centralized world, UCAN is the distributed user controlled version.
## Resources
[Section titled “Resources”](#resources)
### Specifications
[Section titled “Specifications”](#specifications)
The UCAN specification is separated in multiple sub-spec:
* [Main specification](/specification/)
* [Delegation](https://github.com/ucan-wg/delegation/tree/v1_ipld)
* [Invocation](/invocation/)
* [Container](/container/)
Not implemented yet:
* [Revocation](https://github.com/ucan-wg/revocation/tree/first-draft)
* [Promise](https://github.com/ucan-wg/promise/tree/v1-rc1)
### Talks
[Section titled “Talks”](#talks)
* [Decentralizing Auth, and UCAN Too - Brooklyn Zelenka (2023)](https://www.youtube.com/watch?v=MuHfrqw9gQA)
* [What’s New in UCAN 1.0 - Brooklyn Zelenka (2024)](https://www.youtube.com/watch?v=-uohQzZcwF4)
## Status
[Section titled “Status”](#status)
`go-ucan` currently support the required parts of the UCAN specification: the main specification, delegation and invocation. It leverages the sibling project [`go-did-it`](https://github.com/MetaMask/go-did-it) for easy and extensible DID support.
Besides that, `go-ucan` also includes:
* support for encrypted values in token’s metadata
## Getting Help
[Section titled “Getting Help”](#getting-help)
For usage questions, usecases, or issues reach out to us in our `go-ucan` [Discord channel](https://discord.gg/3EHEQ6M8BC).
We would be happy to try to answer your question or try opening a new issue on Github.
## UCAN Gopher
[Section titled “UCAN Gopher”](#ucan-gopher)
Artwork by [Bruno Monts](https://www.instagram.com/bruno_monts). Thank you [Renee French](http://reneefrench.blogspot.com/) for creating the [Go Gopher](https://blog.golang.org/gopher)
## License
[Section titled “License”](#license)
This project is licensed under the dual license [Apache 2.0 OR MIT](https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md).
# UCAN Library Implementation Guide
> A comprehensive guide for implementing UCAN libraries in different programming languages
This guide provides detailed instructions and best practices for implementing UCAN (User Controlled Authorization Network) libraries in various programming languages based on the UCAN v1.0.0-rc.1 specification.
> **Important**: This guide reflects the UCAN v1.0.0-rc.1 specification. UCAN v1.0 introduces significant changes from earlier versions, including a new envelope format and restructured payload fields. Always refer to the latest specifications for implementation details.
## Overview
[Section titled “Overview”](#overview)
Creating a UCAN library involves implementing the core UCAN specification while following language-specific conventions and best practices. This guide will help you build a robust, interoperable UCAN implementation.
### Major Changes in UCAN v1.0
[Section titled “Major Changes in UCAN v1.0”](#major-changes-in-ucan-v10)
UCAN v1.0 introduces significant changes from earlier versions:
* **Envelope Format**: Replaces JWT-based tokens with UCAN-specific envelope format
* **Type Tags**: Each UCAN type has a specific tag (e.g., `ucan/dlg@1.0.0-rc.1` for delegations)
* **Structured Capabilities**: Capabilities are now structured with subject, command, and policy
* **Policy Language**: Introduces a comprehensive policy language for expressing constraints
* **Separate Specifications**: Delegation, Invocation, Promise, and Revocation are separate specs
* **IPLD/CBOR Encoding**: Uses IPLD and CBOR instead of JSON for better efficiency
### What You’ll Build
[Section titled “What You’ll Build”](#what-youll-build)
A complete UCAN library should provide:
* ✅ **Delegation Creation** - Generate UCAN delegation tokens with proper envelope format
* ✅ **Delegation Validation** - Verify delegation signatures and capability chains
* ✅ **Invocation Creation** - Create invocation tokens to exercise delegated capabilities
* ✅ **Capability Management** - Handle capability parsing, validation, and policy checking
* ✅ **Cryptographic Operations** - Support for signature algorithms (EdDSA, ECDSA, RSA)
* ✅ **Envelope Handling** - UCAN envelope encoding/decoding with proper type tags
* ✅ **Chain Validation** - Verify delegation chains and authority propagation
## Getting Started
[Section titled “Getting Started”](#getting-started)
### Prerequisites
[Section titled “Prerequisites”](#prerequisites)
Before implementing a UCAN library, ensure you have:
1. **Cryptographic Library** - Access to EdDSA, ECDSA, or RSA signature algorithms
2. **UCAN Envelope Support** - Ability to handle UCAN envelope format (not traditional JWT)
3. **IPLD/CBOR Support** - For encoding/decoding UCAN envelopes and payloads
4. **Base64 Encoding** - URL-safe base64 encoding/decoding for envelope serialization
5. **JSON Handling** - Robust JSON parsing for policy language and metadata
### Core Components
[Section titled “Core Components”](#core-components)
Every UCAN implementation needs these essential components:
#### 1. UCAN Envelope Structure
[Section titled “1. UCAN Envelope Structure”](#1-ucan-envelope-structure)
UCAN v1.0 uses an envelope format rather than traditional JWT. Each UCAN type has its own envelope structure:
```plaintext
// UCAN Delegation Envelope
UCANDelegation {
envelope: {
tag: "ucan/dlg@1.0.0-rc.1", // Type tag for delegation
// envelope-specific fields
},
payload: {
iss: DID, // Issuer DID
aud: DID, // Audience DID
sub: DID | null, // Subject DID (delegation chain principal)
cmd: String, // Command to eventually invoke
pol: Policy, // Policy constraints (UCAN Policy Language)
nonce: Bytes, // Cryptographic nonce
meta?: Object, // Optional metadata (not delegated)
nbf?: Integer, // Not before timestamp (optional)
exp: Integer | null // Expiration timestamp (required)
},
signature: Bytes // Cryptographic signature
}
// UCAN Invocation Envelope
UCANInvocation {
envelope: {
tag: "ucan/inv@1.0.0-rc.1", // Type tag for invocation
// envelope-specific fields
},
payload: {
// Invocation-specific payload structure
// (see UCAN Invocation specification)
},
signature: Bytes
}
```
#### 2. Key Management
[Section titled “2. Key Management”](#2-key-management)
Implement support for Decentralized Identifiers (DIDs):
* **did:key** - Direct public key encoding
* **did:web** - Web-based DID resolution
* **Custom DID methods** - Extensible for future methods
#### 3. UCAN Policy Language
[Section titled “3. UCAN Policy Language”](#3-ucan-policy-language)
UCAN v1.0 uses a sophisticated policy language for expressing capability constraints:
```plaintext
// Policy examples
Policy = [
["==", ".status", "draft"], // Equality check
[">=", ".size", 1024], // Numeric comparison
["like", ".email", "*@example.com"], // Glob pattern matching
["all", ".reviewers", ["like", ".email", "*@corp.com"]], // Quantification
["and", [ // Logical conjunction
["!=", ".type", "sensitive"],
["or", [["==", ".dept", "eng"], ["==", ".dept", "design"]]]
]]
]
```
## Implementation Steps
[Section titled “Implementation Steps”](#implementation-steps)
### Step 1: Set Up Project Structure
[Section titled “Step 1: Set Up Project Structure”](#step-1-set-up-project-structure)
Create a well-organized project structure:
```plaintext
ucan-library/
├── src/
│ ├── core/
│ │ ├── token.{ext} # UCAN token implementation
│ │ ├── builder.{ext} # Token builder/creator
│ │ └── validator.{ext} # Token validation logic
│ ├── crypto/
│ │ ├── keys.{ext} # Key management
│ │ └── signatures.{ext} # Signature operations
│ ├── capabilities/
│ │ ├── parser.{ext} # Capability parsing
│ │ └── validator.{ext} # Capability validation
│ └── utils/
│ ├── encoding.{ext} # Base64/JWT utilities
│ └── time.{ext} # Timestamp handling
├── tests/
├── examples/
└── docs/
```
### Step 2: Implement Core UCAN Operations
[Section titled “Step 2: Implement Core UCAN Operations”](#step-2-implement-core-ucan-operations)
Start with basic UCAN operations following the v1.0 specification:
1. **Delegation Creation**
* Generate UCAN delegation envelopes with proper type tags
* Implement delegation payload structure with required fields
* Sign delegations with issuer private keys
* Handle capability delegation chains
2. **Envelope Parsing**
* Decode UCAN envelopes (not JWT format)
* Validate envelope structure and type tags
* Extract capabilities and policy constraints
* Parse delegation chains and proof references
3. **Delegation Validation**
* Verify cryptographic signatures using envelope format
* Check expiration and validity periods (nbf/exp)
* Validate capability chains and authority delegation
* Evaluate policy constraints using UCAN Policy Language
### Step 3: Add Cryptographic Support
[Section titled “Step 3: Add Cryptographic Support”](#step-3-add-cryptographic-support)
Implement robust cryptographic operations:
* **Key Generation** - Create new key pairs
* **Signature Creation** - Sign UCAN payloads
* **Signature Verification** - Validate existing signatures
* **DID Resolution** - Resolve public keys from DIDs
### Step 4: Policy Language Implementation
[Section titled “Step 4: Policy Language Implementation”](#step-4-policy-language-implementation)
Build comprehensive policy evaluation:
* **Policy Parsing** - Parse UCAN Policy Language expressions
* **Selector Resolution** - Implement JSONPath-like selectors (e.g., “.field.subfield”)
* **Comparison Operators** - Support ==, !=, <, <=, >, >=, like
* **Logical Connectives** - Implement and, or, not operations
* **Quantifiers** - Handle all/any quantification over collections
* **Glob Matching** - Support wildcard patterns for string matching
### Step 5: Testing & Validation
[Section titled “Step 5: Testing & Validation”](#step-5-testing--validation)
Ensure your implementation is robust:
* **Unit Tests** - Test individual components
* **Integration Tests** - Test complete workflows
* **Interoperability Tests** - Validate against other implementations
* **Security Tests** - Test attack scenarios
## Best Practices
[Section titled “Best Practices”](#best-practices)
### Security Considerations
[Section titled “Security Considerations”](#security-considerations)
* ✅ **Validate All Inputs** - Never trust external data
* ✅ **Secure Key Storage** - Protect private keys appropriately
* ✅ **Time Validation** - Always check token expiration
* ✅ **Chain Validation** - Verify complete delegation chains
* ✅ **Signature Verification** - Never skip cryptographic validation
### Performance Optimization
[Section titled “Performance Optimization”](#performance-optimization)
* ✅ **Lazy Loading** - Load resources only when needed
* ✅ **Caching** - Cache validated tokens and keys
* ✅ **Async Operations** - Use non-blocking I/O where possible
* ✅ **Memory Management** - Handle large capability chains efficiently
### API Design
[Section titled “API Design”](#api-design)
* ✅ **Clear Interfaces** - Use intuitive, well-documented APIs
* ✅ **Error Handling** - Provide meaningful error messages
* ✅ **Type Safety** - Use strong typing where available
* ✅ **Immutability** - Prefer immutable data structures
## Language-Specific Considerations
[Section titled “Language-Specific Considerations”](#language-specific-considerations)
### JavaScript/TypeScript
[Section titled “JavaScript/TypeScript”](#javascripttypescript)
* Use IPLD/CBOR libraries for envelope encoding (e.g., @ipld/dag-cbor)
* Leverage existing cryptographic libraries (noble-ed25519, noble-secp256k1)
* Implement UCAN Policy Language evaluation
* Support both browser and Node.js environments
### Rust
[Section titled “Rust”](#rust)
* Use serde for serialization with CBOR support (serde\_cbor)
* Leverage libipld for IPLD handling
* Use ring or rustcrypto for cryptographic operations
* Implement zero-copy parsing where possible for performance
### Go
[Section titled “Go”](#go)
* Use CBOR libraries for envelope encoding (github.com/fxamacker/cbor)
* Follow Go conventions for error handling and interfaces
* Implement clean, idiomatic UCAN envelope parsing
* Support concurrent validation where appropriate
## Testing Your Implementation
[Section titled “Testing Your Implementation”](#testing-your-implementation)
### Interoperability Testing
[Section titled “Interoperability Testing”](#interoperability-testing)
Test your implementation against other UCAN v1.0 libraries:
1. **Envelope Exchange** - Create envelopes in one library, validate in another
2. **Delegation Chains** - Test delegation chains across implementations
3. **Policy Evaluation** - Ensure consistent policy language evaluation
4. **Signature Compatibility** - Verify signature algorithms work correctly
5. **Type Tag Handling** - Test proper envelope type tag recognition
### Specification Compliance
[Section titled “Specification Compliance”](#specification-compliance)
Verify your implementation follows the UCAN v1.0.0-rc.1 specification:
* ✅ **Envelope Format** - Proper UCAN envelope structure with type tags
* ✅ **Delegation Payload** - Correct delegation payload fields (iss, aud, sub, cmd, pol, etc.)
* ✅ **Policy Language** - Full UCAN Policy Language support
* ✅ **Signature Algorithms** - Support required cryptographic algorithms
* ✅ **Type Tags** - Proper envelope type tags (ucan/dlg\@1.0.0-rc.1, etc.)
* ✅ **Chain Validation** - Verify delegation chains and capability propagation
## Example Implementation
[Section titled “Example Implementation”](#example-implementation)
Here’s a basic example of creating and validating a UCAN delegation:
```pseudocode
// Create a new UCAN delegation
delegation = UCANDelegation.builder()
.envelope_tag("ucan/dlg@1.0.0-rc.1")
.issuer(issuerDID)
.audience(audienceDID)
.subject(subjectDID)
.command("/blog/post/create")
.policy([
["==", ".status", "draft"],
["like", ".author", "*@example.com"]
])
.expiration(futureTimestamp)
.nonce(randomBytes(32))
.sign(privateKey)
// Validate the delegation
validation = UCANValidator.new()
.validateEnvelope(delegation)
.validateSignature(delegation)
.validateExpiration(delegation)
.validatePolicy(delegation, invocationArgs)
if validation.isValid() {
// Delegation is valid, can be used for invocation
} else {
// Handle validation errors
}
// Create an invocation using the delegation
invocation = UCANInvocation.builder()
.envelope_tag("ucan/inv@1.0.0-rc.1")
.capability(delegation)
.arguments({
"status": "draft",
"author": "alice@example.com",
"title": "New Blog Post"
})
.sign(audiencePrivateKey)
```
## Community & Support
[Section titled “Community & Support”](#community--support)
### Getting Help
[Section titled “Getting Help”](#getting-help)
* 📚 **UCAN Specification** - Review the official specification
* 💬 **Community Forum** - Join discussions with other implementers
* 🐛 **Issue Tracking** - Report bugs and request features
* 📖 **Reference Implementations** - Study existing libraries
### Contributing
[Section titled “Contributing”](#contributing)
Help improve this guide:
* 📝 **Documentation** - Add language-specific examples
* 🧪 **Test Cases** - Contribute interoperability tests
* 🔍 **Best Practices** - Share implementation insights
* 🐛 **Bug Reports** - Report inaccuracies or missing information
## Next Steps
[Section titled “Next Steps”](#next-steps)
1. **Study the v1.0 Specifications** - Read the UCAN Delegation and Invocation specifications thoroughly
2. **Choose Your Language** - Select the programming language for your implementation
3. **Set Up IPLD/CBOR Support** - Install required dependencies for envelope handling
4. **Implement Envelope Parsing** - Start with basic envelope encoding/decoding
5. **Add Policy Language Support** - Implement the UCAN Policy Language evaluator
6. **Test Against Reference Implementations** - Ensure compatibility and correctness
7. **Contribute to the Ecosystem** - Share your implementation with the UCAN community
***
*This guide reflects the UCAN v1.0.0-rc.1 specification. For the latest updates and community contributions, visit the [UCAN Working Group](https://github.com/ucan-wg) repositories.*
# iso-ucan
> Documentation for iso-ucan
[hugomrdias/iso-repo](https://github.com/hugomrdias/iso-repo)
> Isomorphic UCAN primitives for delegations, invocations, proof storage, and typed RPC.
## Overview
[Section titled “Overview”](#overview)
UCAN is a local-first authorization model where permissions are cryptographically signed, delegated, and verified without relying on a central authority. See [ucan.xyz](https://ucan.xyz/) for more background.
`iso-ucan` provides:
* Typed capabilities backed by schema validation.
* Delegation and invocation builders for UCAN authorization flows.
* Proof storage with pluggable `iso-kv` drivers.
* Signer and verifier support through `iso-signatures`.
* EIP-191 wallet signing support through `iso-signatures` `EIP191Signer`.
* Filsnap-powered UCAN signature insights in MetaMask signature popups.
* A typed RPC layer for request/response APIs built on UCAN invocations.
## Install
[Section titled “Install”](#install)
```bash
pnpm install iso-ucan
```
## Usage
[Section titled “Usage”](#usage)
```ts
import { Capability } from 'iso-ucan/capability'
import { Store } from 'iso-ucan/store'
import { MemoryDriver } from 'iso-kv/drivers/memory'
import { EdDSASigner } from 'iso-signatures/signers/eddsa.js'
import { z } from 'zod'
const store = new Store(new MemoryDriver())
const AccountCreateCap = Capability.from({
schema: z.object({
type: z.string(),
properties: z
.object({
name: z.string(),
})
.strict(),
}),
cmd: '/account/create',
})
const AccountCap = Capability.from({
schema: z.never(),
cmd: '/account',
})
const owner = await EdDSASigner.generate()
const bob = await EdDSASigner.generate()
const invoker = await EdDSASigner.generate()
const nowInSeconds = Math.floor(Date.now() / 1000)
const ownerDelegation = await AccountCap.delegate({
iss: owner,
aud: bob,
sub: owner,
pol: [],
exp: nowInSeconds + 1000,
})
await store.set(ownerDelegation)
const bobDelegation = await AccountCap.delegate({
iss: bob,
aud: invoker,
sub: owner,
pol: [],
exp: nowInSeconds + 1000,
})
await store.set(bobDelegation)
const invocation = await AccountCreateCap.invoke({
iss: invoker,
sub: owner,
args: {
type: 'account',
properties: {
name: 'John Doe',
},
},
store,
exp: nowInSeconds + 1000,
})
```
## EIP-191 wallet signing
[Section titled “EIP-191 wallet signing”](#eip-191-wallet-signing)
`iso-ucan` can use EIP-191 wallet signatures through `EIP191Signer` from `iso-signatures`. That lets UCAN delegations and invocations be issued by an Ethereum account exposed through an EIP-1193 provider, such as an injected MetaMask wallet, while verification is handled by the matching EIP-191 verifier from `iso-signatures`.
[`examples/eip191`](../../examples/eip191) shows this flow in a browser web app. It uses `wagmi` to connect the wallet, wraps the provider in `EIP191Signer`, then creates and verifies UCAN delegations and invocations directly in the browser.
The example uses `filsnap-adapter` to install or enable [Filsnap](https://github.com/filecoin-project/filsnap), the Filecoin Wallet MetaMask Snap. Filsnap supports UCAN signature insights for EIP-191 signers, so when the app asks MetaMask to sign a UCAN delegation, the MetaMask signature popup can show a readable summary of the issuer, audience, subject, command, expiration, and raw payload.
## RPC
[Section titled “RPC”](#rpc)
`iso-ucan/rpc` is a small request/response layer on top of UCAN invocations. You declare a protocol once as a record of commands (`defineCommand`), then build matching client and server pairs from it (`defineClient` / `defineServer`). Every command’s success and error shapes are described by a single receipt schema (`receipt`, `receiptResult`, `receiptError`) which is shared by both ends, so the call sites are fully type-safe and discriminated unions just work on the client.
Internal server failures (invalid invocation, unknown command, handler threw, …) are surfaced as a generic `SERVER_ERROR` variant that `receipt(...)` adds implicitly, so clients can always handle them with one narrow.
### Define a shared protocol
[Section titled “Define a shared protocol”](#define-a-shared-protocol)
```ts
import {
defineCommand,
receipt,
receiptError,
receiptResult,
} from 'iso-ucan/rpc'
import { z } from 'zod'
const TodoSchema = z.object({
id: z.string(),
text: z.string(),
done: z.boolean(),
})
export const Protocol = {
TodoList: defineCommand({
cmd: '/todo/list',
args: z.object({}),
receipt: receipt(receiptResult(z.object({ todos: z.array(TodoSchema) }))),
}),
TodoAdd: defineCommand({
cmd: '/todo/add',
args: z.object({ text: z.string().min(1) }),
receipt: receipt(receiptResult(z.object({ todo: TodoSchema }))),
}),
TodoComplete: defineCommand({
cmd: '/todo/complete',
args: z.object({ id: z.string() }),
receipt: receipt(
receiptResult(z.object({ todo: TodoSchema })),
receiptError('NOT_FOUND', 'Todo not found.', z.object({ id: z.string() }))
),
}),
} as const
```
### Server (e.g. Hono + `@hono/node-server`)
[Section titled “Server (e.g. Hono + @hono/node-server)”](#server-eg-hono--hononode-server)
```ts
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
import { MemoryDriver } from 'iso-kv/drivers/memory.js'
import { defineServer, type ServerHandlers } from 'iso-ucan/rpc'
import { Store } from 'iso-ucan/store'
import { Protocol } from './protocol.ts'
const store = new Store(new MemoryDriver())
// ...seed `store` with the delegations that authorise your clients...
const handlers: ServerHandlers = {
'/todo/list': ({ invocation }) => ({
cid: invocation.cid,
result: { todos: [/* ... */] },
}),
'/todo/add': ({ args, invocation }) => ({
cid: invocation.cid,
result: { todo: { id: '1', text: args.text, done: false } },
}),
'/todo/complete': ({ args, invocation }) => {
// return either { cid, result: ... } or { cid, error: { code, message, data } }
return {
cid: invocation.cid,
error: {
code: 'NOT_FOUND',
message: 'Todo not found.',
data: { id: args.id },
},
}
},
}
const rpc = defineServer(Protocol, {
signer: serverSigner,
store,
verifierResolver,
handlers,
})
const app = new Hono()
app.post('/rpc', (c) => rpc(c.req.raw))
serve({ fetch: app.fetch, port: 3000 })
```
### Client
[Section titled “Client”](#client)
```ts
import { defineClient } from 'iso-ucan/rpc'
const client = defineClient(Protocol, {
url: 'http://localhost:3000/rpc',
issuer: cliSigner,
audience: serverSigner.didObject,
store,
verifierResolver,
})
// `args` and the returned receipt are typed from the protocol entry for `cmd`.
const r = await client.request({ cmd: '/todo/complete', args: { id: '42' } })
if ('result' in r) {
console.log(r.result.todo)
} else if (r.error.code === 'NOT_FOUND') {
console.error(r.error.message, r.error.data) // data: { id: string }
} else {
// r.error.code === 'SERVER_ERROR' — always present in every receipt union.
console.error('server error:', r.error.message)
}
```
A complete runnable example (Hono server + a small CLI client) lives in [`examples/rpc-todo`](../../examples/rpc-todo).
## License
[Section titled “License”](#license)
MIT © [Hugo Dias](http://hugodias.me)
# Rust Implementation
> Documentation for Rust Implementation
[ucan-wg/rs-ucan](https://github.com/ucan-wg/rs-ucan)
[](https://github.com/ucan-wg/ucan)
# ucan
[](https://crates.io/crates/ucan)[](https://codecov.io/gh/ucan-wg/ucan)[](https://github.com/ucan-wg/ucan/actions?query=)[](https://github.com/ucan-wg/ucan/blob/main/LICENSE)[](https://docs.rs/ucan)[](https://discord.gg/4UdeQhw7fv)
:warning: Work in progress :warning:
> \[!NOTE] These libraries conform to UCAN v1.0.0-rc.1
> \[!WARNING] This code has not been formally audited. Use at your own risk!
## Usage
[Section titled “Usage”](#usage)
Add the following to the `[dependencies]` section of your `Cargo.toml` file:
```toml
ucan = "0.8"
```
## Testing the Project
[Section titled “Testing the Project”](#testing-the-project)
Run tests
| Nix | Cargo |
| ---------- | ------------ |
| `test:all` | `cargo test` |
## Benchmarking the Project
[Section titled “Benchmarking the Project”](#benchmarking-the-project)
For benchmarking and measuring performance, this project leverages [Criterion](https://github.com/bheisler/criterion.rs) and a `test_utils` feature flag.
## Benchmarks
[Section titled “Benchmarks”](#benchmarks)
| Nix | Cargo |
| ------- | ----------------------------------- |
| `bench` | `cargo bench --features=test_utils` |
## Contributing
[Section titled “Contributing”](#contributing)
:balloon: We’re thankful for any feedback and help in improving our project! We have a [contributing guide](#contributing) to help you get involved. We also adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md).
### Nix
[Section titled “Nix”](#nix)
This repository contains a [Nix flake](https://nixos.wiki/wiki/Flakes) that initiates both the Rust toolchain set in [`rust-toolchain.toml`](./rust-toolchain.toml) and a [pre-commit hook](#pre-commit-hook). It also installs helpful cargo binaries for development.
Please install [Nix](https://nixos.org/download.html) to get started. We also recommend installing [direnv](https://direnv.net/).
Run `nix develop` or `direnv allow` to load the `devShell` flake output, according to your preference.
The Nix shell also includes several helpful shortcut commands. You can see a complete list of commands via the `menu` command.
### Formatting
[Section titled “Formatting”](#formatting)
For formatting Rust in particular, we automatically format on `nightly`, as it uses specific nightly features we recommend by default.
### Pre-commit Hook
[Section titled “Pre-commit Hook”](#pre-commit-hook)
This project recommends using [pre-commit](https://pre-commit.com/) for running pre-commit hooks. Please run this before every commit and/or push.
* If you are doing interim commits locally, and for some reason if you *don’t* want pre-commit hooks to fire, you can run `git commit -a -m "Your message here" --no-verify`.
### Recommended Development Flow
[Section titled “Recommended Development Flow”](#recommended-development-flow)
* We recommend leveraging \[cargo-watch]\[cargo-watch], [`cargo-expand`](https://github.com/dtolnay/cargo-expand) and [IRust](https://github.com/sigmaSd/IRust) for Rust development.
* We recommend using \[cargo-udeps]\[cargo-udeps] for removing unused dependencies before commits and pull-requests.
### Conventional Commits
[Section titled “Conventional Commits”](#conventional-commits)
This project *lightly* follows the [Conventional Commits convention](https://www.conventionalcommits.org/) to help explain commit history and tie in with our release process. The full specification can be found [here](https://www.conventionalcommits.org/en/v1.0.0/#specification). We recommend prefixing your commits with a type of `fix`, `feat`, `docs`, `ci`, `refactor`, etc…, structured like so:
```plaintext
[optional scope]:
[optional body]
[optional footer(s)]
```
## Getting Help
[Section titled “Getting Help”](#getting-help)
For usage questions, usecases, or issues reach out to us in the [UCAN Discord](https://discord.gg/4UdeQhw7fv).
We would be happy to try to answer your question or try opening a new issue on GitHub.
## External Resources
[Section titled “External Resources”](#external-resources)
These are references to specifications, talks and presentations, etc.
## License
[Section titled “License”](#license)
This project is [licensed under the Apache License 2.0](#license), or [http://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0).
# UCAN Promise Specification. 1
> Documentation for UCAN Promise Specification. 1
## Depends On
[Section titled “Depends On”](#depends-on)
* [IPLD](https://ipld.io/)
* [UCAN Invocation](/invocation/)
# 0. Abstract
[Section titled “0. Abstract”](#0-abstract)
This specification describes a mechanism for extending [UCAN Invocation](/invocation/)s with [distributed promise pipelines](http://erights.org/elib/distrib/pipeline.html).
# 1. Introduction
[Section titled “1. Introduction”](#1-introduction)
> Machines grow faster and memories grow larger. But the speed of light is constant and New York is not getting any closer to Tokyo. As hardware continues to improve, the latency barrier between distant machines will increasingly dominate the performance of distributed computation. When distributed computational steps require unnecessary round trips, compositions of these steps can cause unnecessary cascading sequences of round trips.
>
> —[Mark Miller](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf)
A promise is a deferred value that waits on the completion of some function. In effect it says “when that function completes, take the output and substitute it here”. Distributed promises do the same, but unlike the familiar `async/await` of languages like JavaScript, MAY reference any already running computation, even from other programs. In effect, this allows a significant reduction in latency, and reduces the requirement that all nodes be online to respond to results and dispatch new invocations.
This of course requires a global namespace. Luckily, [UCAN Invocation](/invocation/) already has [globally-unique identifiers for every Action](#111-actid).
## 1.1 Input Addressing
[Section titled “1.1 Input Addressing”](#11-input-addressing)
Indexing the output of a function by its inputs is called “input addressing”. By comparison, “content addressing” acts on static data[1](#user-content-fn-input-content-addressing).
### 1.1.1 ActID
[Section titled “1.1.1 ActID”](#111-actid)
An Action Identifier (ActID) is the content address of an [Action](/invocation/#31-action). It can be found direction in an Invocation:
```js
// Pseudocode
const actId = invocation.inv.run.act.asCid()
```
A Receipt MAY have multiple input addresses. For instance, if an Action contains a promise versus when it’s fully reified, the associated Receipt is the same.
If an Action is run multiple times, an ActID MAY refer to many Receipts. Actions SHOULD be fully qualified, and include a unique nonce if the Action is non-idempotent. This ensures that any (correctly run) Receipts for the same ActID will have the same output value.
### 1.1.2 Memoization Table
[Section titled “1.1.2 Memoization Table”](#112-memoization-table)
Input addressing plays nicely as a global [memoization](https://en.wikipedia.org/wiki/Memoization) table. Since it maps a hash of the inputs to the outputs, someone with access to the cache can pull out values by their input address, and skip re-running potentially expensive computations.
## 1.2 Comparing Async Promises to Sync Invocations
[Section titled “1.2 Comparing Async Promises to Sync Invocations”](#12-comparing-async-promises-to-sync-invocations)
The semantics of invocations say the same with round trips and promises. Here is an example of delegation, invocation, and promise pipelining to show how these relate:
```
sequenceDiagram
participant Alice 💾
participant Bob
participant Carol 📧
participant Dan
autonumber
Note over Alice 💾, Dan: Delegation Setup
Alice 💾 -->> Bob: Delegate
Bob -->> Carol 📧: Delegate
Carol 📧 -->> Dan: Delegate
Carol 📧 -->> Dan: Delegate
Note over Alice 💾, Dan: Synchronous Invocation Flow
Dan ->> Alice 💾: Read from Alice's DB!
Alice 💾 -->> Dan: Result<➎> = "hello"
Dan ->> Carol 📧: Send email containing "hello" as Carol!
Carol 📧 ->> Carol 📧: Send email containing "hello" as Carol!
Note over Alice 💾, Dan: Async Promise Pipeline Flow
Dan ->> Alice 💾: Read from Alice's DB!
par Promise
Dan ->> Carol 📧: Send email containing Result<➒> as Carol!
and Result
Alice 💾 -->> Carol 📧: Result<➒> = "hello"
end
Carol 📧 ->> Carol 📧: Send email containing "hello" as Carol!
```
# 2. Promise Format
[Section titled “2. Promise Format”](#2-promise-format)
A Promise is encoded as a map with a single field (the tag) which selects for the branch, and the CID of the relevant [Task](/invocation/#32-task). Because Tasks uniquely identify their output and MAY be replicated across multiple trustless providers, referencing the entire [UCAN Invocation](/invocation/) would over-specify the [Result](/invocation/#421-result).
It has several variants:
| Tag | Type | Description |
| ------------- | --------- | ---------------------------------------------------------------------------------------------- |
| `await/*` | `&Action` | Await any branch |
| `await/ok` | `&Action` | Await an `ok` branch of a [Result](/invocation/#421-result), and inline the unwrapped value |
| `await/error` | `&Action` | Await an `error` branch of a [Result](/invocation/#421-result), and inline the unwrapped value |
Here are a few examples:
```js
// In isolation
{"await/*": {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}}
{"await/ok": {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}}
{"await/error": {"/": "bafkr4ig4o5mwufavfewt4jurycn7g7dby2tcwg5q2ii2y6idnwguoyeruq"}}
// In situ
{
"sig": {"/": {bytes: "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}},
"inv": {
"iss": "did:web:example.com",
"aud": "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"run": cid({
"act": cid({
"nnc": "246910121416"
"cmd": "msg/send",
"arg": {
"from": "alice@example.com",
"to": [
"bob@example.com",
"carol@example.com",
{"await/ok": {"/": "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam"}}
// └───┬────┘ └────────────────────────────┬──────────────────────────────┘
// Branch Selector ActID
]
}
}),
"mta": {},
"prf": [{"/": "bafkr4iblvgvkmqt46imsmwqkjs7p6wmpswak2p5hlpagl2htiox272xyy4"}]
})
}
}
```
# 3. Resolution
[Section titled “3. Resolution”](#3-resolution)
Using a shared cache[2](#user-content-fn-bbd), many cooperating processes can collaborate on multiple separate goals while reusing each others results. The exact mechanism is left to the implementation, but [pubsub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern), [gossip](https://en.wikipedia.org/wiki/Gossip_protocol), and [DHT](https://en.wikipedia.org/wiki/Distributed_hash_table)s are all viable.
The Executor MUST extract the [Result](/invocation/#421-result) from a resolved [Receipt](/invocation/#41-receipt-envelope), and attempt to match on the tag. If the match passes or fails branch selection, the behavior is as described below.
## 3.1 Happy Path
[Section titled “3.1 Happy Path”](#31-happy-path)
If the Promise uses the `await/*` tag, then any branch MUST be accepted, and the entire Result (including the `ok` or `error` tag) MUST be substituted. For example:
```js
// Pseudocode
const promised = {
"nnc": "0123456789AB"
"cmd": "msg/send",
"arg": {
"to": "alice@example.com",
"message": {"await/*": {"/": "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam"}}
}
}
returnedReceipt.receipt = {"ok": "hello"}
// └──────┬──────┘
// └───────────────────────────────────────────────────────────────┐
promised.resolve(result, "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam") === { // │
"nnc": "0123456789AB" // │
"cmd": "msg/send", // │
"arg": { // │
"to": "alice@example.com", // │
"message": {"ok": "hello"} // ◄──────────────────────────────────────────────────────────────┘
}
}
```
If the Promise uses an `await/ok` or `await/error` tag, then it MUST only match on Results that match the relevant tag. The inner value MUST be extracted from the outer `ok` or `error` map and substituted. Extending our earlier example:
```js
// Pseudocode
const promised = {
"nnc": "0123456789AB"
"cmd": "msg/send",
"arg": {
"to": "alice@example.com",
"message": {"await/ok": {"/": "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam"}}
} // ▲
} // ┌─YES─┘
// ┌┴─┐
const result = {"ok": "hello"}
// └──┬──┘
// └───────────────────────────────────────────────────────────────────────┐
promised.resolve(result, "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam") === { // │
"nnc": "0123456789AB", // │
"cmd": "msg/send", // │
"arg": { // │
"to": "alice@example.com", // │
"message": "hello" // ◄──────────────────────────────────────────────────────────────────────┘
}
}
```
## 3.2 Branch Mismatch
[Section titled “3.2 Branch Mismatch”](#32-branch-mismatch)
If the branch from the Result doesn’t match the branch selector, the Invocation that contains the Promise MUST return an `error` Result in its own Receipt.
```js
// Pseudocode
const promised = {
"nnc": "0123456789AB"
"cmd": "msg/send",
"arg": {
"to": "alice@example.com",
"message": {"await/ok": {"/": "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam"}}
} // ▲
} // └──NO───┐
// ┌──┴──┐
returnedReceipt.result === {"error": "Divided by zero"}
newReceipt === {
"out": {
"error": {
"reason": "branch mismatch",
"expected": "ok",
"got": "error",
"from": returnedReceipt.cid
}
},
// ...
}
```
Note that this can also happen when matching on the `error` branch:
```js
// Pseudocode
const promised = {
"nnc": "0123456789AB"
"cmd": "log/push",
"arg": {
"msg": {"await/error": {"/": "bafkr4ie7m464donhksutmfqsyqzgcrqhzi2vc5ygiw3ajkhuz6lulnbjam"}}
}
}
returnedReceipt.receipt = {"ok": "hello"}
newReceipt === {
"out": {
"error": {
"reason": "branch mismatch",
"expected": "error",
"got": "ok",
"from": returnedReceipt.cid
}
},
// ...
}
```
# 4. Prior Art
[Section titled “4. Prior Art”](#4-prior-art)
The Capability Transport Protocol ([CapTP](http://erights.org/elib/distrib/captp/index.html)) is one of the most influential object-capability systems, and forms the basis for much of the rest of the items on this list.
The Object Capability Network ([OCapN](https://github.com/ocapn/)) protocol extends [CapTP](http://erights.org/elib/distrib/captp/index.html) with a generalized networking layer. It has implementations from the [Spritely Institute](https://spritely.institute/) and [Agoric](https://agoric.com/). At time of writing, it is in the process of being standardized.
[Cap ’n Proto RPC](https://capnproto.org/) is an influential RPC framework based on concepts from [CapTP](http://erights.org/elib/distrib/captp/index.html). Their website include much text expounding the benefits of promise pipelining.
# 5. Acknowledgements
[Section titled “5. Acknowledgements”](#5-acknowledgements)
Many thanks to [Mark Miller](https://github.com/erights) for his [trail blazing work](https://erights.org) on [capability systems](https://en.wikipedia.org/wiki/Capability-based_security).
Thanks to [Philipp Krüger](https://github.com/matheus23/) for the enthusiastic feedback on the overall design and encoding.
Thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for the many conversations about capability systems and the programming models that they enable.
## Footnotes
[Section titled “Footnotes”](#footnote-label)
1. Content addressing can be seen as a special case of input addressing for the identity function. [↩](#user-content-fnref-input-content-addressing)
2. Sometimes called a [blackboard](https://en.wikipedia.org/wiki/Blackboard_\(design_pattern\)) [↩](#user-content-fnref-bbd)
# UCAN Revocation Specification
> This specification defines the syntax and semantics of revoking a [UCAN Delegation], and the ability to delegate this ability to others....
# Abstract
[Section titled “Abstract”](#abstract)
This specification defines the syntax and semantics of revoking a [UCAN Delegation](/delegation/), and the ability to delegate this ability to others.
# Introduction
[Section titled “Introduction”](#introduction)
Using the [principle of least authority](https://en.wikipedia.org/wiki/Principle_of_least_privilege) such as certificate expiry and reduced capability scope SHOULD be the preferred method for securing a UCAN, but does not cover every situation. Revocation is a manual method for reversing a delegation. It cannot undo irreversible mutations (such as sending an email), but MAY limit misuse going forward. Revocation is the act of invalidating a UCAN after the fact, outside of the limitations placed on it by the UCAN’s fields (such as its expiry).
Even when not in error at time of issuance, the trust relationship between a delegator and delegatee is not immutable. An agent can go rogue, keys can be compromised, and the privacy requirements of resources can (will!) change. While the UCAN Delegation approach recommends following the [principle of least authority](https://en.wikipedia.org/wiki/Principle_of_least_privilege), unexpected conditions that require manual intervention do arise. These are exceptional cases, but are sufficiently important that a well defined method for performing revocation is nearly always desired in token and certificate systems.
# Approach
[Section titled “Approach”](#approach)
UCAN delegation is designed to be [local-first](https://www.inkandswitch.com/local-first/), partition-tolerant, cacheable, and latency-reducing. As such, [fail-safe](https://en.wikipedia.org/wiki/Fail-safe) approaches are not suitable. Revocation is accomplished by delivery of an unforgeable message from a previous delegator.
UCAN Revocations are similar to [block list](https://en.wikipedia.org/wiki/Blacklist_\(computing\))s: they identify delegation paths that are retracted and no longer suitable for use. Revocation SHOULD be considered the last line of defense against abuse. Proactive expiry through time bounds or other constraints SHOULD be preferred, as they do not require learning more information than what would be available on an offline computer.
UCAN Revocation is a mechanism for invalidating a particular Delegation when used in conjunction with another Delegation in an Invocation proof chain. This is conceptually recursive, and more easily described in pictures:
```
flowchart RL
invoker((    Dan    ))
revoker((    Bob    ))
subject((    Alice    ))
subgraph Delegations
subgraph root [Root UCAN]
subgraph rooting [Root Issuer]
rootIss(iss: Alice)
rootSub(sub: Alice)
end
rootAud(aud: Bob)
end
subgraph del1 [Delegated UCAN]
del1Iss(iss: Bob) --> rootAud
del1Aud(aud: Carol)
del1Sub(sub: Alice)
del1Sub --> rootSub
end
subgraph del2 [INVALIDATED Delegation]
del2Iss(iss: Carol) --> del1Aud
del2Aud(aud: Dan)
del2Sub(sub: Alice)
del2Sub --> del1Sub
end
end
subgraph rev [Revocation]
revArg("arg: {revoke: cid(carol_to_dan)}")
revCmd("cmd: ucan/revoke")
revIss(iss: Bob)
revPrf("proofs")
end
subgraph inv [INVALIDATED Invocation]
invSub(sub: Alice)
invIss(iss: Dan)
invPrf("proofs")
end
revoker --> revIss
revArg:::revoked -.-> del2:::revoked
revIss -.-> del1Iss
revPrf:::revocation -.-> del1:::revocation
inv:::revoked
invPrf:::revoked
invIss --> del2Aud
invoker --> invIss
invSub --> del2Sub
rootIss --> subject
rootSub --> subject
invPrf --> Delegations
classDef revocation stroke:blue,fill:#76b0ff
classDef revoked stroke:red,fill:#ff7676,color:red
```
# Semantics
[Section titled “Semantics”](#semantics)
Revocation is the act of invalidating a proof in a delegation chain for some specific UCAN delegation by its CID. All UCAN capabilities are either claimed by direct authority over the Subject, or by delegation chain terminating in that direct (“root”) authority. Each link in a delegation chain contains an explicit issuer (delegator) and audience (delegatee).
*Revocations MUST be immutable and irreversible.* Recipients of revocations SHOULD treat them as a monotonically-growing set. If a Revocation was issued in error, it MUST NOT be retracted — a new, unique UCAN delegation MAY be issued (e.g. by updating the nonce or changing the time bounds). This prevents confusion as the revocation moves through the network and makes [revocation store](#store)s append-only and highly amenable to caching and gossip.
## Scope
[Section titled “Scope”](#scope)
An Issuer of a particular Delegation in a proof chain MAY revoke that Delegation. Note that this is not always the same as revoking the Delegation they they Issued; any UCAN that contains a proof where the revoker matches the `iss` field — even transitively in the delegation chain — MAY be revoked.
Revocation of a particular proof does not guarantee that the Agent can no longer access to the capability in question. If an Agent is able to construct a valid proof chain without relying on the revoked proof, they still have access to the capability. By real-world analogy, if Mallory has two tickets to a film, and one of them is invalidated by its serial number, she is still able to present the valid ticket to see the film.
```
flowchart TB
subgraph RA[Alice can revoke]
direction RL
AB["(Root)\niss: Alice\naud: Bob\niff: [X,Y,Z]"]
subgraph RB[Bob can revoke]
BC["iss: Bob\naud: Carol\niff: [X,Y]"]
BD["iss: Bob\naud: Dan\niff: [Y,Z]"]
subgraph RC[Carol can revoke]
CD["iss: Carol\naud: Dan\niff: [X,Y]"]
subgraph RD[Dan can revoke]
DE["iss: Dan\naud: Erin\niff: [X,Y,Z]"]
end
end
end
end
BD -->|proof| AB
BC -->|proof| AB
CD -->|proof| BC
DE -->|proof| CD
DE -->|proof| BD
```
Here Alice is the root Issuer. Alice MAY revoke any of the UCANs in the chain, Carol MAY revoke the two innermost, and so on. If the UCAN `Carol -> Dan` is revoked by Alice, Bob, or Carol, then Erin will not have a valid chain for the `X` capability, since its only proof is invalid. However, Erin can still prove the valid capability for `Y` and `Z` since the still-valid (“unbroken”) chain `Alice to Bob to Dan to Erin` includes them. Note that despite `Y` being in the revoked `Carol -> Dan` UCAN, it does not invalidate `Y` for Erin, since the unbroken chain also included a proof for `Y`.
## Consistency Model
[Section titled “Consistency Model”](#consistency-model)
UCAN revocation is designed to work in the broadest possible scenarios, and as such needs very weak constraints. UCAN revocation MAY operate in fully eventually consistent contexts, with single sources of truth, or among nodes participating in consensus. The format of the revocation does not change in these situations; it is entirely managed by how revocations are passed around the network. Weak assumptions enable UCAN to work with eventually consistent resources, such as [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)s, [Git](https://git-scm.com/) forks, delay-tolerant replicated state machines, and so on.
These weak assumptions are often associated with being unable to guarantee delivery in a certain time bound. Weak assumptions can always be strengthened, but not vice-versa. For example, if a capability describes access to a resource with a single location or source of truth, sending a revocation to that specific agent enables confirmation in bounded time. This grants nearly identical semantics that many people turn to [ACL](https://en.wikipedia.org/wiki/Access-control_list)s for, but with all of the benefits of capabilities during delegation and invocation.
Out of order delivery is typical of distributed systems. Further, a malicious user can otherwise delay revealing that they have a capability until the last possible moment in hopes of evading detection. Accepting revocations for resources that the agent controls prior to the delegation targeted by the revocation is received is thus RECOMMENDED.
# Store
[Section titled “Store”](#store)
The Agent that controls a resource MUST maintain a cache of Revocations for which it is the Subject. The Agent MAY additionally cache gossiped Revocations about other Subjects as part of a [store and forward](https://en.wikipedia.org/wiki/Store_and_forward) mechanism.
During validation of a UCAN delegation chain, the [canonical CID](/specification/#41-content-identifiers) of each UCAN delegation MUST be checked against the cache. If there’s a match, the relevant Delegation MUST be ignored. Note that this MAY NOT invalidate the entire UCAN chain.
```js
// Pseudocode
const delegators = invocation.prf.map(proof => proof.iss)
invocation.prf.forEach(delegation => {
// Is the proof in the revocation store?
store.lookup(delegation).then(revocation => {
// Is the revocation issuer in this proof chain?
if (delegators.includes(revocation.iss)) {
throw new Error("Invalidated via revocation by delegation issuer")
}
// Is the revocation based on a delegated revocation?
const cids = revocation.iff.filter(cav => !!cav.rev)
if (cids.length === 1 && invocation.prf.includes(cids[0])) {
throw new Error("Invalidated by delegated revocation")
}
})
})
```
## Locality
[Section titled “Locality”](#locality)
Resources with a single source of truth SHOULD follow the typical approach of maintaining a revocation store at the same physical location as the resource. For example, a centralized server MAY have an endpoint that lists the revoked UCANs by [canonical CID](/specification/#41-content-identifiers).
For eventually consistent data structures, this MAY be achieved by including the store directly inside the resource itself. For example, a CRDT-based file system SHOULD maintain the revocation store directly at a well-known path.
## Monotonicity
[Section titled “Monotonicity”](#monotonicity)
Since Revocations MUST NOT be reversible, a new Delegation SHOULD be issued if a Revocation was issued in error.
```
flowchart LR
Alice((   Alice   ))
Bob((   Bob   ))
Carol((   Carol   ))
Dan((   Dan   ))
del1{{Delegate\ncan: crud/read Alice's DB}}
del2{{Delegate\ncan: crud/read Alice's DB}}
del3{{Delegate\ncan: crud/read Alice's DB}}
newDel{{"Delegate\ncan: crud/read Alice's DB\n(Resissued) "}}
Alice === del1 ==> Bob === del2:::Revoked ===x Carol === del3 ==> Dan
Alice === newDel:::Reissued ===> Carol
rev>Revoke!]
Alice === rev:::Invocation
rev -.->|rev| del2
classDef Invocation stroke:#F00,fill:#F00,color:#000;
classDef Revoked stroke:#F00;
classDef Reissued stroke:green;
linkStyle 2 stroke:red
linkStyle 3 stroke:red
linkStyle 8 stroke:red
linkStyle 9 stroke:red
linkStyle 6 stroke:green
linkStyle 7 stroke:green
```
## Eviction
[Section titled “Eviction”](#eviction)
Revocations MAY be evicted once the UCAN that they reference expires or otherwise becomes invalid through its proactive mechanisms, such as expiry (`exp`) plus some clock-skew buffer.
# Delegating Revocation
[Section titled “Delegating Revocation”](#delegating-revocation)
The authority to revoke some Delegation MAY be itself delegated to a Principal not in the delegation chain. The revoked Delegation SHOULD be referenced by its [canonical CID](/specification/#41-content-identifiers).
| Field | Value |
| ------ | ------------------------- |
| `can` | `"ucan/revoke"` |
| `args` | `{"revoke": &Delegation}` |
This is a Delegation of the ability to Revoke:
```js
{
"iss": "did:web:alice.example.com",
"aud": "did:web:zelda.example.com",
"can": "ucan/revoke",
"args": {
"revoke": {"/": "bafkreiem4on23qnu2nn2jg7vwzxkns6sxi5faysq7ekwtjhugqga3vbhim"}
},
// ...
}
```
```
flowchart LR
Alice((   Alice   ))
Bob((   Bob   ))
Carol((   Carol   ))
Dan((   Dan   ))
Zelda((   Zelda   ))
del1{{Delegate\ncan: crud/read Alice's DB}}
del2{{Delegate\ncan: crud/read Alice's DB}}
del3{{Delegate\ncan: crud/read Alice's DB}}:::Revoked
delRev{{Delegate\ncan: ucan/revoke}}
Alice === del1 ==> Bob === del2 ===> Carol === del3 ===x Dan
Alice === delRev ===> Zelda
delRev -.->|cid| del2
rev>Revoke]
Zelda === rev:::Invocation ===> Alice
rev:::Invocation -.->|rev| del3
classDef Revoked stroke:#F00;
classDef Invocation stroke:#F00,fill:#F00,color:#000;
linkStyle 4 stroke:red
linkStyle 5 stroke:red
linkStyle 9 stroke:red
linkStyle 10 stroke:red
```
# Invoking Revocation
[Section titled “Invoking Revocation”](#invoking-revocation)
A revocation Action MUST take the following shape:
| Field | Value |
| ------- | --------------------------- |
| `do` | `"ucan/revoke"` |
| `args` | See [Arguments](#arguments) |
| `nonce` | `""` |
Note that per [UCAN Invocation](/invocation/), the `nnc` field SHOULD is set to `""` since revocation is idempotent.
## Arguments
[Section titled “Arguments”](#arguments)
Being expressed as an Invocation means that Revocations MUST define an Action type for the command `ucan/revoke`.
| Field | Type | Required | Description |
| -------- | --------------- | -------- | --------------------------------------------------------------------------------------- |
| `revoke` | `&Delegation` | Yes | The CID of the [UCAN Delegation](/delegation/) that is being revoked |
| `path` | `[&Delegation]` | No | A [delegation path](#path-witness) that includes the Revoker and the revoked Delegation |
### Path Witness
[Section titled “Path Witness”](#path-witness)
Since all delegation chains MUST be rooted in a Delegation where the `iss` and `sub` fields are equal, the root Issuer is a priori in every delegation chain. This is not the case for sub-delegation. There are many paths through the authority network. For example, take the following delegation network:
```
flowchart LR
Alice -->|delegates| Bob -->|delegates| Dan -->|delegates| Erin
Bob -->|delegates| Carol -->|delegates| Erin
Alice -->|delegates| Mallory
```
Mallory is not in the delegation chain of Erin. This is fine, since the semantics of revocation merely state that she would assert that no delegation of hers may be used in the `prf` field of an Invocation if it also includes the `rev` Delegation. However, issuing spurious Revocations and requiring them to be stored is a potential DoS vector. Executors MAY require a delegation path witness be included to avoid this situation.
Unlike Mallory, Bob, Carol, and Dan can both provide valid delegation paths that include Delegations that they have issued. Bob has two paths (`Alice -> Bob -> Dan -> Erin` or `Alice -> Bob -> Carol -> Erin`), and either will suffice.
### Example
[Section titled “Example”](#example)
```js
// DAG-JSON
{
"s": {"/": {"bytes": "7aEDQIscUKVuAIB2Yj6jdX5ru9OcnQLxLutvHPjeMD3pbtHIoErFpo7OoC79Oe2ShgQMLbo2e6dvHh9scqHKEOmieA0"}},
"p": {
"h": {"/": {"bytes": "NAHtAe0BE3E"}},
"ucan/i/1.0.0-rc.1": {
"iss": "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
"sub": "did:key:z6MkrZ1r5XBFZjBU34qyD8fueMbMRkKw17BZaq2ivKFjnz2z",
"do": "ucan/revoke",
"args": {
"revoke": {"/": "bafkreictzcfwelyww7zmjkl5nptyot24oilky2bppw42nui2acozhfmzqa"},
"path": [
{"/": "bafkreic4lzfu6gq6pxonmalbjzjumrs5p47plsolmccaz4qhgmzo24fagu"},
{"/": bafkreicc3jmhhtkzv26rb43cfx6ihyjlj2hixdfrkirglrermfo6cduelm""}
]
},
"nonce": {"/": {"bytes": ""}},
"meta": {
"comment": "bad behaviour"
},
"prf": [
{"/": "bafkr4idnrqfouibxdqpvh2lmkhgsbw5yabvjbiaea3fplrb4vxifaphvgy"},
]
}
}
}
```
# Prior Art
[Section titled “Prior Art”](#prior-art)
[Revocation lists](https://en.wikipedia.org/wiki/Certificate_revocation) are a fairly widely used concept.
[SPKI/SDSI](https://datatracker.ietf.org/wg/spki/about/) is closely related to UCAN. A different format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written.
[X.509 Certificate Revocation Lists](https://www.rfc-editor.org/rfc/rfc5280) defines two kinds of certificate invalidation: temporary (“hold”) and permanent (“revocation”). This RFC also includes a field for indicating a reason for revocation. UCAN Revocation has no concept of a temporary hold on a capability, but this behavior MAY be emulated by revoking a credential and issuing a new UCAN with a `nbf` field set to a time in the future.
[ZCAP-LD](https://w3c-ccg.github.io/zcap-spec/) is closely related to UCAN, but situated in the W3C-style linked data world (the “LD” in ZCAP-LD). Revocation in ZCAP-LD is only granted to those who have a special caveat on a capability. In contrast, UCAN capabilities MAY be revoked by anyone in the relevant delegation path.
[OAuth 2.0 Revocation](https://www.rfc-editor.org/rfc/rfc7009) is very similar to UCAN revocation. It is largely concerned with the HTTP interactions to make OAuth revocation work. OAuth doesn’t have a concept of sub-delegation, so only the user that has been granted the token can revoke it. However, this may cascade to revocation of other tokens, but the exact mechanism is left to the implementer.
While strictly speaking being about assertions rather than capabilities, [Verfiable Credential Revocation](https://learn.microsoft.com/en-us/azure/active-directory/verifiable-credentials/how-to-issuer-revoke) spec follows a similar pattern to those listed above.
[E](http://www.erights.org/)-style [object capabilities](https://en.wikipedia.org/wiki/Object-capability_model) use active network connections with [proxy agents](http://www.erights.org/talks/thesis/markm-thesis.pdf) to revoke delegations. Revocation is achieved by shutting down that proxy to break the authorizing reference. In many ways, UCAN Revocation attempts to emulate this behavior. Unlike UCAN Revocations, E-style object capabilities are [fail-safe](https://en.wikipedia.org/wiki/Fail-safe) and thus by definition not partition tolerant.
# Acknowledgements
[Section titled “Acknowledgements”](#acknowledgements)
Thank you [Blaine Cook](https://github.com/blaine) for the real-world feedback, ideas on future features, and lessons from other auth standards.
Thanks to [Juan Caballero](https://github.com/bumblefudge) for the numerous questions, clarifications, and general advice on putting together a comprehensible spec.
Many thanks to [Alan Karp](https://github.com/alanhkarp) for sharing his vast experience with capability-based authorization, patterns, and many right words for us to search for.
Thanks to [Benjamin Goering](https://github.com/gobengo) for the many community threads and connections to [W3C](https://www.w3.org/) standards.
Many thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for her handwritten(!) feedback on the design of UCAN, spearheading the [OCapN](https://github.com/ocapn/) initiative, and her related work on [ZCAP-LD](https://w3c-ccg.github.io/zcap-spec/).
Thanks to the entire [SPKI WG](https://datatracker.ietf.org/wg/spki/about/) for their closely related pioneering work.
We want to especially recognize [Mark Miller](https://github.com/erights) for his numerous contributions to the field of distributed auth, programming languages, and computer security writ large.
# UCAN Revocation Schema
> IPLD schema definition for UCAN Revocation
# UCAN Revocation Schema
[Section titled “UCAN Revocation Schema”](#ucan-revocation-schema)
This document contains the IPLD schema definition for UCAN Revocation.
```ipldsch
type RevocationAction <: Action {
cmd "ucan/revoke"
nnc ""
arg RevocationArguments
}
type RevocationArguments struct {
rev &Delegation
pth [&Delegation]
}
```
# User Controlled Authorization Network (UCAN) Specification
> User-Controlled Authorization Network (UCAN) is a [trustless], secure, [local-first], user-originated, distributed authorization scheme. This document provides ...
## Sub-Specifications
[Section titled “Sub-Specifications”](#sub-specifications)
* [UCAN Delegation](/delegation/)
* [UCAN Invocation](/invocation/)
* [UCAN Promise](/promise/)
* [UCAN Revocation](/revocation/)
# Abstract
[Section titled “Abstract”](#abstract)
User-Controlled Authorization Network (UCAN) is a [trustless](https://blueskyweb.xyz/blog/3-6-2022-a-self-authenticating-social-protocol), secure, [local-first](https://www.inkandswitch.com/local-first/), user-originated, distributed authorization scheme. This document provides a high level overview of the components of the system, concepts, and motivation. Exact formats are given in [sub-specifications](#sub-specifications).
# Introduction
[Section titled “Introduction”](#introduction)
User-Controlled Authorization Network (UCAN) is a [trustless](https://blueskyweb.xyz/blog/3-6-2022-a-self-authenticating-social-protocol), secure, [local-first](https://www.inkandswitch.com/local-first/), user-originated, distributed authorization scheme. It provides public-key verifiable, delegable, expressive, openly extensible [capabilities](https://en.wikipedia.org/wiki/Object-capability_model). UCANs achieve public verifiability with late-bound certificate chains and principals represented by [decentralized identifiers (DIDs)](https://www.w3.org/TR/did-core/).
UCAN improves the familiarity and adoptability of schemes like [SPKI/SDSI](https://theworld.com/~cme/html/spki.html) for web and native application contexts. UCAN allows for the creation, delegation, and invocation of authority by any agent with a DID, including traditional systems and peer-to-peer architectures beyond traditional cloud computing.
## Motivation
[Section titled “Motivation”](#motivation)
> If we practice our principles, we could have both security and functionality. Treating security as a separate concern has not succeeded in bridging the gap between principle and practice, because it operates without knowledge of what constitutes least authority.
>
> — [Miller](https://github.com/erights) et al, [The Structure of Authority](http://erights.org/talks/no-sep/secnotsep.pdf)
Since at least [Multics](https://en.wikipedia.org/wiki/Multics), access control lists ([ACL](https://en.wikipedia.org/wiki/Access-control_list)s) have been the most popular form of digital authorization, where a list of what each user is allowed to do is maintained on the resource. ACLs (and later [RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)) have been a successful model suited to architectures where persistent access to a single list is viable. ACLs require that rules are sufficiently well specified, such as in a centralized database with rules covering all possible permutations of scenario. This both imposes a very high maintenance burden on programmers as a systems grows in complexity, and is a key vector for [confused deputies](https://en.wikipedia.org/wiki/Confused_deputy_problem).
With increasing interconnectivity between machines becoming commonplace, authorization needs to scale to meet the load demands of distributed systems while providing partition tolerance. However, it is not always practical to maintain a single central authorization source. Even when copies of the authorization list are distributed to the relevant servers, latency and partitions introduce troublesome challenges with conflicting updates, to say nothing of storage requirements.
A large portion of personal information now also moves through connected systems. As a result, data privacy is a prominent theme when considering the design of modern applications, to the point of being legislated in parts of the world.
Ahead-of-time coordination is often a barrier to development in many projects. Flexibility to define specialized authorization semantics for resources and the ability to integrate with external systems trustlessly are essential as the number of autonomous, specialized, and coordinated applications increases.
Many high-value applications run in hostile environments. In recognition of this, many vendors now include public key functionality, such as [non-extractable keys in browsers](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey), [certificate systems for external keys](https://fidoalliance.org/what-is-fido/), [platform keys](https://www.passkeys.com/), and \[secure hardware enclaves] in widespread consumer devices.
Two related models that work exceptionally well in the above context are Simple Public Key Infrastructure ([SPKI](https://www.rfc-editor.org/rfc/rfc2693.html)) and object capabilities ([OCAP](http://erights.org/elib/capability/index.html)). Since offline operation and self-verifiability are two requirements, UCAN adopts a [certificate capability model](https://web.archive.org/web/20140724054706/http://wiki.erights.org/wiki/Capability-based_Active_Invocation_Certificates) related to [SPKI](https://theworld.com/~cme/html/spki.html).
## Intuition for Auth System Differences
[Section titled “Intuition for Auth System Differences”](#intuition-for-auth-system-differences)
The following analogies illustrate several significant trade-offs between these systems but are only accurate enough to build intuition. A good resource for a more thorough presentation of these trade-offs is [Capability Myths Demolished](https://srl.cs.jhu.edu/pubs/SRL2003-02.pdf). In this framework, UCAN approximates SPKI with some dynamic features.
### Access Control Lists
[Section titled “Access Control Lists”](#access-control-lists)
By analogy, ACLs are like a bouncer at an exclusive event. This bouncer has a list attendees allowed in and which of those are VIPs that get extra access. People trying to get in show their government-issued ID and are accepted or rejected. In addition, they may get a lanyard to identify that they have previously been allowed in. If someone is disruptive, they can simply be crossed off the list and denied further entry.
If there are many such events at many venues, the organizers need to coordinate ahead of time, denials need to be synchronized, and attendees need to show their ID cards to many bouncers. The likelihood of the bouncer letting in the wrong person due to synchronization lag or confusion by someone sharing a name is nonzero.
### Certificate Capabilities
[Section titled “Certificate Capabilities”](#certificate-capabilities)
UCANs work more like [movie tickets](http://www.erights.org/elib/capability/duals/myths.html#caps-as-keys) or a festival pass. No one needs to check your ID; who you are is irrelevant. For example, if you have a ticket issued by the theater to see Citizen Kane, you are admitted to Theater 3. If you cannot attend an event, you can hand this ticket to a friend who wants to see the film instead, and there is no coordination required with the theater ahead of time. However, if the theater needs to cancel tickets for some reason, they need a way of uniquely identifying them and sharing this information between them.
### Object Capabilities
[Section titled “Object Capabilities”](#object-capabilities)
Object capability (“ocap”) systems use a combination of references, encapsulated state, and proxy forwarding. As the name implies, this is fairly close to object-oriented or actor-based systems. Object capabilities are [robust](http://www.erights.org/talks/thesis/markm-thesis.pdf), flexible, and expressive.
To achieve these properties, object capabilities have two requirements: [fail-safe](https://en.wikipedia.org/wiki/Fail-safe), and locality preservation. The emphasis on consistency rules out partition tolerance[1](#user-content-fn-pcec).
## Security Considerations
[Section titled “Security Considerations”](#security-considerations)
Each UCAN includes an assertions of what it is allowed to do. “Proofs” are positive evidence (elsewhere called “witnesses”) of the possession of rights. They are cryptographically verifiable chains showing that the UCAN issuer either claims to directly own a resource, or that it was delegated to them by some claimed owner. In the most common case, the root owner’s ID is the only globally unique identity for the resource.
Root capability issuers function as verifiable, distributed roots of trust. The delegation chain is by definition a provenance log. Private keys themselves SHOULD NOT move from one context to another. Keeping keys unique to each physical device and unique per use case is RECOMMENDED to reduce opportunity for keys to leak, and limit blast radius in the case of compromises. “Sharing authority without sharing keys” is provided by capabilities, so there is no reason to share keys directly.
Note that a structurally and cryptographically valid UCAN chain can be semantically invalid. The executor MUST verify the ownership of any external resources at execution time. While not possible for all use cases (e.g. replicated state machines and eventually consistent data), having the Executor be the resource itself is RECOMMENDED.
While certificate chains go a long way toward improving security, they do not provide [confinement](http://www.erights.org/elib/capability/dist-confine.html) on their own. The principle of least authority SHOULD be used when delegating a UCAN: minimizing the amount of time that a UCAN is valid for and reducing authority to the bare minimum required for the delegate to complete their task. This delegate should be trusted as little as is practical since they can further sub-delegate their authority to others without alerting their delegator. UCANs do not offer confinement (as that would require all processes to be online), so it is impossible to guarantee knowledge of all of the sub-delegations that exist. The ability to revoke some or all downstream UCANs exists as a last resort.
## Inversion of Control
[Section titled “Inversion of Control”](#inversion-of-control)
[Inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control) is achieved due to two properties: self-certifying delegation and reference passing. There is no Authorization Server (AS) that sits between requestors and resources. In traditional terms, the owner of a UCAN resource is the resource server (RS) directly.
This inverts the usual relationship between resources and users: the resource grants some (or all) authority over itself to agents, as opposed to an Authorization Server managing the relationship between them. This has several major advantages:
* Fully distributed and scalable
* Self-contained request without intermediary
* Partition tolerance, [support for replicated data and machines](#beyond-single-system-image)
* Flexible granularity
* Compositionality: no distinction between resources residing together or apart
```plaintext
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ │ │ │
│ │ │ ┌─────────┐ │ │ │
│ │ │ │ Bob's │ │ │ │
│ │ │ │ Photo │ │ │ │
│ │ │ │ Gallery │ │ │ │
│ │ │ └─────────┘ │ │ │
│ │ │ │ │ │
│ Alice's │ │ Bob's │ │ Carol's │
│ Stuff │ │ Stuff │ │ Stuff │
│ │ │ │ │ │
│ ┌───────┼───┼─────────────┼───┼──┐ │
│ │ │ │ │ │ │ │
│ │ │ │ ┌───┼───┼──┼────────┐ │
│ │ │ │ Alice's │ │ │ │ │ │
│ │ │ │ Music │ │ │ │Carol's │ │
│ │ │ │ Player │ │ │ │ Game │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ │ └───┼───┼──┼────────┘ │
│ │ │ │ │ │ │ │
│ └───────┼───┼─────────────┼───┼──┘ │
│ │ │ │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
```
This additionally allows UCAN to model auth for [eventually consistent and replicated state](#beyond-single-system-image).
# Roles
[Section titled “Roles”](#roles)
There are several roles that an agent MAY assume:
| Name | Description |
| --------- | ------------------------------------------------------------------------------------------------ |
| Agent | The general class of entities and principals that interact with a UCAN |
| Audience | The Principal delegated to in the current UCAN. Listed in the `aud` field |
| Executor | The Agent that actually performs the action described in an invocation |
| Invoker | A Principal that requests an Executor perform some action that uses the Invoker’s authority |
| Issuer | The Principal of the current UCAN. Listed in the `iss` field |
| Owner | A Subject that controls some external resource |
| Principal | An agent identified by DID (listed in a UCAN’s `iss` or `aud` field) |
| Revoker | The Issuer listed in a proof chain that revokes a UCAN |
| Subject | The Principal who’s authority is delegated or invoked |
| Validator | Any Agent that interprets a UCAN to determine that it is valid, and which capabilities it grants |
```
flowchart TD
subgraph Agent
subgraph Principal
direction TB
subgraph Issuer
direction TB
subgraph Subject
direction TB
Executor
Owner
end
Revoker
end
subgraph Audience
Invoker
end
end
Validator
end
```
## Subject
[Section titled “Subject”](#subject)
> At the very least every object should have a URL
>
> — [Alan Kay](https://en.wikipedia.org/wiki/Alan_Kay), [The computer revolution hasn’t happened yet](https://www.youtube.com/watch?v=oKg1hTOQXoY)
> Every Erlang process in the universe should be addressable and introspective
>
> — [Joe Armstrong](https://en.wikipedia.org/wiki/Joe_Armstrong_\(programmer\)), [Code Mesh 2016](https://www.codemesh.io/codemesh2016)
A \[Subject] represents the Agent that a capability is for. A Subject MUST be referenced by [DID](https://www.w3.org/TR/did-core/). This behaves much like a [GUID](https://en.wikipedia.org/wiki/Universally_unique_identifier), with the addition of public key verifiability. This unforgeability prevents malicious namespace collisions which can lead to [confused deputies](https://en.wikipedia.org/wiki/Confused_deputy_problem).
### Resource
[Section titled “Resource”](#resource)
A resource is some data or process that can be uniquely identified by a [URI](https://www.rfc-editor.org/rfc/rfc3986). It can be anything from a row in a database, a user account, storage quota, email address, etc. Resource MAY be as coarse or fine grained as desired. Finer-grained is RECOMMENDED where possible, as it is easier to model the principle of least authority ([PoLA](https://en.wikipedia.org/wiki/Principle_of_least_privilege)).
A resource describes the noun of a capability. The resource pointer MUST be provided in [URI](https://www.rfc-editor.org/rfc/rfc3986) format. Arbitrary and custom URIs MAY be used, provided that the intended recipient can decode the URI. The URI is merely a unique identifier to describe the pointer to — and within — a resource.
Having a unique agent represent a resource (and act as its manager) is RECOMMENDED. However, to help traditional ACL-based systems transition to certificate capabilities, an agent MAY manage multiple resources, and [act as the registrant in the ACL system](#wrapping-existing-systems).
Unless explicitly stated, the Resource of a UCAN MUST be the Subject.
## Issuer & Audience
[Section titled “Issuer & Audience”](#issuer--audience)
The Issuer (`iss`) and Audience (`aud`) can be conceptualized as the sender and receiver (respectively) of a postal letter. Every UCAN MUST be signed with the private key associated with the DID in the `iss` field.
For example:
```js
"aud": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
"iss": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
```
Please see the [Cryptosuite](#cryptosuite) section for more detail on DIDs.
# Lifecycle
[Section titled “Lifecycle”](#lifecycle)
The UCAN lifecycle has four components:
| Spec | Description | Requirement Level |
| -------------------------- | ------------------------------------------------------------------------ | ----------------- |
| [Delegation](/delegation/) | Pass, attenuate, and secure authority in a partition-tolerant way | REQUIRED |
| [Invocation](/invocation/) | Exercise authority that has been delegated through one or more delegates | REQUIRED |
| [Promise](/promise/) | Await the result of an Invocation inside another Invocation | RECOMMENDED |
| [Revocation](/revocation/) | Undo a delegation, breaking a delegation chain for malicious users | RECOMMENDED |
```
flowchart TD
prm(Promise)
inv(Invocation)
del(Delegation)
rev(Revocation)
prm -->|awaits| inv
del -->|proves| inv
rev -.->|kind of| inv
rev -->|invalidates| del
click del href "https://github.com/ucan-wg/delegation" "UCAN Delegation Spec"
click inv href "https://github.com/ucan-wg/invocation" "UCAN Invocation Spec"
click rev href "https://github.com/ucan-wg/revocation" "UCAN Revocation Spec"
```
## Time
[Section titled “Time”](#time)
It is often useful to talk about a UCAN in the context of some action. For example, a UCAN delegation may be valid when it was created, but expired when invoked.
```
sequenceDiagram
Alice -->> Bob: Delegate
Bob ->> Bob: Validate
Bob -->> Carol: Delegate
Carol ->> Carol: Validate
Carol ->> Alice: Invoke
Alice ->> Alice: Validate
Alice ->> Alice: Execute
```
### Validity Interval
[Section titled “Validity Interval”](#validity-interval)
The period of time that a capability is valid from and until. This is the range from the latest “not before” to the earliest expiry in the UCAN delegation chain.
### Delegation-Time
[Section titled “Delegation-Time”](#delegation-time)
The moment at which a delegation is asserted. This MAY be captured by an `iat` field, but is generally superfluous to capture in the token.
### Invocation-Time
[Section titled “Invocation-Time”](#invocation-time)
The moment a UCAN Invocation is created. It must be within the Validity Interval.
### Validation-Time
[Section titled “Validation-Time”](#validation-time)
Validation MAY occur at multiple points during a UCAN’s lifecycle. The main two are:
* On receipt of a delegation
* When executing an invocation
### Execution-Time
[Section titled “Execution-Time”](#execution-time)
To avoid the overloaded word “runtime”, UCAN adopts the term “execution-time” to express the moment that the executor attempts to use the authority captured in an invocation and associated delegation chain. Validation MUST occur at this time.
## Time Bounds
[Section titled “Time Bounds”](#time-bounds)
`nbf` and `exp` stand for “not before” and “expires at,” respectively. These MUST be expressed as seconds since the Unix epoch in UTC, without time zone or other offset. Taken together, they represent the time bounds for a token. These timestamps MUST be represented as the number of integer seconds since the Unix epoch. Due to limitations[2](#user-content-fn-js-num-size) in numerics for certain common languages, timestamps outside of the range from $-2^{53} – 1$ to $2^{53} – 1$ MUST be rejected as invalid.
The `nbf` field is OPTIONAL. When omitted, the token MUST be treated as valid beginning from the Unix epoch. Setting the `nbf` field to a time in the future MUST delay invoking a UCAN. For example, pre-provisioning access to conference materials ahead of time but not allowing access until the day it starts is achievable with judicious use of `nbf`.
The `exp` field is RECOMMENDED. Following the [principle of least authority](https://en.wikipedia.org/wiki/Principle_of_least_privilege), it is RECOMMENDED to give a timestamp expiry for UCANs. If the token explicitly never expires, the `exp` field MUST be set to `null`. If the time is in the past at validation time, the token MUST be treated as expired and invalid.
Keeping the window of validity as short as possible is RECOMMENDED. Limiting the time range can mitigate the risk of a malicious user abusing a UCAN. However, this is situationally dependent. It may be desirable to limit the frequency of forced reauthorizations for trusted devices. Due to clock drift, time bounds SHOULD NOT be considered exact. A buffer of ±60 seconds is RECOMMENDED.
Several named points of time in the UCAN lifecycle can be found in the \[high level spec]\[UCAN].
Below are a couple examples:
```js
{
// ...
"nbf": 1529496683,
"exp": 1575606941
}
```
```js
{
// ...
"exp": 1575606941
}
```
```js
{
// ...
"nbf": 1529496683,
"exp": null
}
```
## Lifecycle Example
[Section titled “Lifecycle Example”](#lifecycle-example)
Here is a concrete example of all stages of the UCAN lifecycle for database write access.
```
sequenceDiagram
participant Database
actor DBAgent
actor Alice
actor Bob
Note over Database, DBAgent: Set Up Agent-Owned Resource
DBAgent ->> Database: createDB()
autonumber 1
Note over DBAgent, Bob: Delegation
DBAgent -->> Alice: delegate(DBAgent, write)
Alice -->> Bob: delegate(DBAgent, write)
Note over Database, Bob: Invocation
Bob ->> DBAgent: invoke(DBAgent, [write, [key, value]], proof: [➊,➋])
DBAgent ->> Database: write(key, value)
DBAgent ->> Bob: ACK
Note over DBAgent, Bob: Revocation
Alice ->> DBAgent: revoke(➋, proof: [➊,➋])
Bob ->> DBAgent: invoke(DBAgent, [write, [key, newValue]], proof: [➊,➋])
DBAgent -X Bob: NAK(➏) [rejected]
```
## Capability
[Section titled “Capability”](#capability)
A capability is the association of an ability to a subject: `subject x command x policy`.
The Subject and Command fields are REQUIRED. Any non-normative extensions are OPTIONAL.
For example, a capability may used to represent the ability to send email from a certain address to others at `@example.com`.
| Field | Example |
| ------- | -------------------------------------------------------------------------------------------- |
| Subject | `did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK` |
| Command | `/msg/send` |
| Policy | `["or", ["==", ".from", "mailto:me@example.com"], ["match", ".cc", "mailto:*@example.com"]]` |
For a more complete treatment, please see the [UCAN Delegation](/delegation/) spec.
## Authority
[Section titled “Authority”](#authority)
> Whether to enable cooperation or to limit vulnerability, we care about *authority* rather than *permissions.* Permissions determine what actions an individual program may perform on objects it can directly access. Authority describes the effects that a program may cause on objects it can access, either directly by permission, or indirectly by permitted interactions with other programs.
>
> —[Mark Miller](https://github.com/erights), [Robust Composition](http://www.erights.org/talks/thesis/markm-thesis.pdf)
The set of capabilities delegated by a UCAN is called its “authority.” To frame it another way, it’s the set of effects that a principal can cause, and acts as a declarative description of delegated abilities.
Merging capability authorities MUST follow set semantics, where the result includes all capabilities from the input authorities. Since broader capabilities automatically include narrower ones, this process is always additive. Capability authorities can be combined in any order, with the result always being at least as broad as each of the original authorities.
```plaintext
┌───────────────────────┐ ┐
│ │ │
│ │ │
│ │ │
│ │ │
│ Subject B │ │
┌──────────────────┼ ─ ─ x │ │
│ │ Ability Z │ ├── BxZ
│ │ │ │ Capability
│ │ │ │
│ │ │ │
│ Subject A │ │ │
│ x │ │ │
│ Ability Y ─ ─┼──────────────────┘ ┘
│ │
│ │
│ │
│ │
│ │
└───────────────────────┘
└─────────────────────┬────────────────────┘
│
AxY U BxZ
Capability
```
The capability authority is the total rights of the authorization space down to the relevant volume of authorizations. Individual capabilities MAY overlap; the authority is the union. Every unique delegated capability MUST have equal or narrower capabilities from their delegator. Inside this content space, you can draw a boundary around some resource(s) (their type, identifiers, and paths or children) and their capabilities.
## Command
[Section titled “Command”](#command)
Commands are concrete messages (“verbs”) that MUST be unambiguously interpretable by the Subject of a UCAN. Commands are REQUIRED in invocations. Some examples include `/msg/send`, `/crud/read`, and `/ucan/revoke`.
Much like other message-passing systems, the specific resource MUST define the behavior for a particular message. For instance, `/crud/update` MAY be used to destructively update a database row, or append to a append-only log. Specific messages MAY be created at will; the only restriction is that the Executor understand how to interpret that message in the context of a specific resource.
While arbitrary semantics MAY be described, they MUST apply to the target resource. For instance, it does not make sense to apply `/msg/send` to a typical file system.
### Segment Structure
[Section titled “Segment Structure”](#segment-structure)
Commands MUST be lowercase, and begin with a slash (`/`). Segments MUST be separated by a slash. A trailing slash MUST NOT be present. All of the following are syntactically valid Commands:
* `/`
* `/crud`
* `/crud/create`
* `/stack/pop`
* `/crypto/sign`
* `/foo/bar/baz/qux/quux`
* `/ほげ/ふが`
Segment structure is important since shorter Commands prove longer paths. For example, `/` can be used as a proof of *any* other Command. For example, `/crypto` MAY be used to prove `/crypto/sign` but MUST NOT prove `/stack/pop` or `/cryptocurrency`.
### `/` AKA “Top”
[Section titled “/ AKA “Top””](#-aka-top)
*“Top” (`/`) is the most powerful ability, and as such it SHOULD be handled with care and used sparingly.*
The “top” (or “any”, or “wildcard”) ability MUST be denoted `/`. This can be thought of as something akin to a super user permission in RBAC.
The wildcard ability grants access to all other capabilities for the specified resource, across all possible namespaces. The wildcard ability is useful when “linking” agents by delegating all access to another device controlled by the same user, and that should behave as the same agent. It is extremely powerful, and should be used with care. Among other things, it permits the delegate to update a Subject’s mutable DID document (change their private keys), revoke UCAN delegations, and use any resources delegated to the Subject by others.
```
%%{ init: { 'flowchart': { 'curve': 'linear' } } }%%
flowchart BT
/
/msg --> /
subgraph msgGraph [ ]
/msg/send --> /msg
/msg/receive --> /msg
end
/crud --> /
subgraph crudGraph [ ]
/crud/read --> /crud
/crud/mutate --> /crud
subgraph mutationGraph [ ]
/crud/mutate/create --> /crud/mutate
/crud/mutate/update --> /crud/mutate
/crud/mutate/destroy --> /crud/mutate
end
end
... --> /
```
### Reserved Commands
[Section titled “Reserved Commands”](#reserved-commands)
#### `/ucan` Namespace
[Section titled “/ucan Namespace”](#ucan-namespace)
The `/ucan` Command namespace MUST be reserved. This MUST include any ability string matching the regex `^\/ucan\/.*`. This is important for keeping a space for community-blessed Commands in the future, such as standard library Commands, such as [Revocation](/revocation/).
## Attenuation
[Section titled “Attenuation”](#attenuation)
Attenuation is the process of constraining the capabilities in a delegation chain. Each direct delegation MUST either directly restate or attenuate (diminish) its capabilities.
# Token Resolution
[Section titled “Token Resolution”](#token-resolution)
Token resolution is transport specific. The exact format is left to the relevant UCAN transport specification. At minimum, such a specification MUST define at least the following:
1. Request protocol
2. Response protocol
3. Collections format
Note that if an instance cannot dereference a CID at runtime, the UCAN MUST fail validation. This is consistent with the [constructive semantics](https://en.wikipedia.org/wiki/Intuitionistic_logic) of UCAN.
# Nonce
[Section titled “Nonce”](#nonce)
The REQUIRED nonce parameter `nonce` MAY be any value. A randomly generated string is RECOMMENDED to provide a unique UCAN, though it MAY also be a monotonically increasing count of the number of links in the hash chain. This field helps prevent replay attacks and ensures a unique CID per delegation. The `iss`, `aud`, and `exp` fields together will often ensure that UCANs are unique, but adding the nonce ensures uniqueness.
The recommended size of the nonce differs by key type. In many cases, a random 12-byte nonce is sufficient. If uncertain, check the nonce in your DID’s crypto suite.
This field SHOULD NOT be used to sign arbitrary data, such as signature challenges. See the \[`meta`]\[Metadata] field for more.
Here is a simple example.
```js
{
// ...
"nonce": {"/": {"bytes": "bGlnaHQgd29yay4"}}
}
```
# Metadata
[Section titled “Metadata”](#metadata)
The OPTIONAL `meta` field contains a map of arbitrary metadata, facts, and proofs of knowledge. The enclosed data MUST be self-evident and externally verifiable. It MAY include information such as hash preimages, server challenges, a Merkle proof, dictionary data, etc.
The data contained in this map MUST NOT be semantically meaningful to delegation chains.
Below is an example:
```js
{
// ...
"meta": {
"challenges": {
"example.com": "abcdef",
"another.example.net": "12345"
},
"sha3_256": {
"B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9": "hello world"
}
}
}
```
# Canonicalization
[Section titled “Canonicalization”](#canonicalization)
## Cryptosuite
[Section titled “Cryptosuite”](#cryptosuite)
Across all UCAN specifications, the following cryptosuite MUST be supported:
| Role | REQUIRED Algorithms | Notes |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| Hash | [SHA-256](https://en.wikipedia.org/wiki/SHA-2) | |
| Signature | [Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519), [P-256](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf#page=111), [`secp256k1`](https://en.bitcoin.it/wiki/Secp256k1) | Preference of Ed25519 is RECOMMENDED |
| [DID](https://www.w3.org/TR/did-core/) | [`did:key`](https://w3c-ccg.github.io/did-key-spec/) | |
## Encoding
[Section titled “Encoding”](#encoding)
All UCANs MUST be canonically encoded with [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) for signing. A UCAN MAY be presented or stored in other [IPLD](https://ipld.io/) formats (such as [DAG-JSON](https://ipld.io/specs/codecs/dag-json/spec/)), but converted to DAG-CBOR for signature validation.
## Content Identifiers
[Section titled “Content Identifiers”](#content-identifiers)
A UCAN token MUST be configured as follows:
| Parameter | REQUIRED Configuration |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| Version | [CIDv1](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats) |
| [Multibase](https://github.com/multiformats/multibase) | [`base58btc`](https://github.com/multiformats/multibase/blob/master/multibase.csv#L21) |
| [Multihash](https://www.multiformats.io/multihash/) | [SHA-256](https://en.wikipedia.org/wiki/SHA-2) |
| [Multicodec](https://github.com/multiformats/multicodec) | [DAG-CBOR](https://ipld.io/specs/codecs/dag-cbor/spec/) |
> \[!NOTE] All CIDs encoded as above start with the characters `zdpu`.
The resolution of these addresses is left to the implementation and end-user, and MAY (non-exclusively) include the following: local store, a distributed hash table (DHT), gossip network, or RESTful service.
## Envelope
[Section titled “Envelope”](#envelope)
All UCAN formats MUST use the following envelope format:
| Field | Type | Description |
| --------------------------------- | -------------- | -------------------------------------------------------------- |
| `.0` | `Bytes` | A signature by the Payload’s `iss` over the `SigPayload` field |
| `.1` | `SigPayload` | The content that was signed |
| `.1.h` | `VarsigHeader` | The [Varsig](/varsig/) v1 header |
| `.1.ucan/@` | `TokenPayload` | The UCAN token payload |
```
flowchart TD
subgraph Ucan ["UCAN Envelope"]
SignatureBytes["Signature (raw bytes)"]
subgraph SigPayload ["Signature Payload"]
VarsigHeader["Varsig Header"]
subgraph UcanPayload ["Token Payload"]
fields["..."]
end
end
end
```
For example:
```js
[
{ "/": {"bytes": "bdNVZn+uTrQ8bgq5LocO2y3gqIyuEtvYWRUH9YT+SRK6v/SX8bjt+VZ9JIPVTdxkWb6nhVKBt6JGpgnjABpOCA"}},
{
"h": {"/": {"bytes": "NAHtAe0BE3E"}}, // i.e. signed with Ed25519, encoded with DAG-CBOR
"ucan/example@1.0.0": {
// Body fields, for example:
"hello": "world"
}
}
]
```
### Payload
[Section titled “Payload”](#payload)
A UCAN’s Payload MUST contain at least the following fields:
| Field | Type | Required | Description |
| ------- | ------------------------------------------------------------ | -------- | ------------------------------------------------------------ |
| `iss` | `DID` | Yes | Issuer DID (sender) |
| `aud` | `DID` | Yes | Audience DID (receiver) |
| `sub` | `DID` | Yes | Principal that the chain is about (the \[Subject]) |
| `cmd` | `String` | Yes | The [Command](#command) to eventually invoke |
| `args` | `{String : Any}` | Yes | Any \[Arguments] that MUST be present in the Invocation |
| `nonce` | `Bytes` | Yes | Nonce |
| `meta` | `{String : Any}` | No | \[Meta] (asserted, signed data) — is not delegated authority |
| `nbf` | `Integer` (53-bits[2](#user-content-fn-js-num-size)) | No | “Not before” UTC Unix Timestamp in seconds (valid from) |
| `exp` | `Integer \| Null` (53-bits[2](#user-content-fn-js-num-size)) | Yes | Expiration UTC Unix Timestamp in seconds (valid until) |
# Implementation Recommendations
[Section titled “Implementation Recommendations”](#implementation-recommendations)
## Delegation Store
[Section titled “Delegation Store”](#delegation-store)
A validator MAY keep a local store of UCANs that it has received. UCANs are immutable but also time-bound so that this store MAY evict expired or revoked UCANs.
This store SHOULD be indexed by CID (content addressing). Multiple indices built on top of this store MAY be used to improve capability search or selection performance.
## Memoized Validation
[Section titled “Memoized Validation”](#memoized-validation)
Aside from revocation, capability validation is idempotent. Marking a CID (or capability index inside that CID) as valid acts as memoization, obviating the need to check the entire structure on every validation. This extends to distinct UCANs that share a proof: if the proof was previously reviewed and is not revoked, it is RECOMMENDED to consider it valid immediately.
Revocation is irreversible. Suppose the validator learns of revocation by UCAN CID. In that case, the UCAN and all of its derivatives in such a cache MUST be marked as invalid, and all validations immediately fail without needing to walk the entire structure.
## Replay Attack Prevention
[Section titled “Replay Attack Prevention”](#replay-attack-prevention)
Replay attack prevention is REQUIRED. Every UCAN token MUST hash to a unique [CIDv1](https://docs.ipfs.io/concepts/content-addressing/#identifier-formats). Some simple strategies for implementing uniqueness tracking include maintaining a set of previously seen CIDs, or requiring that nonces be monotonically increasing per principal. This MAY be the same structure as a validated UCAN memoization table (if one is implemented).
Maintaining a secondary token expiry index is RECOMMENDED. This enables garbage collection and more efficient search. In cases of very large stores, normal cache performance techniques MAY be used, such as Bloom filters, multi-level caches, and so on.
## Beyond Single System Image
[Section titled “Beyond Single System Image”](#beyond-single-system-image)
> As we continue to increase the number of globally connected devices, we must embrace a design that considers every single member in the system as the primary site for the data that it is generates. It is completely impractical that we can look at a single, or a small number, of globally distributed data centers as the primary site for all global information that we desire to perform computations with.
>
> —[Meiklejohn](https://christophermeiklejohn.com/), [A Certain Tendency Of The Database Community](https://arxiv.org/pdf/1510.08473.pdf)
Unlike many authorization systems where a service controls access to resources in their care, location-independent, offline, and leaderless resources require control to live with the user. Therefore, the same data MAY be used across many applications, data stores, and users. Since they don’t have a single location, applying UCAN to [RSM](https://en.wikipedia.org/wiki/State_machine_replication)s and [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)s MAY be modelled by lifting the requirement that the Executor be the Subject.
Ultimately this comes down to a question of push vs pull. In push, the subject MUST be the specific site being pushed to (“I command you to apply the following updates to your state”).
Pull is the broad class of situations where an Invoker doesn’t require that a particular replica apply its state. Applying a change to a local CRDT replica and maintaining a UCAN invocation log is a valid update to “the CRDT”: a version of the CRDT Subject exists locally even if the Subject’s private key is not present. Gossiping these changes among agents allows each to apply changes that it becomes aware of. Thanks to the invocation log (or equivalent integrated directly into the CRDT), provenance of authority is made transparent.
```
sequenceDiagram
participant CRDT as Initial Grow-Only Set (CRDT)
actor Alice
actor Bob
actor Carol
autonumber
Note over CRDT, Bob: Setup
CRDT -->> Alice: delegate(CRDT_ID, merge)
CRDT -->> Bob: delegate(CRDT_ID, merge)
Note over Bob, Carol: Bob Invites Carol
Bob -->> Carol: delegate(CRDT_ID, merge)
Note over Alice, Carol: Direct P2P Gossip
Carol ->> Bob: invoke(CRDT_ID, merge, {"Carrot"}, proof: [➋,❸])
Alice ->> Carol: invoke(CRDT_ID, merge, {"Apple"}}, proof: [➊])
Bob ->> Alice: invoke(CRDT_ID, merge, {"Banana", "Carrot"}, proof: [➋])
```
## Wrapping Existing Systems
[Section titled “Wrapping Existing Systems”](#wrapping-existing-systems)
In the RECOMMENDED scenario, the agent controlling a resource has a unique reference to it. This is always possible in a system that has adopted capabilities end-to-end.
Interacting with existing systems MAY require relying on ambient authority contained in an ACL, non-unique reference, or other authorization logic. These cases are still compatible with UCAN, but the security guarantees are weaker since 1. the surface area is larger, and 2. part of the auth system lives outside UCAN.
```
sequenceDiagram
participant Database
participant ACL as External Auth System
actor DBAgent
actor Alice
actor Bob
Note over ACL, DBAgent: Setup
DBAgent ->> ACL: signup(DBAgent)
ACL ->> ACL: register(DBAgent)
autonumber 1
Note over DBAgent, Bob: Delegation
DBAgent -->> Alice: delegate(DBAgent, write)
Alice -->> Bob: delegate(DBAgent, write)
Note over Database, Bob: Invocation
Bob ->>+ DBAgent: invoke(DBAgent, [write, key, value], proof: [➊,➋])
critical External System
DBAgent ->> ACL: getToken(write, key, AuthGrant)
ACL ->> DBAgent: AccessToken
DBAgent ->> Database: request(write, value, AccessToken)
Database ->> DBAgent: ACK
end
DBAgent ->>- Bob: ACK
```
# FAQ
[Section titled “FAQ”](#faq)
## What prevents an unauthorized party from using an intercepted UCAN?
[Section titled “What prevents an unauthorized party from using an intercepted UCAN?”](#what-prevents-an-unauthorized-party-from-using-an-intercepted-ucan)
UCANs always contain information about the sender and receiver. A UCAN is signed by the sender (the `iss` field DID) and can only be created by an agent in possession of the relevant private key. The recipient (the `aud` field DID) is required to check that the field matches their DID. These two checks together secure the certificate against use by an unauthorized party. [UCAN Invocations](/invocation/) prevent use by an unauthorized party by signing over a request to use the capability granted in a delegation chain.
## What prevents replay attacks on the invocation use case?
[Section titled “What prevents replay attacks on the invocation use case?”](#what-prevents-replay-attacks-on-the-invocation-use-case)
All UCAN Invocations MUST have a unique CID. The executing agent MUST check this validation uniqueness against a local store of unexpired UCAN hashes.
This is not a concern when simply delegating since receiving a delegation is idempotent.
## Is UCAN secure against person-in-the-middle attacks?
[Section titled “Is UCAN secure against person-in-the-middle attacks?”](#is-ucan-secure-against-person-in-the-middle-attacks)
*UCAN does not have any special protection against person-in-the-middle (PITM) attacks.*
If a PITM attack was successfully performed on a UCAN delegation, the proof chain would contain the attacker’s DID(s). It is possible to detect this scenario and revoke the relevant UCAN but this does require special inspection of the topmost `iss` field to check if it is the expected DID. Therefore, it is strongly RECOMMENDED to only delegate UCANs to agents that are both trusted and authenticated and over secure channels.
## Can my implementation support more cryptographic algorithms?
[Section titled “Can my implementation support more cryptographic algorithms?”](#can-my-implementation-support-more-cryptographic-algorithms)
It is possible to use other algorithms, but doing so limits interoperability with the broader UCAN ecosystem. This is thus considered “off spec” (i.e. non-interoperable). If you choose to extend UCAN with additional algorithms, you MUST include this metadata in the (self-describing) [Varsig](/varsig/) header.
# Related Work and Prior Art
[Section titled “Related Work and Prior Art”](#related-work-and-prior-art)
[SPKI/SDSI](https://datatracker.ietf.org/wg/spki/about/) is closely related to UCAN. A different encoding format is used, and some details vary (such as a delegation-locking bit), but the core idea and general usage pattern are very close. UCAN can be seen as making these ideas more palatable to a modern audience and adding a few features such as content IDs that were less widespread at the time SPKI/SDSI were written.
[ZCAP-LD](https://w3c-ccg.github.io/zcap-spec/) is closely related to UCAN. The primary differences are in formatting, addressing by URL instead of CID, the mechanism of separating invocation from authorization, and single versus multiple proofs.
[CACAO](https://blog.ceramic.network/capability-based-data-security-on-ceramic/) is a translation of many of these ideas to a cross-blockchain delegated bearer token model. It contains the same basic concepts as UCAN delegation, but is aimed at small messages and identities that are rooted in mutable documents rooted on a blockchain and lacks the ability to subdelegate capabilities.
[Local-First Auth](https://github.com/local-first-web/auth) is a non-certificate-based approach, instead relying on a CRDT to build up a list of group members, devices, and roles. It has a friendly invitation mechanism based on a [Seitan token exchange](https://book.keybase.io/docs/teams/seitan). It is also straightforward to see which users have access to what, avoiding the confinement problem seen in many decentralized auth systems.
[Macaroon](https://theory.stanford.edu/~ataly/Papers/macaroons.pdf) is a MAC-based capability and cookie system aimed at distributing authority across services in a trusted network (typically in the context of a Cloud). By not relying on asymmetric signatures, Macaroons achieve excellent space savings and performance, given that the MAC can be checked against the relevant services during discharge. The authority is rooted in an originating server rather than with an end-user.
[Biscuit](https://github.com/biscuit-auth/biscuit/) uses Datalog to describe capabilities. It has a specialized format but is otherwise in line with UCAN.
[Verifiable credentials](https://www.w3.org/2017/vc/WG/) are a solution for data about people or organizations. However, they are aimed at a related-but-distinct problem: asserting attributes about the holder of a DID, including things like work history, age, and membership.
# Acknowledgments
[Section titled “Acknowledgments”](#acknowledgments)
Thank you to [Brendan O’Brien](https://github.com/b5) for real-world feedback, technical collaboration, and implementing the first Golang UCAN library.
Thank you [Blaine Cook](https://github.com/blaine) for the real-world feedback, ideas on future features, and lessons from other auth standards.
Many thanks to [Hugo Dias](https://github.com/hugomrdias), [Mikael Rogers](https://github.com/mikeal/), and the entire DAG House team for the real world feedback, and finding inventive new use cases.
Thank to [Hannah Howard](https://github.com/hannahhoward) and [Alan Shaw](https://github.com/alanshaw) at [Storacha](https://storacha.network/) for their team’s feedback from real world use cases.
Many thanks to [Brian Ginsburg](https://github.com/bgins) and [Steven Vandevelde](https://github.com/icidasset) for their many copy edits, feedback from real world usage, maintenance of the TypeScript implementation, and tools such as [ucan.xyz](https://ucan.xyz).
Many thanks to [Christopher Joel](https://github.com/cdata) for his real-world feedback, raising many pragmatic considerations, and the Rust implementation and related crates.
Many thanks to [Christine Lemmer-Webber](https://github.com/cwebber) for her handwritten(!) feedback on the design of UCAN, spearheading the [OCapN](https://github.com/ocapn/ocapn) initiative, and her related work on [ZCAP-LD](https://w3c-ccg.github.io/zcap-spec/).
Many thanks to [Alan Karp](https://github.com/alanhkarp) for sharing his vast experience with capability-based authorization, patterns, and many right words for us to search for.
Thanks to [Benjamin Goering](https://github.com/gobengo) for the many community threads and connections to [W3C](https://www.w3.org/) standards.
Thanks to [Juan Caballero](https://github.com/bumblefudge) for the numerous questions, clarifications, and general advice on putting together a comprehensible spec.
Thank you [Dan Finlay](https://github.com/danfinlay) for being sufficiently passionate about [OCAP](http://erights.org/elib/capability/index.html) that we realized that capability systems had a real chance of adoption in an ACL-dominated world.
Thanks to [Peter van Hardenberg](https://www.pvh.ca) and [Martin Kleppmann](https://martin.kleppmann.com/) of [Ink & Switch](https://www.inkandswitch.com/) for conversations exploring options for access control on CRDTs and [local-first](https://www.inkandswitch.com/local-first/) applications.
Thanks to the entire [SPKI WG](https://datatracker.ietf.org/wg/spki/about/) for their closely related pioneering work.
We want to especially recognize [Mark Miller](https://github.com/erights) for his numerous contributions to the field of distributed auth, programming languages, and networked security writ large.
## Footnotes
[Section titled “Footnotes”](#footnote-label)
1. To be precise, this is a [PC/EC](https://en.wikipedia.org/wiki/PACELC_theorem) system, which is a critical trade-off for many systems. UCAN can be used to model both PC/EC and PA/EL, but is most typically PC/EL. [↩](#user-content-fnref-pcec)
2. JavaScript has a single numeric type (\[`Number`]\[JS Number]) for both integers and floats. This representation is defined as a [IEEE-754](https://ieeexplore.ieee.org/document/8766229) double-precision floating point number, which has a 53-bit significand. [↩](#user-content-fnref-js-num-size) [↩2](#user-content-fnref-js-num-size-2) [↩3](#user-content-fnref-js-num-size-3)
# Varsig Specification
> Varsig is a [multiformat][Multiformats] for compactly describing signatures over data and any codec information to serialize the signed data correctly. It is on...
# Abstract
[Section titled “Abstract”](#abstract)
Varsig is a [multiformat](https://multiformats.io) for compactly describing signatures over data and any codec information to serialize the signed data correctly. It is only a description of the signature configuration, but not the signature itself.
# Introduction
[Section titled “Introduction”](#introduction)
Common formats such as [JWT](https://www.rfc-editor.org/rfc/rfc7519) use encoding (e.g. [base64](https://en.wikipedia.org/wiki/Base64)) and text separators (e.g. `"."`) to pass around encoded data and their signatures:
```js
// JWT
"eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCIsInVjdiI6IjAuOC4xIn0.eyJhdWQiOiJkaWQ6a2V5Ono
2TWtyNWFlZmluMUR6akc3TUJKM25zRkNzbnZIS0V2VGIyQzRZQUp3Ynh0MWpGUyIsImF0dCI6W3sid2l
0aCI6eyJzY2hlbWUiOiJ3bmZzIiwiaGllclBhcnQiOiIvL2RlbW91c2VyLmZpc3Npb24ubmFtZS9wdWJ
saWMvcGhvdG9zLyJ9LCJjYW4iOnsibmFtZXNwYWNlIjoid25mcyIsInNlZ21lbnRzIjpbIk9WRVJXUkl
URSJdfX1dLCJleHAiOjkyNTY5Mzk1MDUsImlzcyI6ImRpZDprZXk6ejZNa2tXb3E2UzN0cVJXcWtSbnl
NZFhmcnM1NDlFZnU2cUN1NHVqRGZNY2pGUEpSIiwicHJmIjpbXX0.SjKaHG_2Ce0pjuNF5OD-b6joN1S
IJMpjKjjl4JE61_upOrtvKoDQSxZ7WeYVAIATDl8EmcOKj9OqOSw0Vg8VCA"
```
Many binary-as-text encodings are inefficient and inconvenient. Others have opted to use canonicalization and a tag. This can be effective, but requires careful handling and signaling of the specific canonicalization method used (such as [DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/)).
```js
const payload = canonicalize({"hello": "world", "count": 42})
{payload: payload, sig: key.sign(sha256(payload))}
```
Directly signing over canonicalized data introduces new problems: forced encoding and canonicalization attacks.
## Forced Encoding
[Section titled “Forced Encoding”](#forced-encoding)
Data must first be rendered to binary before signing. This means imposing some encoding. There is no standard way to include the encoding that some IPLD was encoded with other than a [CID](https://docs.ipfs.tech/concepts/content-addressing/). In IPFS, CIDs imply a link, which can have implications for network access and storage. Further, generating a CID means producing a hash, which is then potentially rehashed to conform to the cryptographic signature algorithm.
To remedy this, Varsig includes the encoding information used in production of the signature.
## Canonicalization Attacks
[Section titled “Canonicalization Attacks”](#canonicalization-attacks)
Since formats like [IPLD](https://ipld.io/docs/) and [JCS](https://www.rfc-editor.org/rfc/rfc8785) are deterministically encoded, it can be tempting to rely on canonicalization at validation time, rather than storing the serialized bytes. Since the original payload can be rederived from the output, this can seem like a clean option:
```javascript
// DAG-JSON
{
"role": "user",
"links": [
{"/": "bafkreidb2q3ktgtlm5yio7buj3sypyghjtfh5ernsteqmakf4p2c5bwmyi"},
{"/": "bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"},
{"/": "bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"}
],
"sig": "8ufaS9w3CGN8cbQTUSoL1i7eaKiWLSXsD2LbZVmvM9zF"
}
```
Unfortunately this opens the potential for [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). [Parsers for certain formats](https://www.blackhat.com/presentations/bh-usa-07/Hill/Whitepaper/bh-usa-07-hill-WP.pdf) — such as JSON — are known to [handle duplicate entries differently](https://latacora.micro.blog/2019/07/24/how-not-to.html). IPLD MUST be serialized to a canonical form before checking the signature. Without careful handling, it is possible to fail to check if any additional fields have been added to the payload which will be parsed by the application.
> An object whose names are all unique is interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings. When the names within an object are not unique, the behavior of software that receives such an object is unpredictable. Many implementations report the last name/value pair only. Other implementations report an error or fail to parse the object, and some implementations report all of the name/value pairs, including duplicates.
>
> — [RFC 8259](https://www.rfc-editor.org/rfc/rfc8259#page-10)
```json
{
"role": "user", // Parsed by an IPLD implementation
"role": "admin", // Malicious duplicate field, omitted by the IPLD parser, accepted by the browser
"links": [
{"/": "bafkreidb2q3ktgtlm5yio7buj3sypyghjtfh5ernsteqmakf4p2c5bwmyi"},
{"/": "bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"},
{"/": "bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"}
],
"sig": "8ufaS9w3CGN8cbQTUSoL1i7eaKiWLSXsD2LbZVmvM9zF"
}
```
In the above example, the canonicalization step MAY lead to the signature passing validation, but the client parsing the `role: "admin"` field instead.
### Example
[Section titled “Example”](#example)
The above can be [subtle](https://link.springer.com/chapter/10.1007/978-3-642-14577-3_22). Here is a step by step example of one such scenario.
An application receives some block of data, as binary. It checks the claimed CID, which passes validation.
```plaintext
0x7ba202022726f6c65223a202275736572222ca202022726f6c65223a202261646d696e222ca202
0226c696e6b73223a205ba202020207b222f223a20226261666b72656964623271336b7467746c6d
3579696f3762756a337379707967686a7466683565726e737465716d616b66347032633562776d79
69227d2c202020202020202020202020202020202020202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202020202020
20202020202020202020202020202020202020202020202020202020202020202020202020202020
2020202020202020202020207b222f223a20226261666b72656963373579646735766b773332346f
716b636d716c74667663336b6976796e67716b69626a6f7973647769696c616b68347a356665227d
2ca202020207b222f223a20226261666b726569666664697a3672616634367a72723362327573756
6677a35666f34346167676d6f637a347a61707072366b6868686c6a63647079227da20205d2ca202
022736967223a2022387566615339773343474e386362515455536f4c31693765614b69574c53587
344324c625a566d764d397a4622a7d
```
Decoded to a string, the above reads as follows:
```plaintext
{\n
{\n
"role": "user",\n
"role": "admin",\n
"links": [\n
{"/": "bafkreidb2q3ktgtlm5yio7buj3sypyghjtfh5ernsteqmakf4p2c5bwmyi"},\n
{"/": "bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"},\n
{"/": "bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"}\n
],\n
},\n
"sig": "8ufaS9w3CGN8cbQTUSoL1i7eaKiWLSXsD2LbZVmvM9zF"\n
}
```
> \[!NOTE] The JSON above contains a duplicate `role` key.
Next, the application parses the JSON with the browser’s native JSON parser. Only one `role` key is possible in a JavaScript object, and which one is kept is not consistent across implementations.
```json
{
{
"role": "admin", // Picked the second key
"links": [
{"/": "bafkreidb2q3ktgtlm5yio7buj3sypyghjtfh5ernsteqmakf4p2c5bwmyi"},
{"/": "bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"},
{"/": "bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"}
]
},
"sig": "8ufaS9w3CGN8cbQTUSoL1i7eaKiWLSXsD2LbZVmvM9zF"
}
```
The application MUST check the signature of all fields minus the `sig` field. Under the assumption that the binary input was safe, and that canonicalization allows for the deterministic manipulation of the payload, the object is parsed to an internal representation.
```rust
{
role: "user",
links: [
Cid("bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"),
Cid("bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"),
Cid("bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"),
],
sig: 0xf2e7da4bdc3708637c71b413512a0bd62ede68a8962d25ec0f62db6559af33dc
}
```
> \[!NOTE] In our scenario, the parser has dropped the `role: "admin"` key. This is nondeterministic based on the specific implementation.
The `sig` field is then removed, and the remaining fields serialized to binary;
```rust
serialize!({
role: "user",
links: [
Cid("bafkreidb2q3ktgtlm5yio7buj3sypyghjtfh5ernsteqmakf4p2c5bwmyi"),
Cid("bafkreic75ydg5vkw324oqkcmqltfvc3kivyngqkibjoysdwiilakh4z5fe"),
Cid("bafkreiffdiz6raf46zrr3b2usufgz5fo44aggmocz4zappr6khhhljcdpy"),
]
}).to_json()
```
The signature is then checked against the above fields, which passes since there’s only a `role: "user"` entry. The application then uses the original JSON with the `role: "admin"` entry.
# Safety
[Section titled “Safety”](#safety)
Data already parsed to an in-memory representation can be canonically encoded trivially: it has already been through a [parser / validator](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/).
Data purporting to conform to an IPLD encoding (such as [DAG-JSON](https://ipld.io/specs/codecs/dag-json/spec/)) MUST be validated prior to signature verification. This MAY be as simple as round-trip decoding/encoding the JSON and checking that the hash matches. A validation error MUST be signaled if it does not match.
> Implementers may provide an opt-in for systems where round-trip determinism is a desireable \[sic] feature and backward compatibility with old, non-strict data is unnecessary.
>
> — [DAG-JSON Spec](https://ipld.io/specs/codecs/dag-json/spec/)
As it is critical for guarding against various attacks, the assumptions around canonical encoding MUST be enforced.
# Format
[Section titled “Format”](#format)
A Varsig MUST have metadata about both the [signature](#signature) and [payload encoding](#payload-encoding) that was signed over. Either field MAY be composed of one or more segments. The number of segments MUST be determined by the first segment. Recursive sub-segments MAY be used.
Varsig itself MUST contain the following segments:
* [Prefix](#varsig-multicodec-prefix): The Varsig [multicodec](https://github.com/multiformats/multicodec) prefix `0x34`
* [Version](#version): The Varsig version number `0x01`
* [Signature Algorithm](#signature-algorithm): A signature algorithm tag and any additional fields needed to configure it
* [Payload Encoding](#payload-encoding): The codec used to render the payload to binary
A Varsig MUST begin with one or more segments that configure the signature.
```
block-beta
columns 4
Varsig:4
prefix["Varsig Prefix\n0x34"]
version["Version 1\n0x01"]
SigDetails["Signature Algorithm"]
Encoding["Payload Encoding"]
style Varsig fill:none;stroke:none;
```
ABNF
```abnf
varsig-v1-header = %x34 %x01 signature-algorithm-metadata payload-encoding-metadata
signature-algorithm-metadata = unsigned-varint
payload-encoding-metadata = unsigned-varint
```
For example, an [RS256](https://datatracker.ietf.org/doc/html/rfc7518) signature over some [DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/) is as follows:
```
block-beta
block:Header
columns 1
vsig_header["Header"]
block:HeaderBody
columns 2
prefix["Varsig Prefix\n0x34"]
version["Version 1\n0x01"]
end
end
block:Algo
columns 1
algo_header["Algorithm"]
block:AlgoBody
rsa["RSA\n0x1205"]
sha2_256["SHA2-256\n0x12"]
len["256-bytes\n0x0100"]
end
end
block:Encoding
columns 1
encoding_header["Payload Encoding"]
block:EncodingBody
dag_cbor["DAG-CBOR\n0x71"]
end
end
style Header fill:none;stroke:none;
style vsig_header fill:none;stroke:none;
style Algo fill:none;stroke:none;
style algo_header fill:none;stroke:none;
style Encoding fill:none;stroke:none;
style encoding_header fill:none;stroke:none;
style prefix width:120;
style dag_cbor width:250;
```
A (canonicalized) [JWT](https://www.rfc-editor.org/rfc/rfc7519) signed with [ES256K](https://w3c-ccg.github.io/lds-ecdsa-secp256k1-2019/) is as follows:
```
block-beta
block:Header
columns 1
vsig_header["Header"]
block:HeaderBody
columns 2
prefix["Varsig Prefix\n0x34"]
version["Version 1\n0x01"]
end
end
block:Algo
columns 1
algo_header["Algorithm"]
block:AlgoBody
ecdsa["ECDSA\n0xEC"]
curve["secp256r1\n0x1200"]
sha2_256["Keccak-256\n0x1B"]
end
end
block:Encoding
columns 1
encoding_header["Payload Encoding"]
block:EncodingBody
jwt["JWT\n0x6A77"]
end
end
style Header fill:none;stroke:none;
style vsig_header fill:none;stroke:none;
style Algo fill:none;stroke:none;
style algo_header fill:none;stroke:none;
style Encoding fill:none;stroke:none;
style encoding_header fill:none;stroke:none;
style prefix width:170;
style ecdsa width:110;
style jwt width:350;
```
## Prefix
[Section titled “Prefix”](#prefix)
The Varsig prefix MUST be the [multicodec](https://github.com/multiformats/multicodec) value `0x34`.
## Version
[Section titled “Version”](#version)
A Varsig v1 MUST use the `0x01` version tag.
## Signature Algorithm
[Section titled “Signature Algorithm”](#signature-algorithm)
The signature algorithm field MUST consist of one or more unsigned varint ([LEB128](https://en.wikipedia.org/wiki/LEB128)) segments. The first segment MUST act as a discriminant for the signature algorithm plus the number and type of the fields used to configure that signature type.
| Prefix | [LEB128](https://en.wikipedia.org/wiki/LEB128) Varint | Segments | Description |
| -------- | ----------------------------------------------------- | ---------------------------------- | ----------------------------------- |
| `0xB1` | `0xB101` | `bls-public-key-curve` `multihash` | BLS12\_381 (public key on G1 or G2) |
| `0xEC` | `0xEC01` | `ecdsa-curve` `multihash` | ECDSA (e.g. ES256) |
| `0xED` | `0xED01` | `eddsa-curve` `multihash` | EdDSA (e.g. Ed25519, Ed448) |
| `0x1205` | `0x8524` | `rsa-byte-length` `multihash` | RSASSA-PKCS #1 v1.5 |
ABNF
```abnf
varsig-signature-algorithm
= %xB1 bls-public-key-curve multihash-header ; BLS
/ %xEC ecdsa-curve multihash-header ; ECDSA
/ %xED eddsa-curve multihash-header ; EdDSA
/ %x1205 rsa-size multihash-header ; RSASSA-PKCS #1 v1.5
```
## Payload Encoding
[Section titled “Payload Encoding”](#payload-encoding)
Canonical encodings are convenient for many applications since they allow for efficient storage, compact internal representations, or the conversion between formats like JSON and CBOR. Unfortunately signatures require signing over specific bytes, and thus over a specific encoding of the data. To facilitate this, the type `varsig-encoding-metadata` MUST be used:
| Code | [LEB128](https://en.wikipedia.org/wiki/LEB128) Varint | Description |
| -------- | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| `0x5F` | `0x5F` | Byte-identical payload (no additional encoding) |
| `0x71` | `0x71` | [DAG-CBOR](https://ipld.io/docs/codecs/known/dag-cbor/) |
| `0x0129` | `0xa902` | [DAG-JSON](https://ipld.io/specs/codecs/dag-json/spec/) |
| `0xE191` | `0x91c303` | [EIP-191 “personal sign”](https://eips.ethereum.org/EIPS/eip-191#version-0x45-e) |
ABNF
```abnf
varsig-encoding-metadata
= %x5F ; Byte-identical payload (no additional encoding)
/ %x71 ; DAG-CBOR multicodec prefix
/ %x0129 ; DAG-JSON multicodec prefix
/ %xE191 varsig-encoding-info ; EIP-191 "personal sign"
```
# Signing Over Varsig
[Section titled “Signing Over Varsig”](#signing-over-varsig)
Including the Varsig in the payload that is signed over is RECOMMENDED. Doing so eliminates any ambiguity of the signed payload format and signature algorithm configuration.
# Acknowledgments
[Section titled “Acknowledgments”](#acknowledgments)
Thanks to [Michael Muré](https://github.com/MichaelMure) for feedback from real-world implementation.
Our gratitude to [Dave Huseby](https://github.com/dhuseby) for his parallel work and critiques of our earlier design.