Skip to content

Authorization reference — roles, permissions, gates

For admins

Authorization reference — roles, permissions, gates

Section titled “Authorization reference — roles, permissions, gates”

This is a code-grounded reference for the authorization model. For the conceptual intro to users, roles and departments — start with Users & roles first; come back here when you need to know exactly which permission gates which route.

Every fact below is derived from the running source under app/Modules/Identity/Permissions/ and the per-module routes.php files. If something doesn’t match what the app actually does, the doc is wrong — file paths are quoted throughout so you can verify.

  • Library — Spatie spatie/laravel-permission v6, configured in config/permission.php.
  • Workspace scopingteams mode is on, with a custom KeystoneTeamResolver (app/Modules/Identity/Permissions/KeystoneTeamResolver.php) that mirrors the active workspace. Roles and permissions are global templates (rows have workspace_id = NULL); the assignments (model_has_roles, model_has_permissions) are per-workspace.
  • Naming<scope>::<area>[::subarea]. Scope is either a product slug (helpdesk, assets, estates, people, scr, governance, hs) or platform for cross-product admin concerns. The bare admin permission is the super-admin escape hatch.
  • Single source of truthapp/Modules/Identity/Permissions/PermissionCatalogue.php. The seeder, the admin UI and this document all read from it.
  • Seedingdatabase/seeders/RolePermissionSeeder.php. Idempotent; safe on every deploy. It syncs roles via $role->syncPermissions($perms) so removing a permission from the catalogue strips it from existing seeded roles.

Ten system roles are seeded from PermissionCatalogue::roles():

RoleProductIntended personaNotes
adminCross-cuttingTrust / platform super-adminHolds the bare admin permission; passes every gate via Gate::before.
helpdesk::agentHelpdeskIT / ops agent working tickets, changes, projects, problems
helpdesk::requesterHelpdeskDefault role for any logged-in staff memberAlso carries governance::policy::acknowledge so anyone can ack a published policy.
assets::agentAssetsIT asset coordinatorDiscovery configuration is admin-only — see carve-outs.
estates::agentEstatesSite / facilities managerContractors are admin-only — see carve-outs.
people::agentPeopleHR staff managing employee recordsConfidential records / NI numbers need an explicit grant.
scr::dslSCRDesignated Safeguarding LeadNamed for the persona rather than scr::agent. Includes people::agent so a DSL can look up subjects.
governance::officerGovernanceGovernance officer / clerkRisk, compliance, policy, actions, evidence.
hs::leadHealth & SafetyDeputy head or dedicated H&S officerCan publish risk assessments and manage checklist templates.
hs::operativeHealth & SafetyCleaner / caretakerOnly ever runs checklists — never sees the RA library.

In addition to the seeded set, Platform admin → Roles lets you create custom trust-specific roles. Custom roles can hold any combination of catalogue permissions. The 10 above are flagged is_system: true — you can edit their permission set but you can’t rename or delete them.

63 permissions live in PermissionCatalogue::all(). Each one is shown alongside the UI grouping label that the admin Roles editor uses.

