Authorisation Warrants¶
A warrant is the authority an agent carries with it. Until now the Authorisation Layer decided every action by looking up what the agent may do in its own records. A warrant turns that authority into a short-lived signed object the agent holds: what it may do, within which bounds, on whose Charter, until when, and through which delegation chain. Any tool, MCP server or self-hosted relay can check it offline with Xybern's published public key, so enforcement no longer needs a round trip to Xybern, and a delegate can never hold more than the agent that delegated to it.
Warrants do not replace the Charter. Every action still passes the rules. A warrant is an envelope: it says what an agent is allowed to attempt, the Charter still decides each attempt.
In one minute¶
from xybern import Xybern
from xybern.warrants import WarrantHolder
client = Xybern(api_key="xb_live_...")
holder = WarrantHolder(client, agent_id="agt_payments") # re-mints at 80 percent of the TTL
decision = client.intercept("send_payment", "Refund 120 to #4411",
agent_id="agt_payments", metadata={"amount": 120},
warrant=holder.token())
Or by hand:
w = client.issue_warrant("agt_payments", ttl_seconds=900,
narrow={"action_families": ["send_*", "read_*"],
"argument_bounds": {"send_payment": {"amount": {"max": 500}}}})
token = w["token"] # "xwt1.…", pass as warrant= or the Xybern-Warrant header
The default TTL is 15 minutes (XYBERN_WARRANT_TTL_SECONDS on the issuer). A warrant is always bound to one agent, and to the agent's credential when it has one, so it cannot be lent.
What goes into a warrant¶
The issuer derives the authority from the objects in force at that moment:
| Source | Contributes |
|---|---|
| Charter version | charter_hash, so the receipt can say which Charter the warrant was issued under |
| Access profile (active) | action_families from the allowed and held actions |
| Intent contract (active) | action families and per-action amount caps, the contract's action budget |
| Runtime session | session_id binding and the remaining action budget |
| Credential | holder fingerprint and scopes, expiry floor |
A narrow you pass |
Any further narrowing. Narrowing is an intersection: you can only ask for less |
With no profile or contract the families are ["*"] and a caveat says standing authority applies. That is honest: the agent has no declared box, so the warrant covers whatever the Charter allows.
Enforcement¶
At the choke point (POST /v1/enforce/intercept, the MCP gateway, the relay):
- a presented warrant is always checked. If it does not hold for this agent, action, bounds, session or window, the action is refused with
decision_path: "warrant"before any rule is evaluated. A claimed authority that does not hold is worse than none. - an agent that requires warrants is refused without one. Set it per agent in the dashboard (Warrants view, "Who must carry a warrant") or
PUT /v1/enforce/agents/<id>with{"delegation_policy": {"warrant_required": true}}. The workspace default issettings.features.warrants_required; agents with no explicit setting follow it. - an MCP server can require warrants for every
tools/call(warrant_requiredon the server, headerXybern-Warrant). - a valid warrant is recorded on the decision (
warrant), in the lineage and receipt (authorisation.warrant), and sealed in the Vault entry. The plain-language lineage says "under warrant wrt_… (delegation depth n)". - the issuer counts uses against
budgets.max_actions.
Delegation attenuates¶
When agent A delegates to agent B (POST /v1/enforce/delegate) and the grant is active, the issuer mints B a child warrant narrowed from A's own warrant (minted first if A holds none): the grant's action types and scopes, max_amount as an argument bound, max_uses as the action budget, expiry no later than the grant's. The response carries it as warrant_token. The child records its parent's body hash in chain and its depth; the issuer refuses to sign any child that would widen the parent. Revoking the grant, the parent warrant, the session, the credential or the agent revokes the child.
Revocation¶
POST /v1/enforce/warrants/<id>/revokerevokes a warrant and its descendants.- Killing a session, revoking a credential, revoking a grant, and Revoke everywhere on an agent all revoke the warrants they cover, and the revocation certificate lists them.
GET /v1/enforce/warrants/revocations?since=<seq>is a signed, incremental list a relay or long-lived tool pulls. Every revocation is sealed to the Vault.
Checking a warrant without Xybern¶
from xybern.warrants import verify_offline, covers_action
keys = client.warrant_keys()["keys"] # or GET /api/sentinel/vault/keys, cache it
res = verify_offline(token, keys) # signature, format, window
ok = res["valid"] and covers_action(res["body"], "send_payment", {"amount": 120})
python tools/xybern-verify/verify.py --warrant "$TOKEN" --keys keys.json --action send_payment --metadata '{"amount": 120}'
Offline checks cover everything except revocation and budget, which are issuer state; ask POST /v1/enforce/warrants/verify or use the revocation list for those. The format is specified in Authorisation Warrant v1.
Self-hosted relay¶
The relay checks warrants on your own host: it pulls the issuer's keys, the signed revocation list and the list of agents that require warrants (XYBERN_WARRANT_REFRESH, default 30 seconds), and refuses warrant-bearing actions if the revocation list is older than XYBERN_RELAY_REVOCATION_GRACE_SECONDS (default 60) unless XYBERN_FAIL_OPEN=1. Relay MCP servers accept "warrant_required": true in XYBERN_MCP_SERVERS. GET /v1/status on the relay reports the warrant cache state.
API¶
| Method | Path | Purpose |
|---|---|---|
| POST | /v1/enforce/warrants |
Issue: {agent_id, session_id?, contract_id?, ttl_seconds?, narrow?, parent_warrant_id?} |
| GET | /v1/enforce/warrants |
List (agent_id, status, grant_id, session_id) |
| GET | /v1/enforce/warrants/<id> |
One warrant, with its token while active |
| POST | /v1/enforce/warrants/<id>/revoke |
Revoke (cascade by default) |
| POST | /v1/enforce/warrants/verify |
Check a token against issuer state, optionally for an action |
| GET | /v1/enforce/warrants/keys |
Published signing keys |
| GET | /v1/enforce/warrants/revocations |
Signed incremental revocation list |
SDK 2.5.0: issue_warrant, list_warrants, get_warrant, revoke_warrant, verify_warrant, warrant_keys, warrant_revocations, intercept(warrant=...), and xybern.warrants.WarrantHolder, verify_offline, covers_action.