Skip to content
CommonLayer
Documentation menu
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 const table, 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_admin holds every permission.
  • Role bindings live in the database (users.platform_role, organization_members, project_members, and active maintainer_assignments through the view effective_project_roles). One query against the view effective_grants loads all grants of a principal per request.
  • Every application-service method that is not a *_public read calls actor.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 permissions renders the matrix (the generated page is docs/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 CHECK constraints or triggers (CL006).
  • Additions during implementation: permission org.audit.read (WP-01) and the Workload principal 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.