Wheelhouse docs

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#

RoleRankMay
viewer1Read every operational and configuration endpoint — with secret leaves blanked — and manage its own session, password and second factor.
approver2Everything 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.
operator3Everything 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.
admin4Everything 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#

GateMeans
UnauthenticatedFive routes need no principal at all.
Licence402 unless the licence is usable. Almost every write carries this as well as the operator role.
fleet featureA 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:

MethodPathWhat it does instead
POST/api/diagnostics/tracerouteA command on the router, not a configuration change.
POST/api/ids/ips/planRenders operations; stages nothing.
GET/api/reconcile/fileReads 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#

json
{"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#

PrincipalName in the audit logRole
A signed-in accountthe account namethe account's
An API tokentoken:<label>the token's
The break-glass tokenadmin-tokenalways admin
The reconcile loopreconcile-loop, role systemnot a request principal
First-run bootstrapsystemnot 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#

Updated 2026-09-18 api roles authorisation