ADR-0010: Biscuit tokens for capability authorization¶
- Status: Accepted
- Date: 2026-04-12
Context¶
Capability invocations need bearer credentials. Agent contexts add requirements web-auth doesn't have: - Attenuation: a parent agent delegates a narrower capability to a child agent. - Offline verification: agents on the edge verify tokens without network round-trip. - Capability-scoped: tokens carry their own authorization, not a session lookup.
Options:
- JWT (signed). Ubiquitous. No attenuation. Revocation painful. No built-in authorization logic.
- PASETO. Better crypto defaults than JWT. Same limitations otherwise.
- Macaroons. Attenuatable via caveats. Original capability-token design. Active research but small ecosystem.
- Biscuit. Modern macaroon-descendant. Attenuable, offline-verifiable, has a Datalog-based authorization language for expressing caveats. Rust-native library (
biscuit-auth). - GNAP tokens. IETF draft for advanced delegation. Complex, new, poor agent-context tooling.
- OAuth2 access tokens. Session-bound. Not capability-first. Poor fit for agent-to-agent delegation.
Decision¶
Biscuit is the default capability token format.
trails-identityissues and verifies biscuit tokens.- Biscuit root keys per-app, env-configured (
TRAILS_BISCUIT_KEY). - Caveats encode scoping: principal DID, capability ID whitelist, expiration, audience, resource restrictions.
- Attenuation supported: a handler can issue an attenuated biscuit to a downstream agent.
- Offline verification is default; no network round-trip per invocation.
JWT / VC-JWT remain supported for interop (e.g., VC-based preconditions) but aren't the primary capability token.
Consequences¶
Positive¶
- Attenuation is a first-class operation. Agent chains work cleanly.
- Offline-verifiable. No central auth service bottleneck.
- Datalog caveats express agent-relevant constraints (time, capability, resource) declaratively.
- Rust-native library (
biscuit-auth) — zero FFI cost in kernel. - Small, audited spec.
Negative¶
- Less known than JWT. Some devs will be unfamiliar. Mitigated by docs and examples; framework hides most details.
- Revocation still requires a central check for time-critical cases. Mitigated by short-lived tokens (default 1h) + refresh flow; revocation list optional for strict scenarios.
- Ecosystem smaller than JWT. Fewer client libraries across languages. Mitigated by the framework issuing tokens on behalf of apps; most app code never touches biscuit internals.
Non-consequences¶
- VCs are orthogonal — they carry claims (not capability authority) and are verified independently.
- DIDs are orthogonal — they identify principals; biscuits authorize actions.
Revisit conditions¶
- If biscuit library maintenance stalls, evaluate porting or switching to SPIFFE SVIDs or raw attenuated JWTs.
- If a dominant agent-token standard emerges (e.g., an IETF work product), adopt it as an alternative and let biscuit remain as a default for new projects.
Update (2026-04-12)¶
Per ADR-0013, the primary capability mandate is now an ACT token (draft-nennemann-act-00). Biscuit is scoped to attenuation / chain-delegation below a parent ACT — short-lived, attenuated session tokens issued by a handler to a downstream agent. The Datalog caveat story and offline-verification properties that motivated the original decision still apply in that narrower role. Apps that don't need chain-delegation can operate with ACT alone.
Verification bounds (security hardening). Biscuit Datalog caveat evaluation MUST be bounded at the kernel verifier to prevent crafted-token CPU exhaustion: maximum 1000 facts materialized, maximum 128 evaluation depth, maximum attenuation-chain length 5, per-verification wall-clock budget 1 ms. Tokens that exceed any bound are rejected at parse/verify time with BiscuitBoundsExceeded; the bound is not softened by policy. These bounds are part of the framework surface — not adapter-configurable — so hostile tokens behave identically across deployments.