Roles and permissions
The four roles, what each may do, and which of the two stored roles is the one actually enforced.
AccelerUp has four roles. They are hierarchical: each includes everything below it.
| Role | Can |
|---|---|
viewer | Read everything the organisation can see. Acknowledge alerts. |
engineer | Everything above, plus create and edit services, rules, dashboards, alerts, incidents, deployments. |
admin | Everything above, plus manage members, integrations, datasources, retention, platform thresholds, and most destructive operations. |
owner | Everything above, plus transfer ownership and remove the last admin. One per organisation. |
The part that has caught people out
There are two role columns in the database, and only one of them is enforced.
| Column | Purpose |
|---|---|
users.role | Enforced. Carried in the JWT claim and checked by every middleware. |
organization_members.role | Display only. What the team page shows. |
If those two disagree, the user gets the permissions in users.role while the
team page shows something else — which reads as a bug in the permission system
and is actually two rows out of step.
Always change a role through OrgRepo.SetUserRole, which writes both, and
always call RegisterUserLogout afterwards. The role lives in a JWT: without
forcing a re-login, a demoted user keeps their old permissions until their
token expires.
This applies to every membership or role change, including removing a member.
How a request is checked
request → JWTAuthMiddleware → is the token valid, and whose org is this?
→ IPAllowlistMiddleware → (enterprise) is this address allowed?
→ apiRateLimit → is this caller over budget?
→ AuditMiddleware → record who did what
→ withRole(handler, …) → does users.role clear the bar?
→ handler → scoped to the caller's orgEvery handler is organisation-scoped in its own query as well. The middleware
decides whether you may call it; the query decides whose data you get. Both,
always — a permission check alone would still return another tenant's row if
the query forgot its org_id.
Which endpoints need which role
The endpoint index lists the least-privileged role for all ~1000 routes, generated from the router itself. As a rule of thumb:
GETon organisation data — any authenticated memberPOST/PATCH/PUTon operational objects —engineerDELETE, integrations, datasources, members, retention —admin- Anything that changes who has access —
owneroradmin
Roles that are not user roles
Several features have their own role vocabulary. These are unrelated to the four above and do not grant any platform permission:
| Role set | Where | Values |
|---|---|---|
| Incident roles | Incidents | commander, scribe, comms, responder |
| Jenkins job roles | Jenkins | deploy, rollback, verify |
| MySQL node roles | Databases | writer, replica, either |
| Agent OS identity | Fleet access | root, user |
Agent access is separate
Being an admin in AccelerUp does not give you a shell on a server. Fleet
access is granted explicitly, carries an OS identity and an expiry, and is
capped by the machine's own profile. See Access and grants.
This is deliberate: the blast radius of "can edit a dashboard" and "can run a command as root on production" should not be the same permission.
SSO and SCIM
With enterprise SSO enabled, roles can be mapped from the
identity provider, and SCIM 2.0 can provision and deprovision members. The
mapping writes users.role, so the same rule applies — a role change through
SCIM also invalidates existing sessions.
Where this behaviour lives: backend/internal/domain/user.go, backend/internal/domain/org.go, backend/internal/handler/middleware.go. If the code and this page disagree, the code is right — please fix the page.
Part of Getting started — What AccelerUp is, the words it uses, and the first hour with it.