Protected boundary — what is never grantable
RiskLevel ≠ protection
Section titled “RiskLevel ≠ protection”A high RiskLevel (even Sovereign) still describes something grantable — it just needs the highest approval. The things that must never be touched are a different category entirely:
They are protected by mechanism, not by permission — so they are never in the grantable Library at all.
“Edit a past record,” “extract a signing key,” “approve more than I hold,” “invent a new Axowl permission” — none of these are actions you approve at some tier. They are structurally impossible or reserved.
The five protections
Section titled “The five protections”1. The sealed chain — the past cannot be rewritten
Section titled “1. The sealed chain — the past cannot be rewritten”Every sealed record is an append-only, hash-chained entry. Altering a past record breaks the hash, and the integrity check detects the mismatch and raises a tamper alert. Deleting audit history isn’t forbidden by a permission — it is structurally detectable: you can edit the row, but you cannot hide that you did.
2. Keys — derived in silicon, never extractable
Section titled “2. Keys — derived in silicon, never extractable”Signing and seal keys derive inside the TPM / PUF and are never serialized out — not by an admin, not by Axowl staff. “Extract a key” is not a grantable action; it is physically unavailable.
3. The managed vocabulary is closed
Section titled “3. The managed vocabulary is closed”An organization can add scopes in its own namespace (Custom actions), but it cannot invent or redefine Axowl’s:
- A scope that starts with
org,vieworplatformmust already exist in the Library. Adding anything else in those namespaces to a role is refused with400anderrorCode: "SCOPE_NOT_IN_LIBRARY". - Scopes under
user(what a person owns: their credentials, profile, linked accounts) andplatform(Axowl’s own operations) are never offered for an organization to grant.
4. The no-escalation ceiling — you cannot approve more than you hold
Section titled “4. The no-escalation ceiling — you cannot approve more than you hold”An approver can only approve a request their own grant covers. The subsumption rule compares the approver’s scope with the requested one — path, read/write tier and every constraint — and refuses anything wider, so “give me everything” is rejected by arithmetic, not by someone’s judgment.
- The check runs when the approval line is built and again at the moment of approval, so an approver who lost the scope in between can no longer approve.
- A limit on the approver’s grant has to be carried by the request. A request that drops a key the approver is bounded on, turns a ceiling into an open-ended range (
amount>=10000againstamount<=50000), or leaves out the approver’s deny (region!=KR) is refused. - Time is part of the ceiling: an approver whose own grant expires cannot approve a grant that is permanent or outlives theirs. A grant that is itself limited to a finite number of uses does not make its holder an approver at all.
- Page scopes and resource scopes never cover each other: holding
org.*does not let anyone approve aview.*scope. - A request raised by an agent is never completed without a human approver, so an agent cannot grant itself anything.
- Signed permits follow the same principle:
POST /api/org/{slug}/permits/issueissues a permit only for a scope the caller holds at that moment; otherwise it returns403withdecision: "Deny".
4b. Separation of duties — one person cannot hold both halves
Section titled “4b. Separation of duties — one person cannot hold both halves”The ceiling above stops you from getting more authority than the approver has. It says nothing about a combination that is individually permitted and jointly dangerous — granting permissions or assigning roles, and deleting the audit record of it.
That is checked separately, on the resulting scope set. When a member receives new scopes, they are combined with what the member already holds; if the new scopes complete a conflicting pair, the grant is refused. Pairs are defined on scopes rather than roles, deliberately — a role-pair rule is bypassed by splitting the two halves across two roles, and that split is the whole failure mode.
- Enforced on both grant paths: role assignment and approval.
- A refusal is recorded as
auth.sod.assignment_blocked. - A single grant that carries both halves at once is an explicit design choice rather than a hidden split: it goes through and is recorded as
auth.sod.conflict_detected. GET /api/org/{slug}/roles/sod-reportlists the conflicting pairs members already hold. It needsorg.role.readand records nothing.- Owners and Admins are exempt — a full-authority principal trips every pair by construction, and refusing them would leave an organization unable to repair itself.
4c. Money is carved out of “full access”
Section titled “4c. Money is carved out of “full access””Administrative authority and financial authority are different things, and the second is not a subset of the first. An admin with organization-wide access still cannot change what the organization pays or what it charges: the billing scopes — view.billing and org.org.billing_manage — are excluded from the Admin’s implicit access rather than merely denied in a preset, so a full-access principal does not acquire them by being full-access.
The Owner passes every gate. An Admin passes every gate except those. An admin who is explicitly granted a billing scope passes; the carve-out blocks the implicit route only, so an organization can still delegate deliberately. Putting one of these scopes on a role — directly, or through a wildcard that would include it, such as org.* or view.* — requires holding it yourself; otherwise the call returns 403 PERMISSION_DENIED.
The approval route is closed the same way. A request for a billing scope, or for a wildcard that would include one, can be approved only by the Owner or by an approver who holds that billing scope explicitly; an Admin’s organization-wide access does not make them an approver for it. Roost access — view.roost and the Roost management scopes — is carved out by the same rule.
This covers both directions of money: plan and add-on purchases (what you pay), and the per-app-group collection switch (what you charge your own end users). Turning collection on or off goes through the same gate — stopping revenue is a financial decision too. The UI hides the control for anyone who lacks the scope, but that is a courtesy: the server refuses the call directly with 403 PERMISSION_DENIED, so bypassing the interface changes nothing.
5. Kill switch
Section titled “5. Kill switch”A revoked token or a revoked principal is blocked on every request, ahead of every permission check. The call returns 401 (Token has been revoked. or Account access has been revoked.), and a revoked principal cannot obtain a signed permit either.
The check fails closed: if the revocation store cannot be reached, authenticated requests are refused with 503 and error: "REVOCATION_STORE_UNAVAILABLE" instead of being let through.
Grantable vs protected — the distinction
Section titled “Grantable vs protected — the distinction”| Grantable (in the Library) | Protected (never in it) | |
|---|---|---|
| Examples | invite a member · assign a role · rotate the signing key | edit a past record · extract a key · add a new org.* scope · reach billing as an admin |
| Controlled by | RiskLevel → approval routing | mechanism (chain · silicon · closed vocabulary · ceiling) |
| Highest control | Sovereign = the Owner approves | not approvable — structurally closed |
Related: Action Library · Event & system control · Capability grants · Seals & integrity