Role matrix#
There are four roles, strictly ordered: viewer < approver < operator < admin. A principal
may call a route if its role is at least the route's minimum. The minimum belongs to
the route, and is listed for every one of them in the tables below.
The four roles#
| Role | Rank | May |
|---|---|---|
viewer | 1 | Read every operational and configuration endpoint — with secret leaves blanked — and manage its own session, password and second factor. |
approver | 2 | Everything a viewer may, plus commit, commit-confirm and roll back — somebody else's staged change. Not stage, un-stage, discard, configure or reconcile: those author or destroy a change, and authoring is what this tier does not have. |
operator | 3 | Everything an approver may, plus stage, un-stage, discard, configure, save and load configuration, reconcile, app lifecycle, traceroute, the IPS plan, and reading the desired-state file. |
admin | 4 | Everything an operator may, plus accounts, API tokens, agent settings, the licence, power and boot images, the unredacted configuration export, and the single sign-on view. |
An approver still sees the staged set redacted, because /api/staged redacts below
operator. That is deliberate: approving a change is agreeing to the change, not being
handed the credentials inside it.
Anything that is not one of the four is refused wherever a role is accepted:
POST /api/admin/users, PATCH /api/admin/users/{name}, POST /api/admin/tokens and
--oidc-default-role.
The gates beside the role#
| Gate | Means |
|---|---|
| Unauthenticated | Five routes need no principal at all. |
| Licence | 402 unless the licence is usable. Almost every write carries this as well as the operator role. |
fleet feature | A usable licence and a plan carrying that feature; otherwise 402 with "license":"feature". |
redactSecrets(need, …) | Gates the response: below role need, secret leaves in the body become [redacted]. See What a read hides. |
What each role may call#
Viewer#
Everything in the read plane. Concretely: /api/version, /api/system, /api/config,
/api/config/commands, /api/interfaces and its three siblings, /api/neighbors,
/api/routes, /api/wan, /api/vrrp, /api/lldp, /api/vpn/ipsec,
/api/vpn/openvpn, /api/wireguard, /api/diagnostics/lookup, /api/dhcp/leases,
/api/dhcp/reservations, /api/dns, /api/firewall/rules, /api/firewall/stats,
/api/firewall/groups, /api/nat/rules, /api/nat/stats, /api/qos, /api/qos/stats,
/api/sessions, /api/conntrack/stats, /api/routing, /api/ids, /api/drift,
/api/attention, /api/daemons, /api/apps, /api/apps/catalog, /api/apps/installed,
/api/apps/hints/{page}, /api/apps/logs/{name}, /api/containers, /api/services,
/api/processes, /api/pki, /api/ntp, /api/ddns, /api/log, /api/audit,
/api/history, /api/history/diff, /api/staged, /api/stream, /api/license,
/api/fleet, and /metrics unless --metrics-public is set.
Plus its own session: /api/auth/logout, /api/auth/me, /api/auth/password, the
three /api/auth/totp/* routes, /api/auth/sessions,
DELETE /api/auth/sessions/{id}, /api/auth/oidc/unlink and
PATCH /api/auth/preferences.
Plus, with a licence carrying the fleet feature, /api/fleet/{id}/config and
/api/fleet/{id}/version.
Operator, without a licence#
Three routes are operator-gated and licence-free. None of them changes the configuration:
| Method | Path | What it does instead |
|---|---|---|
| POST | /api/diagnostics/traceroute | A command on the router, not a configuration change. |
| POST | /api/ids/ips/plan | Renders operations; stages nothing. |
| GET | /api/reconcile/file | Reads a file out of the data directory and renders it as operations — not a viewer's business, but not a write either. |
Approver, with a licence#
POST /api/commit, /api/commit/confirm and /api/rollback — and, with the fleet
feature, POST /api/fleet/{id}/commit and /api/fleet/{id}/commit/confirm.
Those five and nothing else. None of them takes operations in its body — commit takes a
confirm timer, confirm takes nothing, rollback takes a revision number — which is what
makes the tier hold: there is no way to smuggle a change in through them.
Operator, with a licence#
POST /api/stage, /api/stage/remove, /api/discard, /api/configure,
/api/config/save, /api/config/load, /api/reconcile, /api/capture,
/api/apps/plan, /api/apps/pull, /api/apps/prepare, /api/apps/update,
/api/apps/restart, /api/apps/feature, POST /api/daemons/{name}/restart, and
DELETE /api/apps/image. The commit, confirm and rollback routes above are reachable
too, by rank.
Plus, with the fleet feature, POST /api/fleet/{id}/configure.
Admin#
GET /api/config/raw (the unredacted backup export), /api/admin/oidc, the four
/api/admin/users routes, the three /api/admin/tokens routes,
GET/PUT /api/admin/settings, POST /api/system/power, GET/POST
/api/system/factory-reset, POST /api/system/image, and PUT/DELETE /api/admin/license.
None of the admin plane needs a licence: an unlicensed router can still manage its own accounts and enter a licence.
What happens when the role is not enough#
{"error": "this action requires the operator role"}Status 403. The refusal is recorded in the audit log as an entry whose operation is
denied POST /api/commit, with the actor, the role it had, the source address and
requires operator as the error.
Role changes take effect immediately#
Changing an account's role, password or identity-provider link through
PATCH /api/admin/users/{name} drops every session that account holds, so a demotion
is not outlived by an open browser tab. Changing your own password does the same to your
other sessions and keeps the one you are using.
Two changes are refused with 409:
- demoting or deleting the last admin;
- deleting the account you are signed in as.
The count and the change happen under one write lock, so two concurrent demotions of the last two admins cannot both leave none.
Special principals#
| Principal | Name in the audit log | Role |
|---|---|---|
| A signed-in account | the account name | the account's |
| An API token | token:<label> | the token's |
| The break-glass token | admin-token | always admin |
| The reconcile loop | reconcile-loop, role system | not a request principal |
| First-run bootstrap | system | not a request principal |
admin-token and any name beginning token: are reserved. The agent refuses to create
an account with either, so an audit entry always names which of these acted.
Enrolment sessions are narrower than viewer#
When the require_totp policy is on and an account has not enrolled, its session is
marked enrolling and may reach only four paths — /api/auth/totp/begin,
/api/auth/totp/confirm, /api/auth/me and /api/auth/logout. Everything else answers
403 with "totp_enrolment_required": true, whatever the account's role is.
See also#
- Endpoint index — the same routes, in registration order.
- Authenticating
- Status codes — 401, 402 and 403 are three different problems.
- What a read hides
- Audit entries
- Roles and planes
- Roles — the screen.