Users and permissions
User management, roles and the RBAC permission model
This covers users inside a tenant. "User" here means someone who works in the workspace — not a site member; see Site members for the difference.
Managing users
Admin → Users:
- Create a user (username plus an initial password)
- Edit details, enable or disable, reset the password
- Assign roles
Users sign in to /app/*; which modules they see depends on their permissions.
Where permissions come from
There are exactly two sources:
- The system administrator flag. A user with it holds every permission in the tenant and needs no roles at all. Keep at least one such user, or nobody can grant anything to anyone.
- Roles. For everyone else, permissions come only from assigned roles. No roles means no permissions.
New tenants ship with no predefined roles. A preset "Editor" means something different at every organization, so rather than guess, the first administrator builds the roles that match how the team actually works.
Permission names
Permissions are named <module>.<action> and each module declares its own, which are
merged into a catalogue:
| Permission | Meaning |
|---|---|
site.read |
View site content |
site.write |
Edit site content |
user.manage |
Manage users |
role.manage |
Manage roles |
audit.read |
Read the audit log |
Only keys that exist in the catalogue take effect. Typing one by hand raises no error but never matches anything, so build roles by ticking entries from the catalogue.
Managing roles
Admin → Roles: browse the permission catalogue, create a role and tick its permissions, edit it, or delete it (roles still assigned must be unassigned first).
Checking permissions
On the client:
const { hasPermission } = usePermissions();
if (hasPermission("site.write")) {
// show the edit button
}
Entrances the user lacks permission for are not rendered at all, rather than disabled — a button that does nothing just generates questions.
On the server:
app.get("/api/site/docs", { preHandler: [app.requirePermission("site.read")] }, handler);
The server check is the only one that actually enforces anything. Hiding UI is a courtesy; anyone can call the endpoint directly.
Audit log
Every write is audited: who, when, which resource, what changed. Filter by module, action and time in Admin → Audit log.
New modules must audit their writes too — it is one of the module contract checks, and
check:modules fails when it is missing.
Members, for comparison
| User | Member | |
|---|---|---|
| Who | Runs the site | Visits the site |
| Entrance | /app/* |
/member/* |
| Permissions | RBAC roles | Signed in as a member, or not |
| Managed in | Admin → Users | Site management → Members |