Authorisation Warrant, format v1 (final)¶
Status: final, version 1, published with Phase 10 of the Authorisation Layer programme. Test vectors and the conformance suite are in the open formats bundle (spec/xybern-formats-v1/vectors/warrant.json). Feedback: security@xybern.com.
An Authorisation Warrant is a short-lived signed capability that an AI agent carries. It states, in a form any party can check offline with the issuer's published public key, what the agent is authorised to do, on whose authority, within which bounds, and until when. It is issued from the objects in force at the moment of issue (the signed Charter version, the agent's access profile, its intent contract, its runtime session budget, its credential) and it can be attenuated when authority is delegated: a child warrant can only narrow its parent, never widen it.
Token¶
base64url without padding. The prefix xwt1. identifies the format. The token is safe to carry in an HTTP header (Xybern-Warrant), a JSON field (warrant), or an MCP request header.
Body¶
Canonical JSON: keys sorted, no whitespace, UTF-8, no escaping of non-ASCII.
| Field | Type | Meaning |
|---|---|---|
format |
string | "xybern-warrant-v1" |
warrant_id |
string | wrt_ followed by 12 hex characters, unique per issuer |
issuer |
object | {"workspace_id": ..., "key_id": ...}, the issuing workspace and the signing key used |
holder |
object | {"agent_id": ..., "fingerprint": ... or null, "did": ... or null}, the agent the warrant is bound to. fingerprint is the SHA-256 fingerprint of the agent's Ed25519 credential when it has one |
charter_hash |
string or null | The Charter version in force at issue (public sha256 over the canonical mandate list) |
policy_set_hash |
string or null | The compiled rule set hash in force at issue |
authority |
object | See below |
session_id |
string or null | The runtime session the warrant is bound to |
grant_id |
string or null | The delegation grant it was issued through |
not_before |
string | ISO 8601 UTC, seconds precision, Z suffix |
expires_at |
string | ISO 8601 UTC |
depth |
integer | 0 for a root warrant, parent.depth + 1 for a child |
chain |
array of strings | The body hash (sha256 hex over the canonical body) of every ancestor, root first |
caveats |
array of objects | Informational narrowing statements ({"type": "access_profile", ...}, {"type": "delegation", ...}, {"type": "standing_authority", ...}). Not evaluated by checkers |
issued_at |
string | ISO 8601 UTC |
contract_id, root_agent_id |
string or null | The mission (intent contract) the warrant serves and the root of its delegation chain |
constraints |
object, optional (XAO 0.2) | {"decay": {"full_seconds", "held_seconds"} or {"full_uses", "held_uses"}, "lease": {"heartbeat_seconds", "requires_session", "requires_parent"}}. Decay by age is checked offline (past held_seconds the warrant is refused; past full_seconds the issuer holds actions for approval); decay by uses and every lease prerequisite are issuer state. A child inherits and may only tighten them |
authority¶
| Field | Type | Meaning |
|---|---|---|
action_families |
array of strings | Action types the warrant covers. * covers everything; a trailing * is a glob (send_*) |
scopes |
array of strings | Credential scopes in force, informational for checkers |
argument_bounds |
object | {<family>: {<field>: <rule>}}. A rule is any of {"max": n}, {"min": n}, {"in": [...]}, {"not_in": [...]}, optionally "required": false (default true: a bounded field that is absent from the action fails the check) |
budgets |
object | {"max_actions": n}, enforced by the issuer, which counts uses |
resources |
array of strings, optional (XAO 0.1) | Resource class patterns the warrant covers (tool:payments, mcp:github/*, data:PUBLIC, class#instance_hash); absent or * covers every resource |
Signature¶
{"algorithm": "ecdsa-p256-sha256", "key_id": <issuer signing key id>, "value": <base64(DER ECDSA signature)>}
The message signed is the canonical JSON bytes of body. The key is one of the issuer's published signing keys: GET /api/sentinel/vault/keys (the Provenance Vault key page) or GET /v1/enforce/warrants/keys. Keys rotate; retired keys stay published so older warrants still verify within their window.
Checks¶
A checker with the published keys and no network access verifies, in order:
- the token decodes and
formatisxybern-warrant-v1 signature.key_idis a published key and the ECDSA signature over the canonical body verifiesnot_before <= now < expires_atholder.agent_idis the acting agent; when the request carries a verified identity assertion andholder.fingerprintis set, the fingerprints matchauthority.action_familiescovers the action typeauthority.argument_boundsfor the covering family are satisfied by the action's parameters- when
session_idis set, the action runs in that session
Issuer state, checked online or through the issuer's signed revocation list:
- the warrant is not revoked
budgets.max_actionsis not exhausted
Any failure refuses the action. An unparseable, unsigned or unknown-key token is not a warrant.
Attenuation¶
A child warrant issued from a parent must satisfy, at issue time and again on every check of the chain:
- every child action family is covered by a parent family (
*in the parent covers all) - child scopes are a subset of the parent's, when the parent lists any
- for every family and field the parent bounds, the child bounds it at least as tightly (
maxnot larger,minnot smaller,ina subset,not_ina superset) budgets.max_actionsnot larger than the parent's, when the parent sets oneexpires_atnot later than the parent'sdepthequals the parent's plus one, andchainequals the parent's chain plus the parent's body hash
The issuer refuses to sign a child that would widen. Revoking a warrant revokes every descendant.
Revocation list¶
GET /v1/enforce/warrants/revocations?since=<seq>
{"list": {"format": "xybern-warrant-revocations-v1", "workspace_id", "as_of", "since_seq", "latest_seq",
"complete", "revoked": [{"warrant_id", "seq", "revoked_at", "body_hash"}]},
"signature": {"algorithm": "ecdsa-p256-sha256", "key_id", "value"}}
Signed over the canonical list. seq is a strictly increasing per-workspace counter, so a relay keeps latest_seq and pulls incrementally. A relay that cannot refresh the list within its grace window refuses warrant-bearing actions (fail closed) unless configured to fail open.
Reference implementations¶
- Issuer and choke-point check:
package/xybern_api/warrants.py - Python offline checker and self-refreshing holder:
xybern.warrantsin thexybernpackage (pip install xybern[crypto]) - Command line:
tools/xybern-verify/verify.py --warrant <token> --keys keys.json [--action <type>] [--metadata '{...}'] - Self-hosted relay:
xybern_relay/warrant_check.py