PermissionGroupingWhat it does
adminCross-cuttingSuper-admin escape hatch. Wired in IdentityServiceProvider::boot() via Gate::before — anyone holding it implicitly passes every subsequent gate.
PermissionGroupingWhat it gates
platform::users::managePlatform / Users & roles/admin/users, /admin/roles, /admin/departments.
platform::workspaces::managePlatform / Workspaces/admin/workspaces CRUD.
platform::settings::managePlatform / SettingsModules toggle, branding, system settings, Wonde integration, status page admin, mail config (with demo.readonly:mail), licence admin.
platform::audit::viewPlatform / Audit log/admin/audit, DSAR builder.
platform::schools::managePlatform / SchoolsMulti-school / trust school configuration.
platform::auth::managePlatform / AuthenticationSSO / authentication provider configuration.
platform::ai::managePlatform / AI settingsLLM provider configuration.
platform::ai::generatePlatform / AI insightsRun AI insight generation endpoints.
platform::imports::runPlatform / Imports/admin/imports — execute import jobs (controllers abort_unless on a per-importer permission too).
PermissionGroupingWhat it gates
helpdesk::adminHelpdesk / AdminInline-gated admin pages within the Agent route group (custom reports config, change templates, project templates, etc.). The Helpdesk admin shell was merged into /agent/manage in 2026-05.
helpdesk::agentHelpdesk / AgentEntry to /agent via EnsureAgent middleware.
helpdesk::requesterHelpdesk / RequesterMarker for the requester persona.
helpdesk::tickets::manageHelpdesk / TicketsCustom report builder (can:helpdesk::tickets::manage).
helpdesk::departments::manageHelpdesk / DepartmentsDepartment CRUD.
helpdesk::catalog::manageHelpdesk / CatalogService catalog management.
helpdesk::solutions::manageHelpdesk / SolutionsKnowledge base management.
helpdesk::sla::manageHelpdesk / SLASLA policy configuration.
helpdesk::automation::manageHelpdesk / AutomationWorkflow automation rules.
helpdesk::change::manageHelpdesk / ChangeChange templates + change management.
helpdesk::project::manageHelpdesk / ProjectProject templates + project management.
helpdesk::problem::manageHelpdesk / ProblemProblem management.
helpdesk::filters::shareHelpdesk / Share saved filtersShare saved ticket filters with the team.
helpdesk::webhooks::manageHelpdesk / Outbound webhooksOutbound webhook configuration.
PermissionGroupingWhat it gates
assets::adminAssets / AdminAsset categories, discovery agents, advanced configuration.
assets::agentAssets / AgentEntry to /agent/assets.
assets::registry::manageAssets / RegistryAsset registry CRUD.
assets::licensing::manageAssets / LicensingLicence tracking.
assets::discovery::manageAssets / DiscoveryInventory discovery daemon configuration (admin-only by convention; see carve-out).
assets::stocktake::manageAssets / StocktakeStocktake audits.
PermissionGroupingWhat it gates
estates::adminEstates / AdminContractor management, advanced compliance config.
estates::agentEstates / AgentEntry to /agent/estates.
estates::places::manageEstates / PlacesFacility places and room management.
estates::compliance::manageEstates / ComplianceCompliance and maintenance records.
estates::contractors::manageEstates / ContractorsPII-sensitive contractor records (admin-only — see carve-out).
estates::bookings::manageEstates / BookingsRoom / facility bookings.
PermissionGroupingWhat it gates
people::adminPeople / AdminPeople catalogues (/agent/people/... admin pages, gated by EnsurePermission::class.':people::admin').
people::agentPeople / AgentEntry to /agent/people.
people::records::managePeople / RecordsEmployee records and engagements.
people::engagements::managePeople / EngagementsEmployment engagement lifecycle.
people::confidential::viewPeople / Confidential recordsAccess flagged-confidential HR notes — explicit grant required, even for HR agents.
people::ni::viewPeople / NI number accessView the NI-number field (subset of confidential).
people::erasePeople / GDPR erasureGDPR erasure requests.
people::exportPeople / GDPR exportGDPR export requests.
PermissionGroupingWhat it gates
scr::adminSCR / AdminSCR admin surfaces.
scr::agentSCR / AgentEntry to /agent/scr.
scr::register::manageSCR / Single Central RecordSCR + check-type management (can:scr::register::manage).
PermissionGroupingWhat it gates
governance::adminGovernance / AdminDfE Handbook install, governance admin tools (many can:governance::admin route groups inside the Agent rail).
governance::agentGovernance / AgentEntry to /agent/governance.
governance::risk::manageGovernance / Risk registerRisk register and mitigations.
governance::compliance::manageGovernance / ComplianceCompliance framework control assessment.
governance::policy::manageGovernance / PolicyPolicy authoring; review-reminder console command also ->filter(fn ($u) => $u->can('governance::policy::manage')).
governance::policy::approveGovernance / Approve published policiesApprove + publish policies.
governance::policy::acknowledgeGovernance / Acknowledge policiesWider permission — granted to helpdesk::requester so any logged-in staff can acknowledge. Gates /me/policies via EnsurePermission::class.':governance::policy::acknowledge'.
governance::actions::manageGovernance / ActionsAction register.
governance::evidence::viewGovernance / View internal evidenceStandard internal evidence access.
governance::evidence::view::restrictedGovernance / View restricted evidenceRestricted-classification evidence (e.g. board-only documents).
governance::evidence::uploadGovernance / Upload evidenceEvidence uploads.
PermissionGroupingWhat it gates
hs::adminHealth & Safety / AdminAccident book mutations + most H&S admin sub-features (many can:hs::admin route groups in HealthSafety/Agent/routes.php).
hs::agentHealth & Safety / AgentEntry to /agent/hs.
hs::risk_assessments::manageHealth & Safety / Risk assessmentsRA publish / archive (can:hs::risk_assessments::manage).
hs::checklists::manageHealth & Safety / ChecklistsChecklist template management.
hs::checklists::executeHealth & Safety / Run checklistsSubmit checklist runs.

