Skip to content

Instantly share code, notes, and snippets.

@fforbeck
Created November 4, 2025 16:21
Show Gist options
  • Select an option

  • Save fforbeck/6cde68d9d093533531a479fa5b24cca5 to your computer and use it in GitHub Desktop.

Select an option

Save fforbeck/6cde68d9d093533531a479fa5b24cca5 to your computer and use it in GitHub Desktop.

How UCAN/Attest Works

Core Concept

ucan/attest enables an authority (like the Storacha service) to attest that a linked UCAN delegation is valid. It's essentially a way for a trusted service to vouch for a delegation that might otherwise be unverifiable.

Structure

{
  iss: "did:web:storacha.network",  // Service/Authority
  aud: "did:key:z6Mk...",            // Agent receiving attestation
  att: [{
    with: "did:web:storacha.network", // Authority DID
    can: "ucan/attest",
    nb: {
      proof: { "/": "bafy..." }       // CID of attested delegation
    }
  }],
  exp: 1234567890,                    // Expiration timestamp
  sig: "..."
}

Key Use Cases

1. Out-of-Band Verification

When a delegation requires external authorization (like email confirmation for did:mailto), the service can attest to it after verification.

2. Absent Signatures

Allows delegations signed with "absent signatures" (zero bytes) to be validated through service attestation instead of cryptographic verification.

3. Proof Chain Compaction

Reduces payload size by attesting to intermediate proofs rather than including entire delegation chains.

Real Migration Example: Gateway Authorization

// Step 1: Create delegation with Absentee issuer 
// (space doesn't have private key, so we use absent signature)
const delegation = await SpaceCapabilities.contentServe.delegate({
  issuer: Absentee.from({ id: space }),  // Space DID as issuer (absent sig)
  audience: gatewayPrincipal,            // Gateway as audience
  with: space,                           // Space DID
  nb: {},                                // No additional caveats
  expiration: Infinity,                  // Permanent delegation
})
console.log(`✓ Delegation created: ${delegation.cid}`)

// Step 2: Service attests the delegation 
// (proves service authorized this on behalf of the space)
const attestation = await UCAN.attest.delegate({
  issuer: serviceSigner,              // did:web:storacha.network
  audience: serviceSigner,            // Service attests to itself
  with: serviceSigner.did(),          // Service DID
  nb: { proof: delegation.cid },      // Links to delegation above
  expiration: Infinity,               // Permanent attestation
})
console.log(`✓ Attestation created: ${attestation.cid}`)

// Step 3: Publish to gateway via access/delegate
const result = await Access.delegate.invoke({
  issuer: serviceSigner,
  audience: gatewayPrincipal,
  with: space,
  nb: {
    delegations: {
      [delegation.cid.toString()]: delegation.cid,
    },
  },
  proofs: [delegation, attestation],  // Both required for authorization
}).execute(gatewayConnection)

Why This Works

  1. Delegation - Grants content/serve capability from space to gateway

    • Signed with "absent signature" (space has no private key)
    • Cannot be cryptographically verified on its own
  2. Attestation - Service vouches for the delegation

    • Service has verified the space exists and migration is authorized
    • Service's signature proves authenticity
  3. Together - They form a valid authorization chain

    • Gateway can now serve content from this space
    • Authorization is verifiable through service attestation

Key Points

  • Absentee Issuer: Absentee.from({ id: space }) creates an issuer without a private key
  • Service Authority: Only the service can create valid attestations
  • Proof Chain: The attestation's nb.proof field links to the delegation CID
  • Both Required: The invocation includes both delegation and attestation in the proofs array

This pattern allows the service to authorize actions on behalf of spaces that don't have accessible private keys, which is exactly what we need for migrating legacy content.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment