Wheelhouse docs

Log lines worth alerting on#

The agent logs structured key-value lines with Go's log/slog, to stderr, which under systemd means the journal. Every message string below is a literal from the source, so they are safe to match on.

bash
journalctl -u wheelhouse-agent -f
journalctl -u wheelhouse-agent -p warning --since -1h

--log-level takes debug, info, warn or error; anything else silently becomes info.

The request line#

One per request, and the only high-volume line:

level=INFO msg=request method=GET path=/api/interfaces status=200 bytes=8123 ms=2 ip=192.0.2.44

WARN at 4xx, ERROR at 5xx. The path here is the real path, not the collapsed metric label, so it carries object names. ip is the source address, subject to --trust-proxy.

Alert on the rate of status=5 rather than on any single line.

Page somebody#

MessageLevelMeans
panic serving requestERRORA handler crashed. The stack is on the line. The request got a 500; the agent kept running. This is a defect — capture it.
starting with an empty state: every account and API token is gone, and a new admin password followsERRORBoth state.json and its backup failed to parse. The agent started empty and made a fresh admin. Every account, token and session is gone. The unreadable files were moved aside with a timestamp.
the state file will not parse; trying the backup beside itERRORHalf of the above. Recovery may still succeed — look for the next line.
the backup state file will not parse eitherERRORThe other half.
unreadable state file kept for inspectionERRORNames the .corrupt-<timestamp> file that was preserved. Keep it.
listener stoppedERRORThe HTTP listener died with something other than a clean shutdown. The process then exits 1; systemd restarts it.
audit write failedERRORAn audit entry could not be appended or synced. Accountability is broken while this persists.
audit entry dropped: no audit log is openERRORWorse: the log is not open at all.
could not rotate the audit log / could not reopen the audit log after rotatingERRORThe log will grow past its cap, or stop recording.
reconcile loop commit failedERRORThe loop tried to commit and the router refused. Reality is drifting and nothing is closing the gap.
another agent is running against this data dirERRORTwo agents, one data directory. The second one exits 1. Usually a stray process after an upgrade.

Worth a ticket, not a page#

MessageLevelMeans
no licence key: configuration changes are disabled until one is entered (Administration → Licence)WARNAt startup. Every write will answer 402.
the licence key on file is not usableWARNThe stored key does not parse or does not verify.
licence refreshWARNOne refresh attempt failed. Nothing changed — the current token stays good until its expiry plus grace. Alert only if it repeats for days.
serving management UI over plain HTTP on a non-loopback addressWARNThe management UI is on the network in clear text. The line names the three fixes.
replacing the existing self-signed certificate: it was generated as a CA, which it should never have beenWARNAn older build's certificate is being replaced. Anyone who imported it into a trust store should remove it.
existing self-signed certificate has expired or expires within 30 days; replacing itWARNRoutine. Browsers will warn again after this.
reconcile loop cannot read its file / reconcile loop cannot diff against the routerWARNThe loop is doing nothing. GET /api/drift carries the same error.
desired-state file: …WARNAn advisory from the file, logged once per change, not once per pass.
secret file is missing; continuing without itWARNNames the flag and the path. Expected for admin-token and license-key; not expected for anything else.
could not refresh the state backupWARNThe filesystem refused a hard link. Writes are still atomic; there is just no second copy for a recovery.
could not sync the data dir / could not open the data dir to sync itWARNA rename may not be durable across a power cut.
audit_retention is larger than the agent serves from memory; older entries stay in the log fileWARNNames what you asked for and what you got.
clamping --reconcile-interval to the 10s floorWARNNames what you asked for.
graceful shutdown timed outWARNRequests were still in flight after 15 seconds at shutdown.
single sign-on callback failed / single sign-on could not completeWARNOne sign-on attempt failed. Alert on the rate, not the line — see the metrics below.
single sign-on group mapping would have demoted the only admin; not doing thatWARNA refusal that kept the router manageable. Fix the group mapping.
could not record token useWARNCosmetic: "last used" on an API token is stale.
could not write the initial password fileWARNThe console banner will not show the first-run password. It is still in the log line above.

Startup lines worth capturing#

MessageLevelCarries
listeningINFOaddr, tls, the router URL, and the agent version. The one line that says what this process actually is.
licenceINFOstate, plan, expires, routers.
single sign-on enabledINFOThe issuer, client id, redirect URI, provisioning and default role — because a mismatch between that redirect URI and what is registered at the provider is the failure an operator has to diagnose.
created first admin account — this password is shown onceWARNThe generated first-run password. Logged exactly once, and never again.
serving UIINFOThe --ui-dir in use.
reconcile loop watching a desired-state fileINFOThe file, the interval and the mode.
fleet config loadedINFOThe router count, and whether the licence includes fleet.
generated a self-signed certificate — browsers will warn until you add a security exception for this host or install a real certificateWARNThe certificate path and its expiry.
shutting down / stoppedINFOA clean SIGTERM. Their absence around a restart means it was not clean.

Startup failures#

These print and the process exits — see Exit codes.

either --demo or both --api-url and --api-key are required
cannot read secret file
single sign-on is misconfigured
--reconcile-mode must be stage or commit
--tls-cert and --tls-key must be given together
cannot create data dir
cannot open state
app catalog is invalid
bootstrap failed
cannot create self-signed certificate
cannot load fleet config

An agent that will not start and keeps restarting produces one of these every RestartSec=5. That pattern in the journal is the fastest diagnosis there is.

Prefer metrics for rates#

A log line is a good alert for a fault; it is a poor one for a rate. Use the metrics for these:

QuestionSeries
Is the router reachable now?wheelhouse_router_reachable, wheelhouse_router_last_success_seconds
Is the agent up?wheelhouse_up, and the absence of a scrape
Are requests failing?wheelhouse_http_requests_total{status=~"5.."}
Is the router slow?wheelhouse_vyos_request_duration_seconds
Has the cache stopped hitting?wheelhouse_cache_reads_total{result="miss"}
Has the router drifted from its file?wheelhouse_desired_drift
Is somebody being refused at sign-on?wheelhouse_oidc_assertions_total{outcome="refused"}

The audit log is not the journal#

Who did what is in audit.jsonl, not here. A refused request appears in both: as a WARN request line with status=403, and as an audit entry whose operation is denied <METHOD> <path>. The audit entry is the one that names the actor.

The router's own system log is a third thing again, readable at GET /api/log and on the Logs page.

Other programs#

ProgramWhere it logs
The installer, the seed finder/var/log/wheelhouse-install.log. Under --answers, also stderr.
First bootThe journal, plus logger -t wheelhouse for its one success line.
The console bannerNothing. It writes /etc/issue and is quiet.
bash
journalctl -t wheelhouse                  # first boot's own tag
sudo tail -f /var/log/wheelhouse-install.log

See also#

Checked against#

agent/main.go (newLogger, runDaemon, bootstrapAdmin, loadSecretFiles), agent/security.go (observe, recoverPanics), agent/store.go (load, flush, syncDir, auditLog), agent/license.go (loadLicenseAtStart, runLicenseRefresher), agent/tls.go (ensureSelfSignedCert), agent/desired.go (reconciler.Run, pass, logWarnings), agent/oidc.go, agent/authhttp.go (touchToken), agent/metrics.go, packaging/firstboot.sh, packaging/wheelhouse-install, packaging/wheelhouse-autoinstall, docs/deploy.md "Observability".

Updated 2026-09-02 logging alerting journal operations