- Two layers: organization roles (org-wide capabilities, e.g.
ADMIN,MEMBER,EXTERNAL) and team roles (per-team capabilities, e.g.ADMIN,MEMBER,VIEWER). - Two surfaces:
/settings/roles(the role catalog: pre-built + custom) and/settings/role-bindings(who is bound to what role at what scope). - One stitching mechanism: a
RoleBindingrow glues a user to a role at a scope (organization, team, project, or governance subscope).
aiTools:manage without granting full organization:manage, or when an audit asks “who can read the audit log” and you want a screen instead of a Slack thread.
Pairs with: Members & invites (assigning roles when inviting), Audit log (every role binding mutation records a row), and AI Gateway → RBAC (the gateway-side enforcement of permissions).
Organization roles
The three pre-built org roles. Each user in the org has exactly one.
The minimum-privilege default is intentional: an
ORGANIZATION role binding for a MEMBER does not grant team-level access. Team-level permissions come from team-role bindings (next).
Team roles
The four pre-built team roles. A user can have different team roles on different teams (ADMIN on Engineering, VIEWER on Marketing).
A user’s effective permissions on a resource are the union of every binding that applies, org-level baseline + team-level baseline + any custom-role overrides.
Permission catalog
Every gate in the codebase is a string of the shape<resource>:<verb>. The full catalog (abridged):
- Organization:
organization:view,organization:manage,organization:delete. - Governance:
governance:view,governance:manage,ingestionSources:*,anomalyRules:*,complianceExport:view,activityMonitor:view. - AI Tools Portal:
aiTools:view(everyone),aiTools:manage(admin). - Audit log:
auditLog:view(team-level by default; note the audit log UI page is gated toorganization:manage, the underlying procedure is onauditLog:view). - Per-team resources:
project:*,analytics:*,traces:*,virtualKeys:*,gatewayBudgets:*,gatewayProviders:*,routingPolicies:*,gatewayGuardrails:*,gatewayLogs:*,gatewayUsage:*,gatewayCacheRules:*,evaluations:*,datasets:*,workflows:*,prompts:*,scenarios:*,secrets:*.
What organization:manage gates today
After this PR’s RBAC defense-in-depth pass, the legacy /settings/{audit-log, teams, members, roles, groups} pages all require organization:manage (admins only). Underlying tRPC procedures match, role.getAll, organization.getAllOrganizationMembers, organization.getOrganizationPendingInvites, team.getTeamsWithRoleBindings, organization.getMemberById, group.getById, group.listForMember, group.listAll, limits.checkAndSendUsageLimitNotification are all gated to organization:manage server-side.
Picker procedures (team.getTeamsWithMembers, team.getTeamWithMembers, organization.getOrganizationWithMembersAndTheirTeams) intentionally stay on organization:view so non-admin pickers (the AddAutomationDrawer, GroupBindingInputRow, project pickers, onboarding flows) continue working, but they redact other members’ email addresses to null for non-admin callers and strip other users’ Personal Workspaces from the result entirely.
The role catalog at /settings/roles
The catalog page lists every role available in the org:
- 4 pre-built org roles (ADMIN, MEMBER, EXTERNAL, VIEWER).
- 4 pre-built team roles (ADMIN, MEMBER, VIEWER, CUSTOM).
- Any custom roles your admin has authored (see below).
MEMBER does, because the gateway and the control plane both encode those defaults. Custom rows are editable.
Custom roles
Custom roles are how you say “letbob@acme.test manage the AI Tools Portal but not the rest of the org.” They’re a RoleBinding row whose role = "CUSTOM" and whose customRoleId references a CustomRole row whose permissions JSON column lists exactly the gates that role grants.
To create one:
- Open
/settings/roles→ + New custom role. - Name the role (e.g.
aitools-curator). - Tick the permissions to grant. Permissions are scope-aware, granting
aiTools:manageat organization scope is different from granting it at team scope. - Save.
- Open
/settings/role-bindings→ + New binding. - Pick the user, pick the role (
aitools-curatorfrom the catalog), pick the scope (Organization, a specific team, a specific project, a governance subscope). - Save.
- Their org-role defaults (e.g. MEMBER →
organization:view+aiTools:view). - Their team-role defaults for every team they’re on.
- The new custom binding (
aiTools:manageat org scope).
Role bindings at /settings/role-bindings
The bindings page is a deep table: every active RoleBinding row in the org. Filter by user, role, scope, or scope target. The most common admin questions:
The page is the answer to “who can do what in this org.” Every change records an audit log row.
How permissions resolve at request time
A request hitting any*:manage-gated procedure flows through checkOrganizationPermission (or checkTeamPermission for team-scoped procs), which:
- Walks the user’s role bindings.
- Builds the effective permission set as the union of: org-role defaults + every team-role default the user has + every custom-role permission via active bindings.
- Returns
trueif the requested permission is in the set.
Where to next
- Invite someone with a specific role: Members & invites.
- Audit who did what: Audit log.
- Gateway-side enforcement: AI Gateway → RBAC.
- Workspace scoping: Workspaces (where org, team, project sit relative to each other).