Capabilities

Arsenal Agent Capability Tokens (ACTs) and capability-scoped tool authority.

Capabilities

Forge agents do not act on their own authority. Every tool invocation is gated by an Agent Capability Token (ACT) — an Arsenal-issued credential that binds a specific set of scopes to a specific agent DID for a specific TTL.

ACT shape

pub struct Act {
    pub agent_did: String,           // who can use this token
    pub issuer_did: String,          // who issued it
    pub scopes: Vec<String>,         // what it permits
    pub expires_at: u64,             // Unix seconds
    pub nonce: [u8; 16],             // replay protection
    pub signature: [u8; 64],         // Ed25519 over the canonical encoding
}

Verifying an ACT

use forge::auth::verify_act;

let valid = verify_act(&act, &issuer_public_key)?;

verify_act checks:

  • Ed25519 signature is valid against the issuer's public key
  • TTL is in the future
  • Format conforms to the canonical encoding

It does not check that the issuer is authoritative or that the scopes are sufficient for a given operation — those are the caller's responsibility.

Scope grammar

Scopes are dotted strings:

forge.tool.execute.shell
forge.tool.execute.http_get
forge.identity.derive
mars.publish.skill
sigil.tx.sign

A tool requires one or more scopes; the ACT must contain all of them.

Wiring into the tool loop

When an agent invokes a tool, Forge:

  1. Reads the agent's current ACT (set via agent.set_act())
  2. Verifies the ACT via verify_act against the issuer's public key
  3. Checks that every scope the tool requires is present in the ACT
  4. If checks pass, calls the approval handler
  5. If approval grants, executes the tool

A failure at any step yields ToolInvocationStatus::Rejected and the loop continues with the failure recorded.

Issuance

Forge does not issue ACTs — it consumes them. Issuance happens in:

  • Arsenal — the credential proxy and broker (see the Arsenal repo)
  • Aegis — for delegation chains (agent A issues a sub-ACT to agent B for a specific task)

For development, a static ACT can be hardcoded:

let act = Act::dev_unrestricted(&agent.identity().did());
agent.set_act(act);

dev_unrestricted is explicitly insecure — it returns an ACT with all scopes for the current agent's DID. Production code must use Arsenal-issued tokens.

Delegation (no amplification)

When agent A delegates to agent B, the resulting sub-ACT can only contain a subset of A's scopes. This is the no-amplification rule — delegation narrows authority, never widens it. Aegis enforces this at issue time.

Next

  • Identity — DIDs and lineage
  • Tools — registering and executing tools
  • Security model — how identity, capability, and approval interact