Verbatim from PermissionCatalogue::roles(). Anything not in this list is a deliberate omission documented in the source.

  • adminadmin. That’s all; everything else falls out of Gate::before.
  • helpdesk::agenthelpdesk::agent, helpdesk::tickets::manage, helpdesk::change::manage, helpdesk::project::manage, helpdesk::problem::manage.
  • helpdesk::requesterhelpdesk::requester, governance::policy::acknowledge.
  • assets::agentassets::agent, assets::registry::manage, assets::licensing::manage, assets::stocktake::manage.
  • estates::agentestates::agent, estates::places::manage, estates::compliance::manage, estates::bookings::manage.
  • people::agentpeople::agent, people::records::manage, people::engagements::manage.
  • scr::dslscr::agent, scr::register::manage, people::agent, people::records::manage.
  • governance::officergovernance::agent, governance::risk::manage, governance::compliance::manage, governance::policy::manage, governance::actions::manage, governance::evidence::view, governance::evidence::upload.
  • hs::leadhs::agent, hs::risk_assessments::manage, hs::checklists::manage, hs::checklists::execute.
  • hs::operativehs::agent, hs::checklists::execute.
  • assets::discovery::manage is not on assets::agent. Discovery daemon configuration is admin-only — agents see results but don’t wire up new agents.
  • estates::contractors::manage is not on estates::agent. Contractor records contain insurance + PII docs, so they sit behind estates::admin.
  • people::confidential::view is not on people::agent. Even HR agents need an explicit grant to read flagged-confidential notes. people::ni::view is a subset for the NI-number field specifically.
  • governance::evidence::view::restricted is not on governance::officer. Restricted-classification evidence is granted per-user.
  • governance::policy::approve is not on governance::officer. Only admin (via Gate::before) currently passes it.
  • helpdesk::requester carries governance::policy::acknowledge — broader than the other role badges, by design.
  • scr::dsl includes people::agent + people::records::manage so DSLs can look up subjects, but not people::confidential::view.

Catalogue permissions that no seeded role holds

Section titled “Catalogue permissions that no seeded role holds”

These exist in PermissionCatalogue::all() but aren’t on any role in ::roles(). They’re reached today only via the admin super-power or explicit per-user grant:

  • All platform::* (9 permissions). Platform admins are individual users granted the specific permissions they need.
  • helpdesk::admin, helpdesk::departments::manage, helpdesk::catalog::manage, helpdesk::solutions::manage, helpdesk::sla::manage, helpdesk::automation::manage, helpdesk::filters::share, helpdesk::webhooks::manage.
  • assets::admin, assets::discovery::manage.
  • estates::admin, estates::contractors::manage.
  • people::admin, people::confidential::view, people::ni::view, people::erase, people::export.
  • scr::admin.
  • governance::admin, governance::policy::approve, governance::evidence::view::restricted.
  • hs::admin.

That’s expected — the catalogue is intentionally wider than the default role set so Platform admin → Roles can compose custom roles.

Five mechanisms enforce permissions across the codebase.

app/Modules/Identity/IdentityServiceProvider.php lines 95–108:

Gate::before(function (?User $user) {
if (! $user) {
return null;
}
try {
if ($user->hasPermissionTo('admin')) {
return true;
}
} catch (PermissionDoesNotExist) {
// Permissions not seeded — fall through to other gate handlers.
}
return null;
});

Anyone with the bare admin permission passes every subsequent gate — no per-feature exceptions, no overrides. This is how the admin role reaches helpdesk::admin, platform::*, governance::policy::approve and every other carve-out without holding them explicitly.

app/Modules/Identity/Http/Middleware/EnsurePermission.php. Used as EnsurePermission::class.':permission.name'. Calls Gate::allows($permission); on denial, fires the AuthorizationDenied event (with permission name, path, method, IP, user-agent) and abort(403).

Applied to product entry rails:

