Roles — Reference
The Roles surface is the access-definition plane for SGEN. It owns the catalog of operator roles, the capability set each role grants, the inheritance rules between roles, and the per-site override layer that lets one site reshape the default catalog without affecting another. Anything that asks "what is this operator allowed to do?" traces back to a record managed here.
This page is a reference for platform engineers and integrators who need to understand the surface before scripting against it, extending the capability catalog, or scoping a custom-role model. Customer-facing role walkthroughs live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Roles live under the Settings → Roles module in SG-Admin. The module renders three primary views — the role list, the role create / edit form (with the capability matrix), and the role-member view that lists every operator currently holding the role. The surface exposes a small set of write operations for create, edit, clone, delete, and bulk reassignment of members.
The module is paired by convention with the Settings surface. Roles are technically a setting family — they ship as the roles family inside the Settings record store — but the editing experience is large enough that SG-Admin renders it as its own module entry. Engineers reading the SG-Admin source will see the role-edit form templates under the Settings view tree, while the role-related writes route through a dedicated controller action set.
A second surface — per-site role overrides — extends the same record shape. A multi-site SGEN deployment can ship a default role catalog at the platform tier and let each site override the catalog locally. Override records carry the same fields as base role records, with a parent pointer that re-establishes inheritance.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Settings → Roles
- URL prefix:
/sg-admin/settings/roles/ - View templates:
application/views/Admin/Settings/Roles/
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Settings → Roles [+ New role] │├──────────────────────────────────────────────────────────────────────┤│ Role Type Members Capabilities Inherits ││ ───────────────── ────────── ──────── ───────────── ─────────── ││ Administrator Built-in 4 all (84) — ││ Editor Built-in 7 42 / 84 — ││ Viewer Built-in 12 18 / 84 — ││ Marketing Editor Custom 3 46 / 84 Editor ││ Support Agent Custom 2 24 / 84 Viewer ││ Read-only Auditor Custom 1 12 / 84 Viewer ││ ││ [⋯ Edit] [⋯ Clone] [⋯ View members] [⋯ Delete] │└──────────────────────────────────────────────────────────────────────┘Actions
The Roles surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List roles
Returns every role definition visible on the current site. Each row carries the role slug, display name, type (built-in or custom), member count, capability count (granted / total), and the parent role for inherited roles. Built-in roles always appear first; custom roles sort by display name.
View role
Loads a single role record with the full capability matrix expanded. The matrix shows every capability the platform exposes, grouped by module (Users, Pages, Posts, Media, Settings, and the rest), with the role's resolved state per capability — granted, denied, or inherited from the parent.
Create role
Opens an empty role form. The form prompts for the role slug, the display name, an optional description for operators picking the role from the user-edit screen, and the starting capability set. The starting set can be empty (every capability defaults to denied), cloned from an existing role (every capability copies its current state), or inherited (every capability defers to a parent role and only the differences are recorded).
Edit role
Loads an existing role into the same form shape used for create, pre-populated. Submit replaces the stored capability overrides with the posted values. The role slug is read-only after creation — changing the slug would orphan every member assignment. The display name and description are freely editable.
Edit capability matrix
A sub-view of the edit form, optimized for sweeping capability changes across many modules at once. Renders the matrix as a checklist grouped by module, with a state indicator per capability (grant, deny, inherit). Each toggle records an explicit override; toggling back to inherit removes the override and re-defers to the parent.
Clone role
Creates a new role with the same capability state as a source role. The clone form prompts for the new slug and display name; the capability matrix copies from the source at clone time and diverges from it after. Useful for shipping a slight variant of an existing role without affecting the original.
Delete role
Removes a custom role. Before deleting, the surface checks for member operators — a role with members cannot be deleted until the members are reassigned. The delete form lists the affected operators and offers a bulk reassign action that moves every member to a chosen replacement role in a single write. Built-in roles cannot be deleted; the platform refuses the action with a structured rejection.
View members
Lists every operator currently holding the role, with the operator's name, email, last-seen, and a per-row reassign action. Useful for an audit pass before deleting or restructuring a role.
Bulk reassign members
Moves every operator from one role to another in a single write. Used by the delete flow, but also directly available as an admin action. The replacement role is chosen at the time of the reassign; operators retain every other profile field (name, email, password hash, profile data) — only the role pointer updates.
Resolve effective capabilities
A read-only computation. Given an operator's role and the role's parent chain, returns the resolved capability map — the same map the per-action gate consults at request time. Useful for diagnostic work, for surfacing "what can this operator do?" in a UI, and for integration testing against the gate.
Data model
A role record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
slug | string | Role identifier. Stable after create. Used by user records and per-action gates. |
display_name | string | Display label rendered in the role picker and the role list. |
description | string | Optional. Surfaced in the role picker tooltip and the create / edit form. |
is_built_in | boolean | True for Administrator, Editor, Viewer. Built-in roles can be edited (capability overrides) but never deleted. |
parent_role_id | integer | Optional. Points to the role this role inherits from. NULL for roots. |
capability_overrides | JSON | Map of capability identifier → grant or deny. Capabilities not listed inherit from the parent (or default to deny for root roles). |
member_count | integer | Denormalized count of operators currently holding the role. Refreshed on user-edit writes. |
site_id | integer | Optional. NULL for platform-tier role definitions; populated for per-site overrides. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on any edit. |
Per-site override semantics: a multi-site SGEN deployment ships a platform-tier role catalog (site_id is NULL) and lets each site override the catalog locally (site_id is populated). The resolution at request time checks for a site-tier override first, then falls back to the platform-tier record with the same slug. The slug is the join key between the two tiers.
Capability identifier shape: capabilities are dotted slugs grouped by module — users.create, users.delete, pages.publish, media.delete, settings.roles.edit. The catalog of capability identifiers is owned by the platform and changes only when a module ships a new gate. Custom roles can grant or deny any capability the platform exposes, but cannot define new capability identifiers.
ROLE RECORD├── id integer primary key├── slug string stable, join key├── display_name string display├── description string optional tooltip├── is_built_in boolean protect against delete├── parent_role_id integer NULL for roots├── capability_overrides JSON {capability_slug: grant|deny}├── member_count integer denormalized├── site_id integer NULL = platform tier└── timestamps created_at, updated_at↓ inheritance chain (resolved at request time)┌───────────────────────────┐│ Role: Marketing Editor │ parent → Editor│ overrides: 4 grants │└─────────────┬─────────────┘│▼┌───────────────────────────┐│ Role: Editor (built-in) │ parent → NULL│ baseline: 42 grants │└───────────────────────────┘Permissions
Access to the Roles surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Roles surface.
Layer 2 — per-action capability. Within SG-Admin, each Roles action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List roles | ✔ | ✔ | ✔ |
| View role | ✔ | ✔ | — |
| Create role | ✔ | — | — |
| Edit role (display name, description) | ✔ | — | — |
| Edit capability matrix | ✔ | — | — |
| Clone role | ✔ | — | — |
| Delete role | ✔ | — | — |
| View members | ✔ | ✔ | — |
| Bulk reassign members | ✔ | — | — |
| Resolve effective capabilities (own role) | ✔ | ✔ | ✔ |
| Resolve effective capabilities (any role) | ✔ | — | — |
Last-administrator protection. At least one operator on the site must hold a role with the settings.roles.edit capability. A capability edit that would remove the last operator's role-edit authority — or a delete that would orphan the last administrator — is rejected with a structured rejection. The protection prevents the site from being locked out of its own role catalog.
Inheritance cycle protection. The parent chain must form a directed acyclic graph. A capability edit that would create a cycle (role A inherits from B, B inherits from C, C inherits from A) is rejected with a structured rejection. The protection runs at save time, not at resolution time, so that the resolver at request time can assume a finite chain.
Audit. Every write — create, edit, capability override change, clone, delete, bulk reassign — emits an Activity Log entry. The log records the acting operator, the role affected, and the change shape (the per-capability diff for matrix edits, the parent pointer change for inheritance edits, the member list for bulk reassign). Role changes have the broadest blast radius of any single change on the platform; the audit posture matches.
REQUEST (operator on role "Marketing Editor")│ needs: pages.publish▼┌───────────────────────────┐│ Look up role record ││ slug = "Marketing Editor" │└─────────────┬─────────────┘│▼┌───────────────────────────┐│ Check capability_overrides│└─────────────┬─────────────┘│├──► grant ──► allow├──► deny ──► reject└──► absent ──► walk to parent│▼┌───────────────────────────┐│ Parent role: "Editor" ││ pages.publish: grant │└─────────────┬─────────────┘│▼allow → action runsActivity Log entryRelated references
- Users — Reference. Every user record carries a role pointer (
rolefield). The Roles surface defines what those pointers resolve to. - Settings — Reference. Roles is technically a Settings family. The Settings surface owns the per-module override layer that role capability checks consult.
- Permissions — Reference. The catalog of capability identifiers (the slugs that appear in
capability_overrides) is defined and described in the Permissions reference. - Pages — Reference. Page publish, edit, and delete actions check capabilities defined here.
- Posts — Reference. Post lifecycle actions route through the same capability gate.
- Media — Reference. Media upload, edit, and delete actions check capabilities defined here.
- Tools — Reference. Activity Log entries for role writes are queryable from Tools.
/docs/roles.