Skip to Content
BusinessFeature Role MatrixFeature role matrix

Feature role matrix

Which of the six roles can do what, one page per product seam. Open a section below, or read Roles first for what each persona is for.

Sections

Where the truth actually lives

Permissions are code, not documentation. Three files decide what a role can do, and the platform enforces from them directly:

FileWhat it holds
packages/services/src/permissions/registry.tsThe vocabulary — every <feature>.<action> the platform knows
packages/services/src/permissions/defaults.tsPLATFORM_ROLE_TEMPLATE_DEFAULTS — the permissions each role is seeded with
apps/web/lib/route-policies.tsWhich roles may open which URL

Server actions enforce the registry via requiredPermission, route handlers via requirePermission, and the route proxy derives its allow-lists from the same defaults. There is no separate list of who-can-do-what that the code consults — these pages describe that code, they do not govern it.

For the live answer, read the scope and licences tab at /admin/settings/scope in the platform (admin only). It renders every role’s effective capabilities and the surfaces each can reach from the tenant’s own role templates and the route rules, so it reflects per-tenant edits that these pages cannot show.

How these pages are maintained

Hand-maintained, one page per seam, and every row names the gate behind it — either the permission that guards the action or the route rule that guards the page. That naming is the maintenance story: change a holder in defaults.ts or a rule in route-policies.ts, and the rows citing it are exactly the rows to update. The delivery pipeline’s Release stage does that in the same PR as the change, via the docs-sync skill.

The choice was deliberate. There is no master file behind these pages any more: a public/feature-role-matrix.md spreadsheet used to claim that title while documenting an architecture the platform no longer has, and it contradicted these pages in roughly a dozen places. It was retired rather than refreshed, and the per-section pages below stand alone. Generating a static role-capability table from PLATFORM_ROLE_TEMPLATE_DEFAULTS at build time remains an open option for later; nothing here depends on it landing.

Reading the tables

Each section splits into two tables:

  • Capabilities — actions gated by a permission from the registry. Cells show the platform default for a fresh tenant; an admin who edits a role template changes their own tenant, not this page.
  • Surfaces — pages, gated by route-policies.ts. Most rules derive their allow-list from a permission; the rest carry an explicit role list and a stated reason, and those are literal — a role absent from an explicit list cannot open the page.

Cell conventions:

  • ✔️ — held by default. ❌ — not held.
  • own — the role reaches only its own records (the scope qualifier in defaults.ts).
  • A parenthesised note such as (manager only) is a presentation qualifier the platform surfaces as a hint, not a second access level: the permission gate itself is binary.
  • admin is a hard superuser — the resolver short-circuits it to allow-all, so it holds every capability whatever the template says. It is not automatically on an explicit route list, which is why some Surfaces rows show ❌ for admin.
Last updated on