Wheelhouse docs

System — history#

The History tab lists the router's archived configuration revisions, shows what each one changed, and restores one. It is the answer to "what changed my NAT rule, when, and can I put it back".

Revisions are the base platform's own mechanism: every commit archives the configuration as a numbered, gzipped file under /config/archive/. Revision 0 is the running configuration; revision 1 is the commit before it, and the numbers grow older from there. GET /api/history is show system commit, parsed into one row per line.

The table#

ColumnFromNotes
Revthe revision numberSorts numerically.
Committedthe timestamp the router recordedYYYY-MM-DD HH:MM:SS.
Bythe router account that committed
Made bythe transport the router recordedTranslated — see below.
Diff and RestoreRestore is offered on every revision except 0.

"Made by" is a translation#

The router records the transport that carried each commit, so a change this product made is filed under the base platform's API component. vyos-http-api is a true statement about plumbing and a useless answer to "who changed my NAT rule", so the column translates it — ui/src/pages/System.tsx:

The router recordedThe column shows
vyos-http-apiWheelhouse (in accent colour)
cliConsole
init, bootBoot
anything elseverbatim

The raw token is on the badge's tooltip: the router recorded this commit as "via vyos-http-api". Nothing is hidden; it is renamed and the original is one hover away.

The diff#

Diff opens a side panel titled Revision <n>, subtitled with its time, its committing account and the translated transport. The body is GET /api/history/diff?rev=<n>, which is show system commit diff <n>.

The panel is explicit about the direction, because a diff with no legend is a coin toss:

Lines marked + exist in this revision; lines marked exist in the running config.

Lines are coloured to match: additions green, removals red — and both - and the typographic are recognised, because some builds print the latter. A revision identical to the running configuration says so rather than showing an empty box.

Above the diff is a one-line summary the browser computes:

4 added, 2 removed · touches firewall, nat

The touched sections come from the [edit …] headers the router prints above changed lines; when the diff is in command form instead, the first word after set or delete is used. That summary is what you scan a restore for — does this touch the firewall — before reading the lines themselves.

GET /api/history/diff is wrapped in redactSecrets(RoleAdmin, …): below the admin role, secrets in the diff are redacted.

Restoring a revision#

Restore is deliberately two clicks. Restore arms the button; it turns into Confirm restore and stays armed for 20 seconds before disarming itself — arming a restore and then walking away should not leave a red button primed on a page someone else picks up.

The same confirmation is available from inside the diff panel, where the button reads "Confirm — load revision <n> and commit", which is exactly what happens.

POST /api/rollback with {"revision": n} loads /config/archive/config.boot.<n>.gz and commits it. There is no rollback operation in the router's HTTP API; the CLI command works by loading the archived file for that revision, and the agent does the same thing — agent/main.go, handleRollback. An empty body defaults to revision 1, which is "undo the last commit".

After a successful restore the page says what happened and offers the way back:

Restored revision 3. The router is now running that config.

Loading a revision commits, so the config that was running before this restore should now be revision 1. — [Review revision 1]

That is offered as open the diff, not as a second blind restore, because the page cannot prove what the router archived.

What a restore does not restore#

A revision is a configuration. It is not a backup of the router.

RestoredNot restored
Interfaces, firewall, NAT, DHCP, DNS, routingWheelhouse accounts and password hashes
Container declarations — so apps revert with everything elseTOTP enrolments, API tokens, sessions
Everything else under config.bootThe audit log
The licence key
Container images and volume contents

Those live in /config/wheelhouse/state.json and on disk, outside the configuration tree entirely. Backup and restore is the page for the rest.

An app that was rolled back to a revision naming an older image tag gets the tag, not the bytes: the image on disk is whatever was last pulled.

Doing it from the CLI or the API#

show system commit
show system commit diff 1
bash
# undo the last commit
curl -sk -X POST -H "Authorization: Bearer $T" \
  -H 'Content-Type: application/json' -d '{"revision":1}' \
  https://<router>:8443/api/rollback

Both the success and the failure are written to the audit log as rollback, with the archive file in the path field.

When the table is empty#

A router that has never been committed through shows the empty state with two commands and one fact: "Revision 0 is the running config." An empty history usually means a freshly installed box, or an archive directory that has been cleared.

See also#


Checked against ui/src/pages/System.tsx, agent/opmode.go, agent/main.go, agent/parse.go, agent/vyos.go, docs/backup-restore.md.

Updated 2026-09-02 manual system history rollback revisions