Route prefixMiddlewareSource
/agent (Helpdesk)EnsureAgent::class → checks helpdesk::agentapp/Modules/Helpdesk/Agent/routes.php:23
/agent/assetsEnsurePermission::class.':assets::agent'app/Modules/Assets/Agent/routes.php:15
/agent/estatesEnsurePermission::class.':estates::agent'app/Modules/Estates/Agent/routes.php:21
/agent/people (admin pages)EnsurePermission::class.':people::admin'app/Modules/People/Agent/routes.php:13
/agent/peopleEnsurePermission::class.':people::agent'app/Modules/People/Agent/routes.php:37
/agent/scrEnsurePermission::class.':scr::agent'app/Modules/Scr/Agent/routes.php:9
/agent/governanceEnsurePermission::class.':governance::agent'app/Modules/Governance/Agent/routes.php:19
/agent/hsEnsurePermission::class.':hs::agent'app/Modules/HealthSafety/Agent/routes.php:16
/me (governance portal)EnsurePermission::class.':governance::policy::acknowledge'app/Modules/Governance/Portal/routes.php:9

app/Modules/Platform/Admin/Http/Middleware/EnsurePlatformAccess.php. Gates the /admin shell. It’s a loose OR check over six platform permissions:

private const PLATFORM_PERMISSIONS = [
'platform::users::manage',
'platform::workspaces::manage',
'platform::settings::manage',
'platform::audit::view',
'platform::schools::manage',
'platform::auth::manage',
];

If you hold any one of them you can enter /admin; per-page gates take over from there. The admin super-power also passes via Gate::before.

The full /admin route group also requires mfa.required-for-admins and the ProvideAdminNavigation middleware (app/Modules/Platform/Admin/routes.php:25).

4. Per-route can:permission.name middleware

Section titled “4. Per-route can:permission.name middleware”

Standard Laravel gate middleware. The notable applications:

PermissionRoutes (file:line)
helpdesk::tickets::manageHelpdesk/Agent/routes.php:119 (custom reports)
helpdesk::adminHelpdesk/Agent/routes.php:134 (admin pages inline within /agent)
helpdesk::change::manageHelpdesk/Agent/routes.php:148
helpdesk::project::manageHelpdesk/Agent/routes.php:170
helpdesk::catalog::manageHelpdesk/Agent/routes.php:247
helpdesk::solutions::manageHelpdesk/Agent/routes.php:284
assets::registry::manageAssets/Agent/routes.php:20
assets::adminAssets/Agent/routes.php:34, 47, 60, 76, 87
scr::register::manageScr/Agent/routes.php:33
governance::adminGovernance/Agent/routes.php:37, 57, 72, 90, 110, 157, 172, 187, 207
hs::risk_assessments::manageHealthSafety/Agent/routes.php:21
hs::adminHealthSafety/Agent/routes.php:36, 52, 68, 103, 126, 138, 153
platform::users::managePlatform/Admin/routes.php:58, 62, 66, 70
platform::workspaces::managePlatform/Admin/routes.php:81–96
platform::settings::managePlatform/Admin/routes.php:39, 101–107, 186–235; Integrations/Wonde/routes.php:8; Core/Status/routes.php:35
platform::audit::viewPlatform/Admin/routes.php:50, 147, 150
platform::schools::managePlatform/Admin/routes.php:112–142
platform::auth::managePlatform/Admin/routes.php:155–181
platform::ai::generateCore/Ai/routes.php:24, 29, 34, 39
platform::ai::manageCore/Ai/routes.php:48, 52, 56
platform::imports::runCore/Imports/routes.php:15

A handful of controllers check inline rather than (or in addition to) route middleware:

  • app/Modules/Core/Imports/Http/Controllers/ImportsController.php:103abort_unless($request->user()?->can($importer->permission()), 403) — each importer carries its own permission slug.
  • app/Modules/Helpdesk/Tickets/Channels/TicketPresenceChannel.php$user->can('helpdesk::agent') to decide presence-channel access.
  • app/Modules/Helpdesk/Agent/Http/Controllers/TicketsController.php:131abort_unless(request()->user()->canAccessDepartment($ticket->department_id), 403) — department scoping is enforced inline (see Department scoping below).
  • app/Modules/Helpdesk/Admin/Http/Controllers/CustomReportsAdminController.php:79 — additional gating on top of can:helpdesk::tickets::manage.
  • app/Modules/Estates/Agent/Http/Controllers/LoneWorkController.php$request->user()->can('estates::admin').
  • app/Modules/Governance/Console/SendPolicyReviewReminders.php:77 — recipient filter ->filter(fn (User $u) => $u->can('governance::policy::manage')).
  • app/Modules/HealthSafety/Console/Commands/SendRiskAssessmentReviewReminders.php:69 — recipient filter on hs::admin.

