Skip to content

UCAN container Specification

User-Controlled Authorization Network (UCAN) 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.

The UCAN spec itself is transport agnostic. This specification describes how to transfer one or more UCAN tokens bundled together, regardless of the transport.

UCAN tokens, regardless of their kind (Delegation, Invocation, Revocation, Promise) 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 array. The ordering of tokens in the array MUST NOT matter. This array SHOULD NOT have duplicate entries.

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:

{
"ctn-v1": [
<token1 bytes>,
<token2 bytes>,
<token3 bytes>,
]
}

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:

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 hexHeader as ASCIIBase encodingCompression
0x40@raw bytesno compression
0x42Bbase64 std paddingno compression
0x43Cbase64 url (no padding)no compression
0x4DMraw bytesgzip
0x4FObase64 std paddinggzip
0x50Pbase64 url (no padding)gzip

For clarity, the resulting serialization is in the form of <header byte><cbor bytes, optionally compressed, optionally encoded>.

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, 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?”

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.

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.

Many thanks to all the Fission team and in particular to Brooklyn Zelenka for creating and pushing UCAN and other critical pieces like WNFS, and generally being awesome and supportive people.