Wheelhouse docs

Add an operator#

You will end up with an account for one person, with one role, that they must change the password on at first sign-in. Give every person their own: the audit log records who did what, and it can only do that when "who" is a person rather than a shared admin.

Before you start#

  • The admin role.
  • A licence — creating an account is a write.
  • The person's name, and a decision about their role.

Step 1 — Choose the role#

Pick the lowest one that lets them do their job.

RoleGive it to
viewerAnyone who needs to see the router but not change it: a colleague diagnosing something, a monitoring account
operatorAnyone who changes configuration: stage, commit, commit-confirm, roll back, reconcile, install apps
adminAnyone who manages accounts, tokens, agent settings, the licence, power or boot images

Roles are enforced server-side on every route. Promoting later is one PATCH; starting someone at admin because it is easier is how a router ends up with four admins.

Step 2 — Create the account#

Administration → Users → + Add user.

bash
R=https://<router>:8443
T=wh_...                                    # an admin token
curl -sk -X POST -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"name":"jo","role":"operator","password":"a passphrase they will change"}' \
  "$R/api/admin/users"

The rules the agent enforces:

  • The name may use letters, digits, dot, underscore and hyphen, 1 to 64 characters. admin-token and anything beginning token: are reserved for the break-glass token and API tokens.
  • The role must be viewer, operator or admin.
  • The password must be non-empty and at most 1024 characters. There is no minimum length and no complexity rule, deliberately: an operator picks their own password, and a rule that pushes people toward Summer2026! buys nothing. What protects the login is the rate limiting and, when you turn it on, two-factor.
  • A name that already exists is refused with 409.

The new account is flagged must change password, so the person is made to replace whatever you set at their first sign-in.

Step 3 — Hand over the password out of band#

Not in the same channel you told them the router's address in.

Check it worked#

They can sign in, and were asked to change the password.

The audit log has the creation, attributed to you:

bash
curl -sk -H "Authorization: Bearer $T" "$R/api/audit?limit=20"

The entry's operation is create-user and its path carries the name and role.

Changing a role, or resetting a password#

bash
curl -sk -X PATCH -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"role":"viewer"}' "$R/api/admin/users/jo"

curl -sk -X PATCH -H "Authorization: Bearer $T" -H 'Content-Type: application/json' \
  -d '{"password":"a new one"}' "$R/api/admin/users/jo"

PATCH takes role, password, oidc_subject and email. The Users page does all four.

Changing a password signs out that account's other sessions.

Removing an account#

Administration → Users, the row's delete, or:

bash
curl -sk -X DELETE -H "Authorization: Bearer $T" "$R/api/admin/users/jo"

Do this when someone leaves. Also revoke any API tokens they issued — a token carries its own role and outlives the account that created it.

See also#


Checked against agent/admin.go · agent/authhttp.go · ui/src/pages/Users.tsx · docs/security.md

Updated 2026-09-02 accounts users roles