Wheelhouse docs

Locked out of the UI#

You will end up signed in again. This page is the order to try things in, from least destructive to most, and it assumes you have physical or serial access to the machine — because for most of these, you do.

Work out which of these you have before you start, because they need different answers.

What is happeningGo to
The login page loads, your password does not workA password you cannot remember
The login page loads, you have the password, the second factor is goneA second factor you lost
Nothing loads at all on port 8443The port does not answer
You are signed in but every change is refused with 402Not a lockout: no licence. Licence.
You are signed in but every change is refused with 403Not a lockout: your role. Read plane, write plane, admin plane.

Before you start#

The console. Physical keyboard and monitor, a serial connection, or the VM console. Working from the console covers getting a shell there. Almost nothing on this page can be done over the network, which is the point of it.

A password you cannot remember#

If the router has never been signed into#

The agent logs a one-time generated password the first time it creates the admin account, and writes it to /config/wheelhouse/initial-password, mode 0600. The console banner shows it until the password is changed, and the file is removed the moment that account's password changes.

bash
sudo cat /config/wheelhouse/initial-password
journalctl -u wheelhouse-agent | grep "shown once"

If you have a break-glass token#

--admin-token-file names a file holding a token that authenticates as admin without an account. The shipped unit points it at /config/wheelhouse/admin-token.

bash
R=https://127.0.0.1:8443
T=$(sudo cat /config/wheelhouse/admin-token)
curl -sk -H "Authorization: Bearer $T" "$R/api/auth/me"

With that you can reset your own account's password through the admin API:

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

If you have another admin account#

Sign in with it and reset the password from Administration → Users. That is the reason to give every person their own account — Add an operator.

Last resort: edit the store#

With the agent stopped, state.json is a JSON file you can edit. Two versions of this, in increasing order of damage.

Remove one account, so the agent no longer knows it — you then create it again from another admin account, or let the bootstrap run.

bash
sudo systemctl stop wheelhouse-agent
sudo cp -a /config/wheelhouse/state.json /config/wheelhouse/state.json.bak
sudoedit /config/wheelhouse/state.json          # delete the object from "users"
sudo systemctl start wheelhouse-agent

Start over completely, which drops every account, token, session and setting:

bash
sudo systemctl stop wheelhouse-agent
sudo mv /config/wheelhouse/state.json /config/wheelhouse/state.json.old
sudo systemctl start wheelhouse-agent
journalctl -u wheelhouse-agent | grep "shown once"

The agent bootstraps a fresh admin account and logs its password once. The audit log in audit.jsonl is untouched by this, and the router's configuration is untouched entirely — that lives in config.boot, not here.

A second factor you lost#

There is no admin reset and there are no recovery codes. That is a real gap, not an oversight you can work around from the UI. Know it before you make TOTP mandatory for a team.

The way back is the console, the agent stopped, and state.json edited by hand: find the account's object and remove totp_enabled, totp_secret and totp_pending.

bash
sudo systemctl stop wheelhouse-agent
sudo cp -a /config/wheelhouse/state.json /config/wheelhouse/state.json.bak
sudoedit /config/wheelhouse/state.json
sudo systemctl start wheelhouse-agent

Then sign in with the password and enrol again.

If require_totp is on in agent settings, everybody needs a second factor, so do this for the account you are recovering and then decide whether that setting is still the right one for a team without recovery codes.

The port does not answer#

Work down this list. Each step tells you whether to stop.

1. Is the agent running?

bash
systemctl status wheelhouse-agent
journalctl -u wheelhouse-agent -n 50
curl -sk https://127.0.0.1:8443/health

{"status":"ok"} from loopback means the agent is fine and the problem is the network path. Go to step 3.

2. If it will not start, the journal names the reason. The three that actually happen:

In the logFix
is mode 644; it must not be readable by group or othersudo chmod 600 that file. The agent refuses to read a loose secret.
Missing --api-url or --api-keyThe unit lost its flags, or /config/wheelhouse/api-key is gone. Restore it from a backup.
A TLS certificate it cannot readsudo ls -l /config/wheelhouse/agent.*. Remove them and restart to have a self-signed pair regenerated.

3. Is the address what you think it is?

bash
ip -4 addr

The console banner prints one Web UI: line per addressed interface. If it says "waiting for an address on any interface", the machine has not got one.

4. Is the firewall in the way?

This is the common one, and it is usually a rule you just committed.

bash
show configuration commands | match firewall

If a change you made shut the door, roll it back at the console:

bash
configure
rollback 1

Undo the last commit has the detail. If the change was committed with commit-confirm and you never confirmed it, the router has already rebooted into the previous configuration and this problem solved itself.

5. Is the agent bound where you expect?

The shipped unit passes --addr 0.0.0.0:8443, so the agent listens on every interface and only the firewall keeps the management UI off the WAN. If somebody edited the unit to bind one address, that is in /lib/systemd/system/wheelhouse-agent.service. Note that an image upgrade replaces that file.

Getting in when the network is gone entirely#

The console can do everything the UI can, because the UI stages commands the console can run. configure, then the set and delete lines, then commit and save. Every editor in the product shows you those lines precisely so this path exists.

If the router will not boot at all, or the new image broke it, GRUB lists both installed images — pick the previous one. Upgrade the whole system.

Check it worked#

Sign in through the browser, not just through curl. Then do the two things that stop this happening again:

  1. Create a break-glass token, as above, and keep it wherever you keep the router's other credentials.
  2. Take a backupTake a backup that is actually complete — so the next time this is twenty minutes rather than an afternoon.

See also#


Checked against docs/security.md · docs/deploy.md · agent/main.go · agent/store.go · agent/admin.go

Updated 2026-09-02 recovery lockout operations