Skip to content
Air-Cloud docs

How authorization works

Everything that decides who may do what in your product, on one page: the organization tree, memberships, profiles, grants, groups, denies, field policy, the node each request acts at, and elevation. Each section links to the guide that puts it to work.

The short version: a request acts at one node of your product's organization tree, with the authority its membership holds there, and the platform reads that authority on every request. Your end user's token says only who they are; what they may do comes from the tree, never from the token and never from your code. A change to a member's authority takes effect within seconds, on the requests that follow, not at the next sign-in.

The picture

 /                          the root: your product's base profiles live here (config/profiles.toml)
 └── /org                   Ada's membership is homed here: roles admin, staff, customer
     ├── /org/customers     Bea signed up here as a customer, and owns her own orders
     └── /org/acme          a client node; deny: entity.delete on orders, binding Acme and below
         ├── /org/acme/sales   Carl: membership at /org/acme, grant "editor" here until 31 Jan
         └── /org/acme/ops     a group "night-shift" holds a grant of "viewer" here

 a request ─► acts at ONE node ─► denies there and above ─► roles there ─► owner / shared ─► field policy
              (its membership's      (refuse first,          (home roles      (which records)   (which fields)
               home, or the entry's   whatever grants          + grants at
               declared org_path)     confer)                  or above it)

The pieces

Who decides, in what order

The platform evaluates a request in this order. The first step that refuses ends it; aircloud authz explain prints which step decided and the rule it names.

step what it reads refuses with the rule explain names
the membership the selected membership, against the projection session_membership_required (a session), membership_required (a direct caller) —
the node is active the acting node and every ancestor org_node_archived —
denies every deny at the acting node and above an op-deny refuses the operation; a role-deny removes the role before the next steps deny.op, deny.role
roles the membership's home roles, its grants and its groups' grants at or above the record's node session_role_required (a route's roles), flex.role_required (write_roles) home, grant.membership, grant.group, write_roles
records owner, shared and the tree a record outside the scope is not found owner
fields the [[policy]] rules at the record's node a hidden field is omitted; a non-writable change is flex.forbidden field_policy

When the authority cannot be read in time, the request is refused (session_authz_unavailable on a session), never treated as "no restriction".

Asking why

aircloud authz explain --sub 2891… --plane end_user --node /org/acme --action entity.delete --entity orders
aircloud authz who --node /org/acme
aircloud members review --window 720h

authz check answers the decision and exits 1 unless it is an allow; authz explain prints the whole answer: the membership, its roles and scopes at the node, the step that decided and the record layer's detail. authz who lists every membership whose authority reaches a node, with its roles there. Each is answered by the same evaluator enforcement uses, over the nodes at or below yours. Your coding agent asks the same question through the dev MCP server's authz_check (The dev MCP server).