There is no app/Policies directory in use. Authorization is entirely permission/gate-based today. If you add an authorize() call in a controller, it’ll resolve through Gate::allows — which routes back to Spatie’s permission check (with the admin short-circuit).

Each role’s authenticated routes. “Reaches” here means the gate lets you in; you may still be scoped by workspace, school pinning, or department membership.

  • /admin (Platform shell) — every section, via Gate::before.
  • Every product agent rail (/agent, /agent/assets, /agent/estates, /agent/people, /agent/scr, /agent/governance, /agent/hs).
  • Every per-feature can:* gate, by virtue of holding admin.
  • Console commands and AI endpoints under platform::ai::*.
  • /agent — full Helpdesk agent rail.
  • Tickets, custom report builder (helpdesk::tickets::manage).
  • Changes (helpdesk::change::manage), Projects (helpdesk::project::manage), Problems (helpdesk::problem::manage).
  • Does not reach: helpdesk::admin pages (KB / catalog / SLA / automation / departments / webhooks / filter sharing / mail / reports config) — those are inline can:helpdesk::admin groups within /agent and require the admin permission or the admin super-role.
  • Department scoping (see internals) further narrows what tickets they actually see.
  • /tickets, /help, /catalog, /me (the portal — Helpdesk self-service).
  • /me/policies — policy acknowledgement (via governance::policy::acknowledge).
  • Does not reach: any /agent or /admin rail.
  • /agent/assets — IT asset rail.
  • Registry CRUD + categories list (assets::registry::manage).
  • Licence tracking (assets::licensing::manage).
  • Stocktake (assets::stocktake::manage).
  • Does not reach: discovery agent config, asset categories admin, or anything else inside the five can:assets::admin route groups in Assets/Agent/routes.php.
  • /agent/estates — Estates rail.
  • Places (estates::places::manage), Compliance (estates::compliance::manage), Bookings (estates::bookings::manage).
  • Does not reach: contractor records (admin-only, PII), lone-work admin (LoneWorkController checks estates::admin directly).
  • /agent/people — People rail.
  • Employee records (people::records::manage), engagements (people::engagements::manage).
  • Does not reach: /agent/people/... catalogues (require people::admin), confidential records, NI-number fields, GDPR erasure/export, or people::admin pages.
  • /agent/scr — SCR rail.
  • SCR + check types (scr::register::manage).
  • /agent/people rail and employee records — granted explicitly so a DSL can look up subjects (people::agent + people::records::manage).
  • Does not reach: SCR admin surfaces (scr::admin), people::confidential::view / people::ni::view, GDPR controls.
  • /agent/governance — Governance rail entry.
  • Risk register (governance::risk::manage), Compliance (governance::compliance::manage), Policy authoring (governance::policy::manage), Actions (governance::actions::manage).
  • Evidence view (governance::evidence::view) + upload (governance::evidence::upload).
  • Does not reach: the many can:governance::admin sub-route-groups inside Governance/Agent/routes.php (lines 37–207), policy approval (governance::policy::approve), restricted evidence (governance::evidence::view::restricted). These need explicit grants or admin.
  • /agent/hs — Health & Safety rail.
  • RA publish / archive (hs::risk_assessments::manage).
  • Checklist templates (hs::checklists::manage) and execution (hs::checklists::execute).
  • Does not reach: the seven can:hs::admin route groups (accident book mutations, etc.) — needs hs::admin or admin.
  • /agent/hs — entry only, restricted view.
  • Execute checklists (hs::checklists::execute).
  • Does not reach: RA library, RA management, checklist template management, or any hs::admin page.

Custom resolver: app/Modules/Identity/Permissions/KeystoneTeamResolver.php. Mirrors WorkspaceContext rather than using Spatie’s DefaultTeamResolver.

  • Permission catalogues live globally — one set for the whole platform.
  • Role rows live globally too (workspace_id = NULL on every seeded row).
  • model_has_roles and model_has_permissions carry a workspace_id FK — that’s where the per-workspace assignment is stored.

