Chio/Docs

Capabilities

A capability token is a signed data structure that grants explicit authority. Agents cannot do anything without one. Every token declares exactly what is permitted, for how long, and by whom, then proves it with a cryptographic signature.

What Capabilities Are

Authority is not inherited; it is granted through signed tokens. The framing for why this matters lives in the Trust Model. This page is about the tokens themselves: their fields, their bindings, and the rules they have to satisfy.

A capability token answers four questions:

  1. Who issued it? The issuer public key identifies the Capability Authority or delegating agent.
  2. Who is it for? The subject public key binds the token to a specific agent.
  3. What does it allow? The scope declares which tools, resources, and prompts the bearer can use.
  4. When does it expire? issued_at and expires_at timestamps bound the token's validity window.

CapabilityToken Structure

The CapabilityToken struct contains eight fields. The signature covers the canonical JSON (per RFC 8785) of all other fields.

CapabilityToken
pub struct CapabilityToken {
    /// Unique token ID (UUIDv7 recommended, used for revocation).
    pub id: String,
    /// Capability Authority (or delegating agent) that issued this token.
    pub issuer: PublicKey,
    /// Agent this capability is bound to (DPoP sender constraint).
    pub subject: PublicKey,
    /// What this token authorizes.
    pub scope: ChioScope,
    /// Unix timestamp (seconds) when the token was issued.
    pub issued_at: u64,
    /// Unix timestamp (seconds) when the token expires.
    pub expires_at: u64,
    /// Ordered list of delegation links from the root CA to this token.
    pub delegation_chain: Vec<DelegationLink>,
    /// Ed25519 signature over canonical JSON of all fields above.
    pub signature: Signature,
}

Ed25519 signatures

All signatures use Ed25519 via the ed25519-dalek crate. Canonical JSON serialization follows RFC 8785, ensuring deterministic byte ordering across implementations.

ChioScope

The scope field is a ChioScope containing three grant vectors: one for tools, one for resources, and one for prompts. All canonical JSON serialization (used for signing) follows RFC 8785.

ChioScope
pub struct ChioScope {
    /// Individual tool grants.
    pub grants: Vec<ToolGrant>,
    /// Individual resource grants.
    pub resource_grants: Vec<ResourceGrant>,
    /// Individual prompt grants.
    pub prompt_grants: Vec<PromptGrant>,
}

The most common grant type is ToolGrant, which authorizes invocations of a specific tool on a specific server.

ToolGrant

Each ToolGrant specifies the server, tool, allowed operations, and optional constraints on invocation count, cost, and proof-of-possession:

ToolGrant
pub struct ToolGrant {
    /// Which tool server (by server_id from the manifest).
    pub server_id: String,
    /// Which tool on that server.
    pub tool_name: String,
    /// Allowed operations.
    pub operations: Vec<Operation>,
    /// Parameter constraints that narrow the tool's input space.
    pub constraints: Vec<Constraint>,
    /// Maximum number of invocations allowed under this grant.
    pub max_invocations: Option<u32>,
    /// Maximum monetary cost per single invocation.
    pub max_cost_per_invocation: Option<MonetaryAmount>,
    /// Maximum aggregate monetary cost across all invocations.
    pub max_total_cost: Option<MonetaryAmount>,
    /// If true, the kernel requires a valid DPoP proof for every invocation.
    pub dpop_required: Option<bool>,
}

The MonetaryAmount type carries a units field (minor units, e.g., cents for USD) and a currency field (ISO 4217 code). No floats, no rounding.


Time-Bounded Tokens

Every capability token has an issued_at and expires_at timestamp, both Unix seconds. There are no permanent grants. The kernel rejects any token where the current time falls outside this window.

Short-lived tokens bound the blast radius of a compromise. A stolen token expires on its own clock; revocation closes the gap before then. The window an attacker has is whichever one ends first.

Subject Binding

The subject field binds a token to a specific agent via its Ed25519 public key. A token issued to agent A cannot be used by agent B. The kernel verifies the subject matches the requesting agent's identity before processing any guards. DPoP is the per-invocation replay-resistance hardening on top of this subject binding.

DPoP (Demonstration of Proof of Possession)

Subject binding alone proves that a token was intended for an agent. DPoP proves the agent currently holds the corresponding private key. When dpop_required is set on a grant, the kernel requires a fresh, signed proof with each invocation.

This prevents token theft via replay. Even if an attacker intercepts a valid token, they cannot produce a valid DPoP proof without the agent's private key.

