Identity and Capabilities
So far the grocery list has lived on one device. Before Alice’s commits can reach Bob, they have to be stored somewhere both can reach, and that somewhere has to know who is allowed to write. Dialog has no user accounts on a server and no passwords. Everyone is a key, and permission is something one key signs over to another.
Everyone is a key
Every key Dialog creates is an Ed25519 key pair, named by its public key written as a did:key. The name is built from bytes like this:
The two-byte prefix ed 01 says what kind of key follows. The 34 bytes are written in base58, and prefixed with z to say so. The bytes above become did:key:z6MkgyhVBiwiVdpYtCepzRZFdQcdBNEZZjNBZN6CwyYC97tg. Every Ed25519 did:key starts with z6Mk, because that is what the prefix bytes turn into.
Several kinds of things are keys:
- A repository has its own key. Its DID is the repository’s name: in permissions, on a remote peer’s storage, and in the identities of its replicas and branches.
- An authority is someone responsible for changes: usually a person, like Alice, with a presence across several peers, such as her laptop and her phone. It is the key those peers act for. No peer holds it. The authority’s key is made once, when Alice first sets up, and sealed to the recovery credentials she keeps. Her peers act through permissions the authority signs for them.
- A peer is a place where replicas live, such as Alice’s laptop or Bob’s phone. One peer can hold replicas of many repositories. Its key never leaves it. The key is kept in a credential store of its own, apart from the storage that holds the data, and in a browser it is a non-extractable WebCrypto key, so even the page that uses it cannot read its secret half.
- A session is the same peer acting with a second key, under less. The key is usually derived from the peer’s key for one app or one purpose, and the session acts only under a permission from the peer. It is never handed the peer’s key or a repository’s key, so the app never holds either.
A secret key that has to outlive one device is never stored in the open. It is sealed to another key: encrypted so that only the holder of that key can open it. A key that other secrets are sealed to is called a vault, and an authority is one.
When Alice’s laptop creates the grocery list repository, it makes a fresh key for it. It seals the secret half to Alice, as its authority, and keeps a second copy sealed to itself, so it can sign as the repository without asking for her recovery credentials. The repository’s key then signs one permission, to the authority, allowing everything. Storage is handed only the public half. From then on, the repository is Alice’s because the repository itself said so.
Capabilities
A permission in Dialog is a capability: a signed statement that one key may do something on behalf of another. Dialog uses UCAN for these.
A delegation says “you may do this, as me.” It names an issuer, an audience, a subject, and a command, plus optional conditions and a time window. The subject is the repository the permission is about, or any, which lets the audience act wherever the issuer may. Delegations to devices and sessions are usually of that second kind. The command is a path, and a delegation for a path covers every command under it. A delegation for / covers everything.
An invocation says “do this, for this subject.” It is signed by the key that wants the work done, and it carries the delegations that prove it may.
The commands Dialog sends to a remote peer look like paths:
| Command | What it asks for |
|---|---|
/use/get/archive/block | read a block |
/use/put/archive/block | store a block |
/use/get/memory/cell | read a cell, such as a branch head |
/use/put/memory/cell | update a cell, with compare-and-swap |
/use/delete/memory/cell | remove a cell |
/use/get/archive/blob, /use/put/archive/blob | read or store an asset |
The first part of a command says what kind of thing it does. Everything under /use reads or writes. Destroying something, such as deleting a branch, is under /void, so a delegation for /use does not allow it. More commands exist than a remote peer ever sees: creating and listing branches, or keeping a peer’s contacts, are commands too, performed on the device and checked the same way.
Here is a typical chain that lets the session on Alice’s laptop store a block in the grocery list’s remote peer:
Each delegation’s issuer must be the previous one’s audience, and the first issuer must be the repository itself. The repository’s delegation to the authority names the repository as its subject. The authority’s delegation to the laptop, and the laptop’s to its session, are of the any kind. Bob gets access the same way: someone who holds a delegation for the repository signs a new one to Bob, possibly for a narrower command.
Asking a remote peer
A remote peer that speaks UCAN takes one HTTP request per operation. The signed container goes in the Authorization header and the body is the raw bytes. The command and subject are also copied into the URL, but only as labels for network logs: the remote peer reads them from the signed invocation and ignores the URL:
The remote peer checks, in order:
- The chain links up: each issuer is the previous audience, and the first issuer is the subject.
- The current time falls inside every delegation’s window.
- The command is covered by every delegation in the chain, and the arguments meet their conditions.
- Every signature holds, and no link has been revoked.
- The body is what the invocation promised. For a block, its BLAKE3 hash is the digest the invocation named and its SHA-256 is the checksum. For a cell, its SHA-256 is the checksum. For an asset, its size and digest match.
Only then does the remote peer store the block or answer the read. The remote peer never needs to know who Alice is. It only needs to know the repository’s DID, and to check signatures.
A second exchange exists for storage the remote peer does not proxy. There the remote peer answers an invocation with a presigned S3 URL, a permit, and the client talks to S3 directly. Which exchange to use is part of the remote peer’s address, not negotiated per request.
Revocation. UCAN lets an issuer revoke a delegation it signed, and the verifier in Dialog asks a revocation checker about every link of the chain. The checker Dialog ships today does not consult any store, so it reports every delegation as not revoked. Until a real store is plugged in, a delegation stays valid until its time window ends, and the repository’s delegation to its authority has no window at all.
Implementations. Keys are in dialog-credentials and signatures in dialog-varsig. Capabilities are dialog-capability, and the commands are defined in dialog-effects. UCAN tokens and chain checks are dialog-ucan-core. The HTTP exchange is dialog-remote-ucan. Creating a repository and delegating it to its authority is in dialog-peer/src/peer/space.rs. Vaults and authorities are in dialog-peer/src/peer/secret.rs, sealing in dialog-credentials/src/secret.rs, and the design is written up in notes/peer-and-session.md.