Skip to content

The authority model

Every decision the Xybern Authorisation Layer makes is now made under one explicit model of authority, the Xybern Authority Ontology (XAO). The model has eight primitives, seven invariants that must hold everywhere, and one document per request, the Authority Slice, that names everything the decision rested on. Nothing about the wire contract changed: decision is still allow, block, escalate or terminate. What changed is that the layer can now say precisely what it decided about, under which authority, and why it holds.

This page is the reference for XAO version 0.3 (Authority Control Plane programme, Phases 1 to 4). The formats are drafts in the open formats bundle: xybern-authority-slice-v1 and xao-invariants-v1.

Eight primitives

Primitive What it names Where it comes from today
Principal Who acts: an agent, a human, the system, an external party; with the principal it acts on behalf of and the root of the delegation chain The registered agent, its roles, the grant chain
Capability What kind of thing can be done: an action type, its family, the tool The action type and the registered tool schemas
Resource What the action touches, as a public class and a hashed instance Tool schemas (tool:<name>), MCP servers (mcp:<server>/<tool>), connectors, flow labels (data:<class>), otherwise the family (family:<family>)
Intent Why: the session, contract, commitment or mission the action serves Sessions, intent contracts, declared intent, missions (Phase 2)
Authority A held permission with its origin, validity, depth and parent Warrants, delegation grants, authority request grants, access profiles, temporal windows, breakglass events, roles, mandates
Constraint A limit that applies regardless of authority held Rules, invariants, budgets, collective limits, obligations
Effect What the action would do, as comparable shapes The argument shape hash, the arguments hash, the resource class
Evidence Proof presented with or produced by the request Warrant proofs, stamps, commitments, attestations, receipts

Authority is an interface, not a table. The warrants, grants, profiles, windows and mandates you already have are read through it; there is no graph database, no migration, and every authority you issued before this release is presented unchanged.

The Resource primitive

Resource is the one new primitive. A warrant, grant or temporal window may now carry resources, a list of patterns over resource classes:

{"action_families": ["payment"], "resources": ["tool:payments", "mcp:ledger/*"]}

Absent or ["*"] covers every resource, so nothing you issued before changes behaviour. A child warrant may keep or tighten its parent's resources, never widen them. One instance can be named as class#instance_hash. Identifying fields (accounts, recipients, paths, repositories) are hashed before they enter the slice, an event row or a stamp: the class is public, the instance never is.

Seven invariants

The eight invariants are defined once, in package/xybern_api/xao/invariants.py, as pure versioned functions. The same functions run at decision time, in Charter lint, and in the conformance suite; the reference checker tools/xybern-verify carries an independent re-implementation and the vectors prove the two agree.

Id Name Statement
INV-001 no widening A derived authority never exceeds the authority it was derived from: action families, scopes, argument bounds, budgets and resources are each at least as tight
INV-002 validity window An authority acts only inside [not_before, expires_at)
INV-003 revocation is final A revoked, expired, closed or inactive authority grants nothing
INV-004 principal intersection A delegate never does what its principal could not, and no party resolves a request it made
INV-005 arguments within bounds The arguments of an action satisfy the bounds of every authority the action relies on
INV-006 resource within authority The resource an action touches is one the relied-upon authority covers
INV-007 accountable issuer Every authority names its issuer, and a Charter in force names its signer
INV-008 mission alignment An action under a mission serves one of its allowed outcomes within its caps and none of its forbidden outcomes or data classes (Phase 2, see Missions)

At decision time the slice records every finding. INV-006 is enforced directly: an action on a resource outside the presented warrant's resources is refused with the rule id invariant:INV-006. The other invariants were already enforced by the checks that existed (warrant validity, revocation, principal intersection, argument bounds); the slice now names which one held. Charter lint reports invariants with how many pairs it checked (active child warrants and grants against their parents for INV-001, active mandates for INV-007) and how many violations it found.

GET /v1/enforce/authority/invariants returns the catalogue with versions.

The Authority Slice

