Wheelhouse docs

Roles#

There are four roles, and the agent enforces them on every request. A principal — a person signed in, an API token, or the break-glass token — holds exactly one. The web interface disables what your role cannot do and says why when you hover over it; the check that matters happens in the agent, and a request that gets past a disabled button is refused and recorded.

viewer  <  approver  <  operator  <  admin

The roles are ranked: an admin can do everything an operator can, an operator everything an approver can, and an approver everything a viewer can.

The four roles#

The sentences the Users page shows for each role:

RoleWhat the Users page says
ViewerSees everything. Changes nothing.
ApproverApplies, keeps and reverts changes others have prepared, and restores earlier configurations. Cannot prepare changes, and does not see the secrets in them.
OperatorPrepares and applies changes, and restores earlier configurations.
AdminEverything an operator can do, and manages users, API tokens and settings.

Approver is the review tier. The person who reviews a change is usually not the person who wrote it, and without this role the reviewer had to be made an operator — given every change to the router to buy the one action they are there for. An approver can apply pending changes somebody else prepared, with Apply changes or Apply with auto-revert; press Keep changes before the router reverts them; and restore an earlier revision. An approver cannot add to the pending changes, remove one, Discard them, or reconcile. None of the requests an approver can make carries a change of its own, which is what makes the line hold.

What each role may do#

ViewerApproverOperatorAdmin
See every page: interfaces, routes, sessions, logs, the audit logyesyesyesyes
See the router's configurationyes, secrets hiddenyes, secrets hiddenyes, secrets hiddenyes, in full
See the pending changesyes, secrets hiddenyes, secrets hiddenyes, in fullyes, in full
See its own sessionsyesyesyesevery account's
Change its own password, set up two-factor authentication, turn Advanced mode on or offyesyesyesyes
Apply changes, Apply with auto-revert, Keep changes, restore an earlier revisionnoyesyesyes
Add to the pending changes, remove one, Discardnonoyesyes
Save configurationnonoyesyes
Reconcile against a desired-state filenonoyesyes
Install, update, restart or remove an app; restart a companion service or a containernonoyesyes
Run a traceroute or a packet capture; plan an inline IPSnonoyesyes
Check a certificate before adding it, or generate one; plan an OpenVPN servernonoyesyes
Check now for updatesnonoyesyes
Remove a UPnP mappingnonoyesyes
Apply changes on a fleet routernoyes, with a licence that includes fleetyes, sameyes, same
Prepare changes on a fleet routernonoyes, with fleetyes, same
Add, change and delete users; clear another user's two-factor authenticationnononoyes
Issue and revoke API tokensnononoyes
Change agent settings, Restart agent, enter or remove a licencenononoyes
Turn the daily update check on or off; install an agent updatenononoyes
Change the UPnP settingsnononoyes
Set up the Git export of the configurationnononoyes
Restart router, Shut down, manage system imagesnononoyes
Download configuration, with its secretsnononoyes
Download or restore a backup; take or restore a snapshotnononoyes
Download a support bundlenononoyes
Verify the audit log's hash chain, or export the audit lognononoyes

Changing the router, and a few admin actions — backups, snapshots, installing an agent update, the UPnP settings — also need a usable licence; see The licence gate is not a role.

Where a role comes from#

PrincipalRole from
A person signed inThe account's role, copied into the session when it was opened.
An API tokenThe role chosen when the token was issued.
The break-glass tokenAlways admin.
A person signed in through single sign-onThe account's role, which the identity provider's groups may set — see Single sign-on.

A session carries the role it was opened with, so changing an account's role ends its sessions.

The licence gate is not a role#

A licence gates changes to the router, whatever the role. Without a usable licence the agent refuses them with 402, and keeps answering every read, the audit log and the sign-in screen. It is a commercial gate, not a security boundary — Licence.

What needs a licence: adding to the pending changes, removing one or discarding them; applying, keeping, and restoring an earlier revision; Save configuration; every app action; reconcile; downloading or restoring a backup, and taking or restoring a snapshot; installing an agent update; the UPnP settings and removing a UPnP mapping; and everything on a fleet router, which needs the fleet feature in the licence as well.

