permissions row matching the requested capability — either on
the item itself or on the profile that holds it.
The data model is one row per (agent, target, capability) triple. UI
and API surfaces let you grant multiple capabilities to one
(agent, target) pair atomically — the router writes one row per
capability inside a single transaction — but every row remains
independently revokable, with its own expiresAt, createdAt, and
grantedBy.
The two capabilities
Legacy capability names continue to work as aliases and are stored as
canonical on write:
The two grant targets
A grant targets either a single item or an entire profile.
Profile-target grants are powerful — they cover items that don’t yet
exist. Use them when you want a stable contract with an agent
(“Acme Robotics gets
use on the acme-corp profile”) and you don’t
want to add a grant every time you rotate a credential.
Runtime constraints
Some(agent locality, storage mode, capability) tuples are unreachable
at runtime. The server pre-checks and rejects them at grant-create time
with INVALID_CAPABILITY_LOCALITY or INVALID_CAPABILITY_STORAGE:
Profile-target grants pre-check every storage mode present in the
profile.
Permission shape
Composite unique index ensures one permission per
(agentId, itemId | profileId, capability) triple.
Decision flow
Every access request follows this exact order:1
Authenticate
Resolve the
abs_... agent session token. access.* accepts no other credential.2
Resolve item
The item must belong to the same organization as the agent.
3
Check permission
A row matching the canonical capability must exist either on the
item itself or on its profile.
4
Check expiry
expiresAt must be null or in the future.5
Enforce matrix
Locality + storage mode must match the capability.
6
Audit
Append the attempt — allowed or denied — before returning a response.
7
Decrypt / reserve
Only if every preceding check passed.