DPoP is per-grant

DPoP is configured at the grant level, not the token level. A single token can have some grants that require DPoP and others that do not. Use DPoP for high-value operations like writes and payments.

Delegation Chains

Capabilities can be delegated. An agent holding a token can issue a child token to another agent. The critical constraint: delegation can only attenuate (narrow) permissions, never amplify them.

A delegated token must have:

  • A scope that is a subset of the parent token's scope
  • An expiration that is no later than the parent's expiration
  • A max_invocations value that is no greater than the parent's
  • A max_cost_per_invocation value that is no greater than the parent's
  • A max_total_cost value that is no greater than the parent's

This attenuation rule (child scope is a subset, child expiry is no later, child max_invocations is no greater, child max_cost_per_invocation and max_total_cost are each no greater) is proved in Lean 4 as theorem family P1 Capability Monotonicity. The P1 bounded-model theorems are fully discharged under the claim-registry scope.

The delegation_chain field records the ordered list of delegation links from the root CA to the current token. The kernel walks this chain to verify that every link is a valid attenuation of its parent.

delegated-capability.yaml
# Root token: issued by CA, grants read_file + write_file
capability:
  id: cap_root_a1b2
  issuer: "ed25519:pub:ca-authority-key..."
  subject: "ed25519:pub:orchestrator-key..."
  scope:
    grants:
      - server_id: srv-files
        tool_name: read_file
        operations: [invoke]
        max_invocations: 100
      - server_id: srv-files
        tool_name: write_file
        operations: [invoke]
        max_invocations: 50
  issued_at: 1744536000
  expires_at: 1744539600
  delegation_chain: []
  signature: "ed25519:..."

# Delegated token: orchestrator → research-agent
# Attenuated: only read_file, fewer invocations, shorter window
capability:
  id: cap_child_c3d4
  issuer: "ed25519:pub:orchestrator-key..."
  subject: "ed25519:pub:research-agent-key..."
  scope:
    grants:
      - server_id: srv-files
        tool_name: read_file
        operations: [invoke]
        max_invocations: 25
  issued_at: 1744536000
  expires_at: 1744537800
  delegation_chain:
    - capability_id: cap_root_a1b2
      delegatee: "ed25519:pub:agent-key..."
      attenuations: []
      timestamp: 1744536000
      signature: "ed25519:..."
  signature: "ed25519:..."

In this example, the orchestrator narrows its authority before passing it to the research agent. The child token cannot invoke write_file (removed from scope), has a lower invocation cap (25 vs. 100), and expires 30 minutes sooner. The kernel verifies all of this by walking the delegation chain.


Revocation

The Capability Authority can revoke any token at any time by its id. Revocation cascades through the entire delegation subtree: revoking a parent token automatically invalidates every descendant derived from it. This cascading behavior is proved as P2 Revocation Completeness in the Lean 4 proof set.

The kernel checks the revocation list during token validation, before any guards run. A revoked token is treated the same as an expired token: the request is denied immediately.

Revocation is permanent

Once a token is revoked, it cannot be reinstated. Issue a new token if the agent needs to regain access. This prevents confusion about whether a previously-revoked token is trustworthy.

Example Capability Token

A complete capability token granting a research agent read-only filesystem access with a per-invocation cost cap:

capability-token.yaml
capability:
  id: cap_7f3a9b2c-e91d-4a5f-b8c1-d6e7f8a9b0c1
  issuer: "ed25519:pub:9c7b3f..."
  subject: "ed25519:pub:a4d8e2..."
  scope:
    grants:
      - server_id: srv-files
        tool_name: read_file
        operations: [invoke]
        constraints:
          - param: path
            pattern: "./workspace/**"
        max_invocations: 50
        max_cost_per_invocation:
          units: 10
          currency: USD
        max_total_cost:
          units: 200
          currency: USD
        dpop_required: false
      - server_id: srv-files
        tool_name: list_directory
        operations: [invoke]
        max_invocations: 100
    resource_grants: []
    prompt_grants: []
  issued_at: 1744536000
  expires_at: 1744539600
  delegation_chain: []
  signature: "ed25519:e5f6a7b8..."

This token allows the agent to call read_file (up to 50 times, constrained to paths under ./workspace/, max $0.10 per call, max $2.00 total) and list_directory (up to 100 times). It expires one hour after issuance.


Next Steps

  • Guards: how the guard pipeline enforces policy on every invocation
  • Receipts: cryptographic proof of every kernel decision
  • Economics: budgets, metering, and settlement