How to Delegate Capabilities with UCAN
Mint UCAN tokens, delegate capabilities to other identities, and invoke delegated commands.
How to Delegate Capabilities with UCAN
This guide shows you how to delegate capabilities to another identity using UCAN (User-Controlled Authorization Networks) and how to invoke commands with a delegation token.
How capability-based auth works here
In Hyperauth, capabilities are granted by issuing a UCAN token that specifies what a delegate is allowed to do. The issuer's DID signs the token. The delegate presents the token when invoking a command — no central authority checks permissions at runtime.
The vault must be unlocked for all operations below, as they require access to the identity's signing key.
Mint a UCAN token
mintUcan produces a signed UCAN from the identity's key material. You supply the raw UCAN payload as a plain object. The vault signs it internally.
const result = await client.mintUcan(encryptedShares, {
iss: myDid,
aud: delegateDid,
exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
att: [
{
with: `hyperauth:${myDid}`,
can: '/vault/read',
},
],
prf: [],
});
const token = result.token; // signed JWT-style UCAN stringencryptedShares comes from the identity's QueryOutput — specifically the encrypted_shares field on the identity record returned from client.query().
Delegate a capability
delegate is a higher-level method that creates and stores a delegation in the vault.
await client.delegate({
to: 'did:key:z6Mk...', // the delegate's DID
command: '/vault/read', // UCAN command path
expiration: Math.floor(Date.now() / 1000) + 86400, // optional, seconds
caveats: { resource: 'profile' }, // optional attenuation
});If the delegation fails (for example, the delegate DID is not found or the vault is locked), delegate throws a PluginCallError.
If you need to delegate without an expiration:
await client.delegate({
to: delegateDid,
command: '/payments/send',
});Invoke a command with a delegation token
createInvocation executes a UCAN command by presenting a delegation token. Use this when acting on behalf of another identity that has delegated a capability to you.
await client.createInvocation({
command: '/vault/read',
args: { key: 'profile' },
token: delegationToken, // the UCAN token string from the delegator
});If you hold a UCAN delegation chain (multiple hops), pass the innermost token. The vault resolves the proof chain internally.
If the invocation fails because the token is expired or the command does not match the delegation, createInvocation throws a PluginCallError with the error from the vault.
Delegation without expiration or caveats
Both fields are optional. A minimal delegation:
await client.delegate({
to: delegateDid,
command: '/profile/update',
});A minimal invocation:
await client.createInvocation({
command: '/profile/update',
args: { displayName: 'Alice' },
token: myToken,
});