What does not, although it needs the operator role or more: the planners — an app install, an inline IPS, an OpenVPN server, a certificate check — a traceroute, a packet capture, and checking for updates, because none of them changes the router. The admin pages — users, tokens, agent settings, Restart agent, the licence itself — restarting or shutting down the router, and its system images need no licence either.

What is hidden below admin#

Below the admin role, secrets in the router's configuration are hidden wherever the web interface shows it: private keys, pre-shared keys, passwords and other credentials, and values that are secrets because of where they sit — a container's environment values, SNMP community strings, the tokens monitoring services use. An SNMP community, for example, shows as Hidden to anyone below admin, with "Only an administrator can see or change a community." Values that only look like secrets stay readable: public keys, the SSH keys that may sign in to the router, GRE tunnel keys and BGP communities.

Two boundaries are drawn differently:

  • The pending changes are hidden below operator, not admin. The operator who typed a key sees it in the change they are preparing. An approver reviewing that change does not — the Users page's "does not see the secrets in them". The audit log's record of a change never holds the secret at all: it is blanked before the entry is written.
  • Download configuration is admin-only rather than hidden. The whole configuration with its secrets in it is a backup, and taking it off the router is not a read.

For automation: the guards#

Every route in the agent that needs a principal carries one of these guards:

GuardMeans
readOnlyAny authenticated principal.
confirmableThe approver role.
confirmedThe approver role and a usable licence.
writeableThe operator role.
adminOnlyThe admin role.
licensedThe operator role and a usable licence.

In this release that is 154 route patterns: 80 readOnly, 2 confirmable, 3 confirmed, 16 writeable, 15 licensed, 32 adminOnly, and six that take no authentication at all. GET /metrics is registered one way or the other depending on --metrics-public, and is counted once, in readOnly.

Thirty-three of those routes need a usable licence: the 15 licensed and the 3 confirmed; 6 adminOnly routes that add the licence check (the four backup and snapshot routes, installing an agent update, and the UPnP settings); one writeable route that does too (removing a UPnP mapping); and the 8 fleet routes, which check the licence and the fleet feature — two reads, four operator routes and two approver routes.

The configuration reads hidden below admin are GET /api/config, /api/config/commands, /api/wireguard, /api/vpn/ipsec, /api/vpn/openvpn, /api/pki, /api/drift, /api/daemons, /api/history/diff and the fleet configuration read. GET /api/staged is hidden below operator, and GET /api/audit never carries a secret, for any role. GET /api/config/raw is admin-only.

What is unauthenticated#

Six route patterns answer without a session or a token, and they are the whole of it — plus /metrics when the agent was started with --metrics-public:

RouteReturns
GET /health{"status":"ok","version":"…"}. The agent's version, and nothing else about the router.
GET /api/auth/statusWhether an account exists, whether TLS is on, whether the router is licensed, whether two-factor authentication is required, and single sign-on's label, sign-in path and whether the provider answers.
GET /api/oidc/statusSingle sign-on's label and sign-in path, whether the provider answers, and the redirect address. Not the provider's own address — for a self-hosted provider that host name is reconnaissance, so it is removed even from an error message.
GET /api/oidc/login, GET /api/oidc/callbackThe sign-on redirect and its return leg.
POST /api/auth/loginSigning in itself, behind three limits on failed attempts.
GET /metricsOnly when the agent was started with --metrics-public; otherwise it is readOnly.

What a refusal looks like#

An unauthenticated request gets 401 with a reason. A request from a principal whose role is too low gets 403 with "this action requires the operator role" (naming the role it needs), and an audit entry:

json
{"operation":"denied POST /api/stage","actor":"alice","role":"viewer","success":false,"error":"requires operator"}

An attempt to use a route your role does not reach is recorded with your name on it. A request the licence refuses gets 402, with the reason and the licence state in the body.

A browser session of an account that has no second factor while two-factor authentication is required gets 403 with totp_enrolment_required and enrol_at on every route except five: /api/auth/totp/begin, /api/auth/totp/confirm, /api/auth/me, /api/auth/password and /api/auth/logout. A single sign-on session is not held up, and neither is a token or the break-glass token — see Account.

See also#

Updated 2026-09-18 roles authorisation api