RBAC and roles
Oxagen's role-based access control: built-in roles, how the IAM resolver decides a capability call, and where to see who holds what.
Built-in roles
Org-scoped roles
| Role | Capabilities |
|---|---|
| Owner | Everything, including billing, org deletion, and ownership transfer. |
| Admin | All operations except destructive org-level actions (deleting the org and transferring ownership). |
| Compliance | Security and privacy policy management. Cannot reach operational surfaces or billing. |
| Billing | Billing and subscription management only. Cannot reach operational surfaces or IAM. |
Org roles govern the organization-level surfaces: members, access (IAM), security, billing, and developer settings.
Workspace-scoped roles
| Role | Capabilities |
|---|---|
| Owner | Everything in this workspace. |
| Admin | Everything in this workspace, the same as Owner. |
| Member | Run agents, resolve approvals, edit memories, and configure agent triggers. |
| Viewer | Read-only across the workspace. |
Workspace roles are assigned per workspace. A person can be a Member in one workspace and a Viewer in another within the same organization.
Workspace Owner and Admin
A workspace's Owner and Admin can do everything in that workspace. They pass the role check of every capability that acts inside the workspace, whichever roles that capability names, in the app, the API, MCP, and Stella.
No role grant restricts them inside their workspace. A grant with the effect deny or require approval does not apply to them there, even on a role they also hold.
They gain nothing outside it. Billing, organization settings, organization members, API keys, roles, and other workspaces still need an organization role. Two settings a customer chooses still apply to them:
- The approvers a mandate names.
- The
regulatedgovernance mode, where an organization Owner or Admin merges each steering PR.
An agent never gains this access, even when the person who started its run holds the role.
IAM resolution order
When a principal calls a capability, the IAM resolver (packages/oxagen/src/iam/resolve.ts) decides it. This is the path a live request takes today:
What the chart shows and leaves out:
- The resolver runs on every plan. On Enterprise it reads every role a person holds, custom roles included, and every grant. On Free, Build, and Scale it reads only the built-in roles a person holds, with each role's grants taken from the capability itself. Custom roles and the grants a customer writes apply only on Enterprise. Agent principals are decided on every plan.
- An Oxagen API key acts as the person who created it. If the resolver cannot find that person in the org, it denies the call outright rather than falling through to the contract default.
- Across roles, deny wins. A principal in several roles with conflicting grants for one capability gets deny over require-approval over allow. The resolver decides a role grant on its effect alone. It does not evaluate conditions such as time windows or IP ranges.
- No grant restricts an organization Owner. The Owner rule runs first. It allows a person who holds the system-provisioned org Owner role (
isSystemDefault=true), in the organization and in every workspace. A deny or require-approval grant never decides for them. A user-created role named "Owner" gets nothing from this rule. - No grant restricts a workspace Admin in their workspace. The next rule allows a person who holds the system workspace Owner or Admin role on the call's own workspace, when the capability acts inside that workspace. It never decides an organization-level capability, another workspace, or an agent. There, role grants decide as they do for anyone. See Workspace Owner and Admin.
- The last step is per capability. Most contracts declare
defaultEffect: "deny". Some declarealloworrequire_approval. - Direct grants and org policies are not live. The resolver still contains rules for workspace grants, org-enforced policies, and org grants (rules 1–6), but the tables behind them were removed, so they decide nothing on a live request. The one exception is internal. When the IAM tables are missing, or the resolver cannot find an API key's creator, it receives a synthetic org-enforced deny so the call fails closed.
The resolver returns a structured decision log alongside the decision. Oxagen stores it in the trace_jsonb field of the ClickHouse audit_events row. The app does not show ClickHouse rows, so query audit_events directly to read it. See Audit logging.
Capability domains
Capabilities are organized into domains, and role grants are listed by domain:
| Domain | Example capabilities |
|---|---|
agent.* | Define, deploy, and govern agents, record lessons from a run, and resolve approvals |
chat.* | Send messages and manage conversations |
knowledge.* | Manage the graph and sources |
billing.* | Purchase credits and change the subscription |
org.* | Manage members, roles, and org settings |
workspace.* | Create workspaces and manage workspace settings |
plugin.* | Install, enable, and govern plugins |
Agents as principals
Every agent configuration is also a principal with its own grant matrix. The resolver decides an agent's request against two principals at once, the agent's own and the authenticated person who started the run, and the more restrictive answer wins:
The human side is the person who started this run, never the agent's creator. Otherwise anyone able to trigger an agent would borrow its creator's grants. A run with no initiating person resolves the human side as a principal with no roles and no grants, which can only narrow the result. A subagent is further narrowed by its parent run's scope. The audit event records both principals.
Agents get their own roles, separate from the human ones above. There are three built-in postures, intersected with the agent's own configuration and bounded by a delegation ceiling, so an agent can never outgrow the person it acts for. See Agent roles.
This is what makes agent work auditable. Every action leads back to the person who started it, or to a configured identity for an automated trigger.
Seeing and changing access
Roles and grants
Organization → Roles lists roles and their permissions. Owners and Admins can create custom roles, replace their permission sets, and delete them. Built-in roles are read-only. The page reports whether the organization tier enforces the grants. Change membership on the organization page.
Approval requests
When the deciding grant's effect is require approval, the kernel refuses the call with pending_approval and records an access request (arq_…) naming the principal and capability. There is no in-app screen for answering these requests yet.
Access reviews
Review members on the organization page and inspect membership events through query_audit_log. The former Access Reviews screen is not part of the current app.
Single sign-on
An Owner or Admin can register an OIDC or SAML 2.0 identity provider on Organization › Single sign-on and map IdP groups to organization roles on Organization › Roles. Each SSO sign-in replaces the person's organization role with the highest role their groups map to, and never changes an Owner. See Single sign-on.
SCIM directory sync is not available. Removing someone from the IdP stops their next SSO sign-in but does not end an existing session or revoke their API keys.
Governance overview
How Oxagen puts every person, agent, and API key under one set of rules, and keeps the answer to every request.
Single sign-on
Register an OIDC or SAML identity provider, verify your domain, map IdP groups to organization roles, require SSO, and provision people with SCIM from Okta or Microsoft Entra ID.