Roles & permissions
A permission catalog, two seeded roles, tenant-authored roles and permissions, and the difference between a Team and an IdP Group.
The first identity model in Exo had two roles printed into the schema:admin and member. It was good enough for early tenants and quickly stopped being good enough for anyone with an audit obligation. The current model replaces the enum with a permission catalog, tenant-scoped roles that bundle permissions, and a clean separation between IdP-sourced groups and tenant-authored teams.
The permission catalog
Every authorising decision in Exo is keyed by a string permission. The shipped catalog covers everything the built-in handlers gate. Most permissions follow aresource:verb shape; a few are namespace-wide read flags.
1agents:read | invoke | write | admin See, attach to, edit, and ACL-manage agents2deployments:read | write | delete Manage Exo Operator deployments + tokens3audit:read | export Read and export the audit streams4tools:read | write Cluster Tools / bastion catalogue5policies:read | write Runtime Policies6guardrails:read | write Guardrails7providers:read | write LLM provider gateways + credentials8integrations:read | write Slack / Jira / Adaptive integrations9oauth_apps:read | write OAuth Apps10mcp:read | write MCP servers + publications11skills:read | write Skills catalogue12users:read | invite | write Team members + invitations13roles:read | write Roles + permission assignment14idp:read | write Identity providers (SSO / SCIM)15tenant:read | write Tenant settings16claw / memory / log_storage :read|write Agent-adjacent + log-sink surfacesPermission keys are flat resource:verb strings. The full system list lives in the backend as model.SystemPermissions; every key the built-in handlers gate is in it, and a new key is a code change (so the catalog can't drift from what's actually enforced).
The two seeded roles
Every tenant ships with two roles populated. They are editable — including deletable, once you have created replacements you trust.
- Administrator. Every permission in the system catalog — the migration rebuilds this role from
SystemPermissionson each release, so it always covers new keys. Bound to the first signup on a new tenant. - Member. Read across the surfaces a user can already see, plus the verb that lets them do work:
agents:invoke. (Seeded fromMemberPermissions: read on agents, deployments, audit, tools, policies, guardrails, integrations, and providers.)
Authoring a role
Roles are tenant-scoped records — a name, a description, and a set of permissions. Author them in Settings → Roles in the dashboard. Two common patterns we see in practice:
- AuditReader.
audit:read,agents:read,deployments:read. Useful for SOC2 reviewers who need to inspect history without touching configuration. - OnCallEngineer.
agents:read,agents:invoke,tools:read. Just enough to attach to an agent and re-run things, with no write access.
Custom permissions
Tenants can author permissions that don't exist in our catalog —acme:invoice:approve, pii:read,treasury:wire-out. These permissions don't gate Exo's built-in handlers (those check the fixed system catalog); they're consumable by your own code:
- Inside an agent's runtime, gate a tool or action on a custom permission string.
- Inside a Runtime Policy classifier, deny tool input when a custom permission is missing.
The role-attach surface is identical — your role gets a checkbox next totreasury:wire-out the same way it does next toagents:invoke.
Teams and groups
Two ways to attach roles to groups of people, and the distinction matters for audit:
- Teams are tenant-authored. The dashboard is the source of truth for membership and role attachment. Useful for ad-hoc projects, on-call rotations, and tenants who don't have an IdP at all.
- Groups are IdP-sourced. SCIM pushes (or a periodic LDAP/AD pull) populate them. The dashboard treats them as read-only — only the IdP can mutate membership. You attach roles to a group; the user inherits those roles every time SSO or SCIM reconciles them.