Access control overview
How MagOneAI decides whether someone may do something: actions, grants, roles, and groups
Overview
MagOneAI answers one question in one place: may this person perform this action on this object?
Everything in this section is how you shape that answer. The model has four pieces, and they build on each other:
An action is one thing a person can do
usecase.run, kb.view, secret.manage.org. There are 128 of them, and the full list is in the action reference.
Actions are fixed. You do not create them.
A grant says someone may perform an action, on something
A grant is three parts: the action, the level it attaches to, and the target at that level. For example usecase.run at project level on project A.
A target of * means every object at that level.
A role is a named bundle of grants
You assign a role to a person, and they get its grants. MagOneAI ships eight seeded roles, and you can author your own. See Roles.
A group is a named population that carries a role
You put people in a group, and the group's role applies to all of them. A group holds no permissions of its own. See Groups.
How this relates to the role tiers
The four project tiers, Admin, Builder, Operator and Viewer, still exist. Each is a seeded role with the same name and the same reach, so nothing you had configured stopped working. See Role tiers for how each one maps onto this model.
What the tiers cannot express is anything narrower than a tier. Give this person the ability to run this one use case, and nothing else is not one of the four shapes. An org admin can grant exactly that from the Access screen in Studio.
How a decision is made
For each request, MagOneAI resolves the person's effective permissions and tests whether any grant answers the question being asked.
A platform superadmin is always allowed
This is a break-glass path, resolved from the database rather than trusted from a token. It exists so a lockout is always recoverable.
MagOneAI works out which organizations to decide in
The named organization if the request has one, otherwise every organization the person belongs to.
It gathers the person's grants in that organization
Four sources, combined: the undeletable Default role everyone holds, roles assigned to them directly, roles carried by groups they belong to, and per-person overrides.
Denies win
A deny always beats an allow, wherever it came from. This is what makes "everyone in Support except one person" expressible.
Someone holding no role at all is allowed through
This is a deliberate fail-open for a seeding gap, and it is logged loudly.
Holding no role is a configuration problem, so it fails open. Holding a role that grants nothing is a policy decision, so it denies. If you empty the Default role, everyone still holding it is denied, not let through.
Grants: level and target
A grant attaches at a level. Levels exist because not every action is about the same kind of thing: org.enter has no project, and a platform setting has neither.
| Level | A grant at this level is about |
|---|---|
self | The person's own data, such as their own profile or their own runs |
usecase | One use case |
project | One project |
app | One app surface: Studio, Hub, Superadmin, or a custom app |
org | One organization |
platform | The whole deployment |
The action reference lists the levels each action can be granted at. Granting an action at a level it does not evaluate is refused.
Wildcard and explicit targets mean different things
This is the one rule in the model that surprises people, and it is deliberate.
| Grant | Means |
|---|---|
usecase.run at project level, target * | On the projects you can already reach, you may run use cases |
usecase.run at project level, target project A | You may reach project A, and run use cases there |
A wildcard narrows what you may do where you already are. An explicitly targeted grant establishes access to that one target.
Without that split, holding the seeded Project Operator role would give you execute access on every project in the organization, because its grants are wildcards. The wildcard is what makes a seeded role reusable; the explicit target is what makes a one-off grant possible.
You cannot grant what you do not hold
Anyone who can author roles could otherwise write themselves a role containing every action and assign it to themselves. MagOneAI therefore applies a ceiling: you may only add an action to a role if you hold that action yourself.
Three things the ceiling deliberately does not do:
The role editor is where you go to fix a lockout. A ceiling that applied to break-glass would make the product unrecoverable.
Denying an action you do not hold removes access. It cannot add any. Applying the ceiling to denies would break the "everyone except one person" pattern.
Seeded roles contain more than most operators hold, and saving a role replaces its whole permission set. Checking the whole submitted set would make seeded roles uneditable by anyone but a superadmin.
Only genuinely new additions are checked.
The ceiling is per action, not per target. If you hold an action, you may delegate it at any level you can already reach.
Enforcement modes
Each action has an enforcement mode per organization, which a platform administrator sets in the superadmin portal.
| Mode | Behaviour |
|---|---|
| Off | No decision is made. The action is not governed. |
| Shadow | A decision is made and a denial is recorded, but the request still goes through. |
| Enforce | A denial blocks the request. |
Shadow mode is how a deployment moves to this model safely: you watch what would have been denied, fix the configuration that turns out to be wrong, and only then enforce.
126 of the 128 actions ship in shadow mode. The two exceptions, which enforce from the start, are app.open (opening Studio, Hub or the Admin Portal) and usecase.run (running a use case). Those two are the ones worth enforcing immediately, because they are the outermost doors.
So if you are reading audit entries that show denials against requests that plainly succeeded, that is shadow mode working as intended, not a bug. See Audit logging.
Two layers, not one
Ordinary actions are answered by the model described above. One action is different: app.open, which decides whether a person may open an app surface at all.
That one is answered by a policy engine over apps and entitlements, because it has a shape the grant model does not: a surface can be marked as requestable, so a person without access opens a request instead of being refused. See Apps catalog for the apps themselves, and Access requests for the request flow.
| Ordinary actions | app.open | |
|---|---|---|
| Decided by | Roles, groups, grants | Apps and entitlements |
| Absence of a rule means | Depends on the role | Allowed, until the app requires assignment |
| Can be requested | No | Yes, when the surface is flagged for approval |
Both are real, both are enforced, and you configure them in the same place.
Where to configure what
| Surface | Who uses it | Covers |
|---|---|---|
| Access screen in Studio | Org admins | Members, their roles, and the grants those produce. See The Access screen. |
| Access Control page in the Admin Portal | Platform administrators | Roles, groups, apps, the access grid, enforcement modes, and effective permissions |
| Roles page in the Admin Portal | Platform administrators | A read-only matrix of what each seeded role contains |
| Org Groups page in the Admin Portal | Platform administrators | Cross-organization group assignment |
The Access screen is deliberately the narrower one. It shows members, their roles, and the resulting grants, because that is the whole of what an org admin needs.