The governance model
Roles, policies and conditions decide what any caller may do; approvals put a person in front of the steps you choose.
Everything Authoric does about access reduces to one sentence:
A principal holds roles. Roles carry policies. A policy grants actions on resources, optionally under conditions. Every request is checked against the policies of the principal making it, at the moment it happens.
There is no second mechanism and no bypass for internal callers. Your app's users, its API keys, its scheduled jobs and its AI agents are all governed by that sentence, and every decision lands in the same audit log. So does every change to it: creating, renaming or deleting a role or a policy, attaching a policy to a role, giving a role to a principal, and adding or changing a custom action, each with who made it and when.
Policies
A policy grants actions on resources. Policy keys read as resource:action:
| Key | Grants |
|---|---|
notes:read_list | List rows in notes |
notes:read | Read one row of notes by uid |
notes:create | Insert into notes |
orders:delete | Delete from orders |
sdk:query | Run raw SQL from inside a handler |
Every table you create arrives with the same five actions (create, read, read_list,
update, delete), and none of them is reachable until a policy grants it. Custom actions and
handler SDK calls have keys of their own.
A grant can carry conditions: only rows the caller owns, only an amount up to a ceiling, only certain values of an argument. "May refund" is a permission; "may refund up to $5,000" is a policy. The condition is evaluated by the server on every call, so no instruction, prompt or request body can talk its way past it.
Roles
A role is a named bundle of policies: support, billing-admin, read-only. You bind policies to
roles and roles to principals. Principals never hold policies directly, which keeps "what can this
caller do" answerable by looking at one place; the console shows each principal's Effective
permissions.
Roles belong to an app. A role in one app has no meaning in another. Every app has built-in roles, including User, which every user of the app holds and which grants nothing on your tables until you assign a policy to it.
Checking happens per request
Authorization is resolved for each request from that request's own principal. It is not computed once at sign-in and carried in the token, and it is not held in shared state between requests. That is why a token can never widen its own reach: what a caller may do comes from its roles at the moment of the call, not from anything it sends. Revoke a policy and the next request is refused.
Approvals
Some steps deserve a person's decision even when the caller is allowed to take them. Two kinds of work can pause for one:
- An agent's step. Every operation has a risk level: reads are
low, creates and updates aremoderate, deletes arehigh, and you can override any of them per table or action. A step abovelowpauses unless the agent is set to auto-approve actions. - A handler. Your own code can stop and ask, with
sdk.awaitApproval, before it does something consequential.
The step is reached
The agent has decided to issue a refund, delete a record or publish a handler; or a handler has reached its approval point.
The work pauses
Nothing is executed. The run reports pending_approval, and the intended action, with its exact
arguments, is recorded.
A person decides
The pending step appears in the console under Approvals (or App approvals in the app), with what it does, what the run was for, and what has already happened in it. The caller that raised a request can never approve it.
The run continues
On approval, the step executes and the run carries on from where it stopped. On rejection, nothing executes; the agent is told no, with your reason if you gave one, and continues without that step.
An API key can tighten this, forcing a person's approval even for an agent set to
auto-approve, but nothing a caller sends can loosen it. A pause is a normal outcome, not an error:
treat pending_approval as a state to wait on.
Where an agent's abilities come from
An agent has no tool list of its own. Its tools are derived from the policies on its roles.
Grant refunds:create and creating a refund appears among its tools; revoke it and the tool is
gone, for its runs, for direct tool calls and for any MCP client connected to it at once, because
all three read the same derivation.
So governing an agent is the same work as governing a user, not a parallel system that has to be kept in step. And because the limits are policy, an agent cannot exceed its grant no matter what its instructions or its input say.