Skip to content

Roles

A role bundles permissions for one app. Users receive roles only through their groups — never directly.

Roles list

The permission model

User
   ↓ membership (transitive BFS)
Group(s)
   ↓ does BoundTo contain the requesting app?  (otherwise: dormant)
active group(s)
   ↓ roles
Role(s) (with AppSlug)
   ↓ filter: Role.AppSlug == requesting app?  (or permission is fully-qualified)
Permission(s)  →  app:resource:action

Effect: a user is Editor in Acme-Tasks because

  1. they are a member of a group Acme-Tasks Team,
  2. the group has BoundTo: ["acme-tasks"],
  3. the group references a role Acme-Tasks Editor with AppSlug = "acme-tasks",
  4. the role's permissions read, write on resource todo expand to acme-tasks:todo:read, acme-tasks:todo:write.

Permission format: three segments

Modgud manages permissions as app:resource:action strings:

PermissionMeaning
modgud:user:readRead the user list in modgud
modgud:oauth-client:writeEdit OAuth clients in modgud
acme-tasks:todo:writeWrite todos in the Acme-Tasks app

Plus three bypass tiers:

  • realm:admin — realm-wide. The holder may do anything in any app.
  • <app>:admin — app-wide.
  • <app>:<resource>:admin — resource-wide.

Standard roles (after setup)

When the first admin in a realm is created (recovery CLI or HTTP bootstrap-invite — see First-time setup), Modgud atomically seeds three roles — all under the system app modgud:

RoleAppEffect
System Adminmodgudholds the fully-qualified permission realm:admin → realm-wide bypass
User Managermodgudmodgud:user:read/write + :session:read/write + :authorization-group:read + :permission-role:read + :auth-log:read
Viewermodgudread-only on user, authorization-group, permission-role

Run node scripts/seed-demo.mjs after first login and you'll get additional roles for realistic test setups (see data/demo-seed.json for the manifest).

Resources available per app

What resources an app has is defined by the app itself — see Applications. The system app modgud has these built in:

ResourceTypical actions
appread, write, admin (for app management itself)
userread, write
sessionread, write
permission-roleread, write
authorization-groupread, write
oauth-clientread, write
oauth-scoperead, write
oauth-apiread, write
login-provideradmin, read, write
idp-configread, write
realmread, write
auth-logread
gdpradmin

External apps (Acme-Tasks, Knowledge, …) bring their own resources, defined in their App record.

Creating or editing a role

Administration → RolesCreate, or double-click an entry.

Role detail

Fields:

  • Name (unique per realm)
  • Description (optional)
  • AppSlug — which app does this role belong to? Required. A role belongs to exactly one app.
  • Resource Type — together with AppSlug determines the permission prefix
  • Permissions — actions on the resource. With Resource Type todo and Permissions ["read", "write"], the role resolves to <AppSlug>:todo:read and <AppSlug>:todo:write.

Multi-resource roles

If you want a role to span several resources (e.g. "User Manager" covers user, session, authorization-group), leave Resource Type empty and write fully-qualified permissions in the list:

modgud:user:read
modgud:user:write
modgud:session:read
modgud:authorization-group:read

Fully-qualified strings (containing :) pass through the resolver unchanged. The seeded System Admin / User Manager / Viewer roles are built exactly this way.

Cross-app roles (special case)

A role can also include fully-qualified permissions from other apps in its permissions list — for example a "Cross-App Auditor" with modgud:auth-log:read AND acme-tasks:audit:read. This works because fully-qualified permissions pass through without further filtering.

In practice though: prefer two separate roles in two separate groups (each with their own BoundTo). Cleaner to understand and audit.

Bypass roles

A role becomes a bypass role when its permissions list contains an admin-shaped entry:

In the permissions listEffect
realm:admin (fully qualified)realm-wide bypass
<app>:adminapp-wide bypass
<app>:<resource>:admin (Resource Type empty + fully qualified)resource-wide
admin (with Resource Type set)resource-wide, AppSlug-prefixed

On setup exactly one user is seeded as realm admin (System Admin role + Administratoren group with BoundTo: ["*"]). Grant sparingly — realm-admin is the nuclear option.

Deleting a role

List → right-click → Delete.

Soft delete

Roles are soft-deleted. Groups that referenced the role keep the entry technically — but the role contributes no permissions any more. To remove a role cleanly, remove it from all groups first.

Tips

Keep roles narrow

Many small roles, each tied to a clear resource, compose freely into groups. A "SuperAdmin" role with every permission is usually a design smell; use realm:admin for that, or combine specialised roles in an admin group.

Per-app roles

Roles for Acme-Tasks go under AppSlug = "acme-tasks", not modgud. They show up in the right permission lists, and [Authorize(Roles = "...")] in the Acme-Tasks backend finds them via the resource_access["acme-tasks"] claim in the token.

Released under the Apache-2.0 License.