An Agent Capability Token is how an agent proves it may act: a short-lived, Ed25519-signed credential issued by a broker (Arsenal) after verification, scoped to minimum permissions, bound to the agent’s key. Most services never mint one — they verify one. This page covers the whole loop.

The wire format

One canonical format, owned by the agent-capability-token crate and documented in act/SPECIFICATION.md. A CBOR envelope:
  • v — format version (1). A verifier reports an unimplemented version distinctly from a signature failure — a future v=2 token with a valid signature is refused as unimplemented, never as forged.
  • alg"Ed25519". Only Ed25519 is implemented.
  • claims — the CBOR claim set, as a byte string.
  • sig — 64-byte Ed25519 signature over the claims byte string.
  • kid — optional key identifier (audit/rotation hint, never authoritative for key selection).
The claim set is deliberately small; issuer policy the verifier doesn’t evaluate (rate limits, spend budgets, device bindings) rides in ext, signature-covered and passed through intact.

The claims

The scope grammar is exactly three segments: service:resource:action. Wildcards expand in a grant and are literal in a request — a holder of tools:calendar:invoke can never widen it to tools:calendar:*.

Verification, normatively

The normative procedure (spec section 4.1), in order:
  1. Reject input over 16 KiB.
  2. Decode the envelope; reject unimplemented v or alg.
  3. Reject an absent or non-64-byte signature.
  4. Verify the signature before any claim check. Until this completes, the claim set is attacker-controlled — an exp checked first reports a forged token as merely expired and leaks unauthenticated claims into logs.
  5. Decode and structurally validate the claim set.
  6. Temporal checks with configured leeway.
  7. Issuer match. 8. Audience membership. 9. Scope coverage (directional).
A verifier with an empty trusted-key set fails at construction, not as a stream of signature failures — a misconfiguration must not disguise itself as an attack.

Verifying in every language

Every language passes the same 13 canonical conformance vectors in CI: valid tokens, wildcard coverage, scope refusal, expiry, not-yet-valid, leeway, audience/issuer refusal, foreign keys, tampering, oversized payloads, multi-audience, and the normative-ordering case — a v=99 envelope with an otherwise-valid signature refused as unimplemented, never as a forgery.

Minting (the broker’s side)

Brokers mint ACTs after verification: identity verified (challenge- response), lineage authority confirmed (anchor answer), policy evaluated, consent recorded (HITL where required). Then the canonical crate signs. The token’s sub is the agent’s OAS DID — the same DID that resolves through OAS. This matters: a verifier can resolve the subject’s document, evaluate its lineage, and enforce its revocation, because the token names a resolvable identity, not an opaque surrogate. TTLs are short (default 300s). Renewal is a fresh mint through the same pipeline, not an extension.

The three failure modes that matter

  1. Divergent implementations. Three handwritten ACT implementations once drifted while each claimed to match the others. The fix was one canonical crate plus conformance vectors. If you are writing an ACT verifier by hand instead of using the canonical surface, you are re-creating the bug.
  2. Claims checked before signatures. A verifier that checks expiry first becomes a forgery oracle that also lies in its logs. The order is normative.
  3. Opaque subjects. A sub that isn’t a resolvable DID forces every verifier to trust the issuer’s say-so about identity. OAS DIDs are the whole point.

Next