A user who is helpdesk::agent in workspace A and helpdesk::requester in workspace B has two rows in model_has_roles, one per workspace.

app/Modules/Identity/Models/User.php::mustEnrollMfa() returns true when the user:

  • Holds the bare admin permission, or
  • Has any direct platform::* permission, or
  • Holds a role that grants any platform::* permission.

The mfa.required-for-admins middleware on /admin (Platform/Admin/routes.php:25) enforces enrolment before the admin shell loads.

User::isTrustAdmin() returns true when the user has the admin role and is not school-pinned. User::isSchoolPinned() returns true when restricted_to_school_id is set. The SchoolSwitchController (Core/Schools/Http/SchoolSwitchController.php) aborts 403 if a pinned user tries to switch schools.

DSLs who should only see their own school’s safeguarding data are the common case for pinning.

Orthogonal to roles. Users can be assigned to one or more helpdesk departments (User::canAccessDepartment(...)). The check is enforced inline in TicketsController (app/Modules/Helpdesk/Agent/Http/Controllers/TicketsController.php:131). Empty membership means “no scoping” — the user sees all tickets in the product.

  • people::confidential::view — flagged-confidential HR notes. Not on people::agent by default.
  • people::ni::view — narrower; just the NI-number field.
  • governance::evidence::view::restricted — restricted-classification governance evidence.
  • governance::policy::approve — held by no seeded role; effectively admin-only today.

Permission mutations are audited via Spatie events, wired in IdentityServiceProvider:

  • RoleAttachedEvent / RoleDetachedEvent / PermissionAttachedEvent / PermissionDetachedEventAuditPermissionMutation handler.
  • AuthorizationDenied (custom, app/Modules/Identity/Events/AuthorizationDenied.php) — fired by EnsurePermission on every 403. Records permission name, path, method, IP, user-agent. Useful for spotting privilege-escalation probes.

database/seeders/RolePermissionSeeder.php. Idempotent; reads PermissionCatalogue::all() + ::roles() and:

  1. Upserts every permission.
  2. Upserts every system role with is_system => true.
  3. Calls $role->syncPermissions($perms) — so removing a permission from the catalogue strips it from the role on the next deploy.

Safe to run on every deploy. Custom roles created in the admin UI are untouched.

FileWhat’s there
app/Modules/Identity/Permissions/PermissionCatalogue.phpPermissions + roles catalogue (single source of truth).
app/Modules/Identity/Permissions/KeystoneTeamResolver.phpWorkspace resolver for Spatie teams mode.
app/Modules/Identity/IdentityServiceProvider.phpGate::before registration; Spatie audit-event listeners.
app/Modules/Identity/Http/Middleware/EnsurePermission.phpGeneric permission-gate middleware + AuthorizationDenied fire.
app/Modules/Identity/Events/AuthorizationDenied.phpForensic event for 403 from EnsurePermission.
app/Modules/Identity/Models/User.phpHasRoles trait + mustEnrollMfa(), isTrustAdmin(), isSchoolPinned(), canAccessDepartment().
app/Modules/Platform/Admin/Http/Middleware/EnsurePlatformAccess.phpLoose OR gate for /admin shell.
app/Modules/Platform/Admin/Http/Controllers/RolesController.phpAdmin UI for system + custom roles.
config/permission.phpSpatie config (teams on, custom resolver, workspace_id column).
database/migrations/identity/2026_04_26_000200_create_permission_tables.phpPermission tables with UUIDv7 + workspace columns.
database/seeders/RolePermissionSeeder.phpIdempotent role/permission sync.
app/Modules/*/Agent/routes.phpPer-product agent rails (entry + per-feature can:* groups).
app/Modules/Platform/Admin/routes.php/admin route group with EnsurePlatformAccess + per-section can:*.

The companion Users & roles article describes “standard roles” that include Platform admin, Workspace admin, Finance lead, DPO, HR lead and DSL (in the users-roles.md sense). Of those, only DSL maps cleanly onto the seeded role catalogue (as scr::dsl). The others reflect the design intent — they aren’t seeded today.

When in doubt, this document is authoritative for what the code does; users-roles.md is authoritative for the admin-onboarding mental model. Reconciling the two is a separate piece of work.