The slice is resolved at the start of every intercept, before any check, and attached to the decision (authority_slice in the response and in the decision's lineage), to the receipt (authorisation.authority_slice, with authorisation.decision_outcome and authorisation.modification), to the stamp (slice, the first twelve characters of the slice hash) and to every pre-flight step (results[].authority_slice). It is a description, not a decision: the existing checks still decide.

{
  "format": "xybern-authority-slice-v1", "xao_version": "0.1",
  "principal": {"kind": "agent", "id": "payer", "on_behalf_of": "planner", "root_principal": "planner"},
  "capability": {"action_type": "payment", "family": "payment", "tool": "payments"},
  "resource": {"class": "tool:payments", "instance_hash": "3f9a…", "source": "tool_schema"},
  "intent": {"session_id": "ses_…", "contract_id": "ctr_…", "commitment_id": null, "declared": false},
  "authorities": [{"kind": "warrant", "id": "wrt_…", "issuer": "owner@bank.example", "expires_at": "…", "depth": 1, "parent_id": "wrt_…", "status": "active"}, …],
  "constraints": 4,
  "context": {"charter_hash": "…", "charter_signed_by": "owner@bank.example", "hour_bucket": 9, "weekday": 3},
  "effect": {"argument_shape_hash": "…", "arguments_hash": "…", "resource_class": "tool:payments"},
  "evidence": [{"kind": "warrant_proof", "id": "wrt_…", "valid": true}],
  "invariants": {"checked": 14, "violations": [], "held": ["INV-002", "INV-003", "INV-005", "INV-006", "INV-007"]},
  "slice_hash": "…"
}

The stored form carries ids and hashes only. The full form, with each authority's grants and the constraints, is returned by POST /v1/enforce/authority/slice ({agent_id, action_type, metadata?}), which resolves without deciding, and by the SDK's client.authority_slice(...).

Decision outcomes

A rule used to have two ways to speak: refuse, or hold for a person. It now has four more. The wire decision keeps its four values; outcome names what happened and modification says what changed.

Outcome Wire decision What the client must do
restrict allow Execute with the named argument reduced to modification.to; the decision covers only the reduced request
rewrite allow Execute modification.action_type.to instead of the original capability
delay escalate Poll the escalation as usual; it releases itself at modification.release_at unless a person refuses first
isolate terminate (or block with no session) Stop; the session was quarantined as the decision and its warrants fell with it

The existing outcomes are named too: allowed, allowed_with_warning, allow_with_obligations, held_for_co_authorisation, escalated, refused, terminated.

A rule opts in with on_match in its conditions:

{"operator": "AND", "rules": [{"field": "amount", "operator": ">", "value": 100}],
 "on_match": {"outcome": "restrict", "field": "amount", "to": "authority"}}

"to": "authority" reduces to the tightest max the agent's held authorities place on that field; a number reduces to that number. Other forms: {"outcome": "rewrite", "action_type": "draft_email"}, {"outcome": "delay", "seconds": 900} or {"outcome": "delay", "until_hour": 9}, {"outcome": "isolate"}. Keep the rule's own decision: it applies whenever the modification cannot (no authority bounds the field, the value is already inside it, there is no session to isolate). A plain refusal or hold from any other triggered rule always stands; an outcome never softens a rule that was written to refuse.

The mandate compiler passes on_match through when an outcome says what should happen instead of a refusal ("payments above the limit are reduced to the limit", "external emails become drafts", "large exports wait until nine").

In the SDK:

d = client.authorize("payment", context={"amount": 900, "to": "acc-1"})
if d.modified:
    req = d.modified_request(metadata={"amount": 900, "to": "acc-1"})   # {"action_type": "payment", "metadata": {"amount": 100, "to": "acc-1"}}
if d.delayed:
    print(d.release_at)
print(d.outcome, d.authority_slice["resource"]["class"])

Authority telemetry

Every decision writes one row to authority_events: principal kind and root principal, agent, department, capability and family, resource class and instance hash, argument shape hash, decision, outcome, the rule types and invariant codes that spoke, delegation depth, trust state, hour bucket and weekday, the slice hash and the latency. No argument values and no content. It is the foundation for Authority Memory and the Threat Genome in later phases.

  • GET /v1/enforce/authority-events?since=&until=&limit=&cursor= exports rows in order with a cursor.
  • GET /v1/enforce/authority-events/summary?days=30 returns counts by outcome, top capabilities and resource classes, and the hour profile.
  • Rows are purged after XYBERN_AUTHORITY_EVENTS_DAYS (default 400) by a daily job.
  • Set XYBERN_SIEM_AUTHORITY_EVENTS=1 to emit each row to your SIEM as authority.event.

Where it appears

In the dashboard, the decision record shows the resource class, the kinds of authority the action ran under, the invariant findings and the outcome with its modification. The navigation gained the groups that later phases fill: Build (Authority Builder, the authority language), Simulate (Simulation, reachability, blast radius, attack my system), Missions under Authority, Passports under Agents, and the Authority Graph also under Oversee.

Simulate

Reachability, blast radius, the Attack System and the Expected Effect of every action are described in Simulate.

Trust states

The trust state of every principal (trusted, observed, restricted, quarantined), its signals, transitions and narrowing are described in Trust states and incident narratives.

The Authority model view

Under Oversee, Authority model is the map of your workspace: everything in it is one of the eight things, and the page lets you ask about any of them and open any of them.

Three questions, answered as the eight rows. At the top of the page:

Question What you give What comes back
What can this agent do? an agent the authority it holds (its own, the Charter's mandates, what others lent it), the action types it can perform, directly and through other agents, the resource classes it reaches, its missions and sessions, the rules and budgets that bound it, what it did in the last 30 days, and what proves it
Who can do this? an action type, or a resource class the agents that can, directly or through others, under what authority, the missions and rules that speak about the action, its decisions in the last 30 days and its receipts
Why was this decision made? a recent decision what each of the eight was for it, from the Authority Slice sealed with the decision, nothing recomputed

An agent that acts only with what it is lent reads as such: its capability is what is lent, and nothing else, and it is not among those who can do an action nobody lends it. Every answer names the real things: each chip opens the record behind it.

What the model says is missing. Below the questions, findings derived from the relations between the eight, on the workspace's own data over the last 30 days, each a plain sentence with the place that fixes it: an agent that acts only with what it is lent and is lent nothing (everything it does will be refused); an agent that acted under standing authority, with nothing declared narrowing it (a warning when the Charter is empty); an agent that is restricted or quarantined; an agent that acted without a passport; an agent that acts under no declared mission; an action performed that no live rule speaks about; a resource class touched that no rule bounds; decisions not sealed in the Provenance Vault; a mandate that has never matched since it was signed; lent authority used up or ending within a day. Warnings come first. Clicking a primitive shows the findings that concern it beside its instances. Over HTTP: GET /api/sentinel/enforcement/authority/model/findings.

From anywhere to the model. A decision record, a held action in Escalations and every agent in Passports carry a button that opens the Authority model on the fitting question: Through the ontology for a decision, What can this agent do? for an agent.

The things of each primitive. Clicking a card lists what this workspace holds of it, grouped by kind and newest first: agents and roles; Charter mandates, lent authority, warrants, authority requests, access profiles, time windows and open break glass; the action types seen and declared; the resource classes touched and declared; missions and sessions; rules and the budgets on lent authority; decisions; sealed receipts, stamps, execution attestations, co-authorisations and passports. Every row opens in place with its facts in plain words, and from there you can ask the question that fits it or go to the page that manages it.

Walking the relations. Every record opened on the page shows the relations it takes part in, each in the words of the ontology and each listing the real things at the other end: an agent holds authorities, can action types, acts over resource classes, lends authority, may instruct agents, declares missions and sessions, is bounded by rules, produced decisions and is proved by receipts, stamps, attestations and its passport; lent authority is held by and issued by agents, grants action types and produced the decisions taken under it; a mandate is compiled into rules and produced the decisions it caused; an action type is performed by agents, granted by authorities and bounded by rules; a decision was performed by an agent, under authorities, is an instance of an action type, serves a mission, is bounded by the rules that spoke and proved by its receipt, and sits in the same chain as the other decisions of its request. Clicking a thing at the other end walks to it; a trail at the top of the record, in the colours of the eight, shows the walk and takes you back; the card of the primitive you are on is selected on the diagram behind. Over HTTP: GET /api/sentinel/enforcement/authority/model/node/<kind>/<id>.

The model in time. Pick a moment (24 hours, 7, 30 or 90 days back, or a date): the eight as they stood then, counted against now, and what changed since, dated and in words: agents registered, trust states changed, authority lent, taken back or ended, warrants issued or revoked, mandates signed into or leaving the Charter, missions given or ended, passports issued or revoked, the decisions since then by outcome, and the findings that opened or closed. It is read from the dated rows and the Charter versions; a status change that carries no date is read as it is now, and the page says so. Over HTTP: GET /api/sentinel/enforcement/authority/model/snapshot?at= and GET .../changes?since=&until=, and .../findings?as_of=.

Before you sign. Propose a change and see what it changes in the model before anything exists: a mandate about an action (held, refused or allowed, for one agent or every agent), lending authority from one agent to another, taking a lending back, a trust state, or an agent acting only with what it is lent. The answer is the findings the proposal would close, open or change, and a sentence for each principal it touches, with a link to the page where it is signed. Nothing is written. Over HTTP: POST /api/sentinel/enforcement/authority/model/simulate with {mandate: {action_types, decision, agent_id?}}, {lend: {from, to, actions}}, {revoke_grant}, {trust: {agent_id: state}} or {lent_only: {agent_id: bool}}.

The whole workspace as one signed document. The Authority Bundle button (workspace admins) downloads the eight primitives, the findings, the invariants and the public keys as one document signed with the Vault signing key, format xybern-authority-bundle-v1, for your own systems to hold and to check offline. Check a bundle file under For engineers verifies one.

The page is read-only. Its diagram, the sentence, the invariants with their lint status and violations, and the outcomes of the last 30 days are as before. The open formats, the retention schedule and the raw Authority Slice resolver sit under For engineers.

Over HTTP for the dashboard session: GET /api/sentinel/enforcement/authority/model/instances?primitive= (one of the eight, limit, offset), GET /api/sentinel/enforcement/authority/model/record/<kind>/<id>, POST /api/sentinel/enforcement/authority/model/ask with {question: "agent", agent_id}, {question: "who", action_type | resource_class} or {question: "decision", decision_id}. See Passports, Time Travel, Constraint Observance, retention for Phase 3.

Specifications