On this page
ADR-0006: RBAC with one static role-to-permission matrix, enforced in services
Status: Accepted (plan decision D6). Implemented in crates/commonlayer-core/src/authz/.
Context
Permissions are a first-class requirement: maintainers, project admins, organization admins, billing managers and platform admins have overlapping but different rights, and several of them move money or publish software. Checks spread across handlers and UI code drift apart and are hard to audit.
Decision
- One
consttable,commonlayer_core::authz::policy::ROLE_PERMISSIONS, maps each role in each scope (platform, organization, project, self) to permissions. Roles are cumulative within a scope;platform_adminholds every permission. - Role bindings live in the database (
users.platform_role,organization_members,project_members, and activemaintainer_assignmentsthrough the vieweffective_project_roles). One query against the vieweffective_grantsloads all grants of a principal per request. - Every application-service method that is not a
*_publicread callsactor.require(permission, scope)before any side effect. Handlers and the UI never decide. - API tokens intersect the owner's permissions with the token's scopes; a fixed set of permissions is never available to tokens (ledger adjustment, payout approval, user management, settings, credential changes).
Consequences
clctl permissionsrenders the matrix (the generated page isdocs/security/permissions.md, produced by the integration work package).- Rules that a matrix cannot express (nobody approves their own reward, payout run or
exception; a ledger adjustment needs a written justification) are enforced in services
and, where possible, by database
CHECKconstraints or triggers (CL006). - Additions during implementation: permission
org.audit.read(WP-01) and theWorkloadprincipal for CI submissions (ADR-0024). - Each domain crate has authorization tests per non-public method (anonymous, unrelated user, role just below, lowest sufficient role).
Amendment, 2026-11-10
- Since the security review (HIGH-2) a set of approval permissions is human-only: service accounts never hold them, whatever role they were given, and the deciding services also require a person or a person in an interactive session (ADR-0035). The generated permission matrix lists the set.