§05 — Identity

Roles & permissions

A permission catalog, two seeded roles, tenant-authored roles and permissions, and the difference between a Team and an IdP Group.

5 min read·Set by Exo Editorial·v0.3.0 Beta

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.

system catalog (excerpt)· text
1agents:read | invoke | write | admin See, attach to, edit, and ACL-manage agents
2deployments:read | write | delete Manage Exo Operator deployments + tokens
3audit:read | export Read and export the audit streams
4tools:read | write Cluster Tools / bastion catalogue
5policies:read | write Runtime Policies
6guardrails:read | write Guardrails
7providers:read | write LLM provider gateways + credentials
8integrations:read | write Slack / Jira / Adaptive integrations
9oauth_apps:read | write OAuth Apps
10mcp:read | write MCP servers + publications
11skills:read | write Skills catalogue
12users:read | invite | write Team members + invitations
13roles:read | write Roles + permission assignment
14idp:read | write Identity providers (SSO / SCIM)
15tenant:read | write Tenant settings
16claw / memory / log_storage :read|write Agent-adjacent + log-sink surfaces

Permission 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 SystemPermissions on 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 from MemberPermissions: 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.