Sharing and Accepting Capabilities (CapTP)

The Capability Transport Protocol (CapTP) is how capabilities move between agents. Whether you are delegating access to a sub-agent, accepting a capability from a colleague, or sending a message to an offline recipient, CapTP handles the transport, persistence, and verification.

Key Concepts

Live References vs. Sturdy References

A live reference exists only within an active session between two agents. It is fast (no lookup required) but ephemeral -- if either party disconnects, the reference is gone.

A sturdy reference survives disconnection, serialization, and restart. It encodes the target cell, a Swiss number (unforgeable designator), and a routing hint. You can save a sturdy ref to disk, email it, or seal it in a box for later delivery.

Store-and-Forward

If your target is offline, CapTP queues the message in their MerkleQueue inbox. You pay a small deposit (refunded when the recipient processes the message). Messages persist until the recipient comes online or the TTL expires.

Sharing a Capability

Via the Cipherclerk (Browser Extension)

  1. Open the cipherclerk and navigate to "Tokens".
  2. Select the token you want to share.
  3. Click "Delegate" and enter the recipient's public key (or scan a QR code).
  4. Choose restrictions: which actions, what TTL, what budget limit.
  5. Click "Send". The cipherclerk attenuates the token and transmits via CapTP.

Via the CLI

# Delegate a capability with restrictions
dregg cap delegate \
  --token $TOKEN_ID \
  --to $RECIPIENT_PUBKEY \
  --restrict service=storage,action=read,budget=1000 \
  --ttl 24h

# The recipient can accept with:
dregg cap accept --from $YOUR_PUBKEY

Via the SDK (Programmatic)

use dregg_sdk::{AgentCipherclerk, Attenuation};

let mut alice = AgentCipherclerk::new();
let mut bob = AgentCipherclerk::new();

let root = alice.mint_token(b"secret-key-here-32-bytes!!!!!!!!", "compute");

// Alice delegates to Bob with restrictions
let delegated = alice.delegate(&root, &bob.public_key(), &Attenuation {
    services: vec![("compute".into(), "inference".into())],
    max_budget: Some(5000),
    max_ttl: Some(std::time::Duration::from_secs(3600)),
    ..Default::default()
}).unwrap();

// Bob receives the signed delegation envelope. He passes the authority policy
// (here: "I trust envelopes signed by Alice's pubkey") so the SDK can verify
// the envelope binding before accepting.
bob.receive_signed_delegation(
    delegated,
    &DelegationAuthority::TrustedKey(alice.public_key()),
).unwrap();

Accepting a Capability

When someone delegates a capability to you, it arrives in your inbox. The cipherclerk (or CLI) validates:

You can inspect the capability before accepting -- see what service it grants, what restrictions apply, when it expires, and who delegated it.

Offline Delivery (Sealed Boxes)

To send a capability to someone who is offline, CapTP uses sealed boxes (X25519 + ChaCha20-Poly1305 encryption):

  1. The sender seals the capability under the recipient's public key.
  2. The sealed box is deposited in the recipient's MerkleQueue inbox (federation stores it).
  3. When the recipient comes online, they unseal using their private key.

The sealed box reveals nothing to the federation or any observer -- only that a message of a certain size was deposited at a certain time.

MerkleQueue inbox — four sealed messages awaiting decryption.

Four sealed capabilities sit in the recipient’s MerkleQueue. The federation can see counts and ciphertext lengths; it cannot see senders, capability contents, or which messages link to which prior interactions.

Three-Party Introduction

If Alice holds capabilities to both Bob and Carol, she can introduce them:

  1. Alice invokes Effect::Introduce in a turn, granting Bob a (possibly attenuated) capability to Carol.
  2. Bob receives a routing directive telling him how to reach Carol.
  3. Bob can now communicate directly with Carol -- no further involvement from Alice.

This is how new communication paths form in dregg. There is no global directory; all paths are introduced.

Promise Pipelining

You can send messages to the result of a pending operation without waiting for it to complete. If you request a service and the result will be a new capability, you can start using that capability immediately -- the messages queue and deliver once the promise resolves. This eliminates round-trip latency in multi-step workflows.

Revoking Shared Capabilities

After sharing a capability, you may need to revoke it:

# Revoke a specific delegation
dregg cap revoke --token $TOKEN_ID --delegation $DELEGATION_HASH

# Trip a revocation channel (instant, all subscribers affected)
dregg cap trip-channel --channel $CHANNEL_ID

Next Steps