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.
Arsenal is the capability layer in the L1fe ecosystem. OAS identifies the agent, AEGIS governs the root behind that identity, and Arsenal decides what that identity may do right now. Forge consumes Arsenal ACTs in the runtime hot path.
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:
- Reads the agent's current ACT (set via
agent.set_act()) - Verifies the ACT via
verify_actagainst the issuer's public key - Checks that every scope the tool requires is present in the ACT
- If checks pass, calls the approval handler
- 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 Forge Arsenal page)
- 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
- Ecosystem map — how Arsenal relates to OAS, AEGIS, ANVIL, Aut0, Sigil, and Foundry