The commands Wheelhouse runs#
The agent is a separate program that drives the router over its documented HTTP API. It
does not link against the router's code, and it does not shell out. Everything it knows
comes from the reads on this page, and everything it changes goes through /configure.
If you are wondering what an idle Wheelhouse box is doing to your router, this is the whole list.
The router API endpoints it posts to#
| Endpoint | Used for | Client timeout |
|---|---|---|
/retrieve | showConfig, in JSON and in the native format | 30 s |
/show | every operational read | 30 s |
/configure | set, delete, commit, commit-confirm, confirm | 3 minutes |
/config-file | save and load | 3 minutes |
/container-image | pulling and deleting images | 15 minutes |
/image | add, delete, set_default, show system image | 15 minutes |
/traceroute | the diagnostics traceroute | 15 minutes |
/reboot, /poweroff | power actions | 15 minutes |
Three timeout tiers because one figure cannot be right for all of it. A read that hangs should not hold a dashboard for a minute; a commit on slow hardware legitimately runs past fifteen seconds, and timing out mid-commit leaves the caller unable to tell whether it landed; an image pull is bounded by the uplink, and the router keeps pulling after the agent hangs up.
Two encodings, and the reason is the router's own parser: most calls are form-encoded,
but its form parser requires a path field on every command — so operations that have
none (confirm) and fields it does not read (confirm_time) go as JSON instead.
Operational reads#
Every one of these is show <path> through /show. This is the complete list; nothing
else is read.
| Command | Behind |
|---|---|
show version | System, Dashboard, /api/version, the fleet health check |
show system uptime | System, Dashboard |
show system memory | System, Dashboard |
show system storage | System, Dashboard |
show system processes | Diagnostics |
show system image | System — boot images |
show system commit | System — the revision list |
show system commit diff <rev> | System — one revision's diff |
show system logging | warmed by the primer |
show hardware cpu | System, Dashboard |
show interfaces | Interfaces, the Dashboard's stream, the throughput sampler |
show interfaces <kind> <name> | Interfaces — per-interface detail, and the QoS page's qdisc |
show interfaces wireguard | WireGuard |
show interfaces openvpn <mode> | OpenVPN, once per configured mode |
show ip route | Routes |
show arp | Neighbours |
show lldp neighbors | High availability, LLDP |
show vrrp | High availability |
show wan-load-balance | High availability — the load balancer's own verdict |
show firewall statistics | Firewall — hit counters |
show nat source statistics, show nat destination statistics | NAT — hit counters |
show conntrack table ipv4 | Sessions |
show conntrack statistics | Sessions — per-CPU health |
show dhcp server leases | DHCP |
show dns forwarding statistics | DNS |
show dns dynamic status | Dynamic DNS |
show ntp | System |
show pki | Certificates |
show container | Apps |
show container image | Apps — is the image already here |
show container log <name> | One app's log |
show host lookup <host> | Diagnostics |
show log tail <n> | Logs |
show configuration commands | Every CLI panel, and /api/config/commands |
show vpn ipsec sa, show vpn ipsec connections | IPsec |
Re-derive it:
grep -ohE 'showText\([^,]*, [^)]*\)' agent/*.go | sort -uConfiguration subtrees it reads#
Through /retrieve with showConfig. The whole tree (an empty path) is read too — by
the reconcile diff, and by the Config tree page.
container protocols service dhcp-server
container name protocols bgp service dns
container network protocols ospf service dns dynamic
firewall protocols static service lldp
firewall group qos service ntp
firewall ipv4 system service suricata
high-availability pki service conntrack-sync
interfaces policy vpn ipsec
interfaces openvpn load-balancing nat
interfaces wireguard serviceRe-derive it:
grep -ohE 'configOf\(vyos, [^)]*\)|configTag\(vyos, [^)]*\)' agent/*.go | sort -uWhat it writes#
Only through /configure, and only as set and delete operations whose path the
operator has seen. The staging endpoint refuses anything else:
[{"op":"set","path":["service","dns","forwarding","cache-size","10000"]},
{"op":"delete","path":["nat","destination","rule","10"]}]For a set, the last path element is the value — the router's own convention, and
why an empty path element is refused where the request can still say which operation is
wrong.
Commit-confirm is the router's native mechanism: confirm_time on the commit, and a
later confirm operation to cancel it. Miss the window and the router reboots into the
previous configuration. That reboot is the rollback — it is how the configuration
backend implements the feature, and it is what makes a WAN or firewall change
survivable.
Rollback is not an operation at all. The router's HTTP API has no rollback; the CLI
command works by loading the archived configuration file for that revision, so
POST /api/rollback loads /config/archive/config.boot.<N>.gz through /config-file
and commits it.
What it costs the router#
Every call into the router's HTTP API forks a shell helper on the router, so the agent budgets calls, not intervals.
| Activity | Cost |
|---|---|
| The throughput sampler | one show interfaces every 5 s — one call for every interface |
| An open Dashboard | one uncached show interfaces every 2 s, for the stream |
| Operational reads | cached for 3 seconds, served stale-while-revalidating |
| Per-interface detail | cached for 20 seconds |
| Configuration reads | cached for an hour, and invalidated by the agent's own commits |
| The primer | one warm-up pass at start, and one configuration-only pass every 10 s and after each commit |
Idle, that is roughly a dozen calls a minute.
The router serialises its HTTP API: concurrent reads are no faster than sequential, and each read is in the region of 150 to 800 milliseconds. That is why the cache exists, why the primer does not stagger its calls — spacing them out would not lower the peak, only lengthen the warm-up — and why the primer does not refresh operational reads on a timer.
Reads that bypass the cache#
Three, and each for a reason:
| Read | Why |
|---|---|
The telemetry stream's show interfaces | A stream that replays a cached answer is not live. |
| The throughput sampler's counters | Rate arithmetic needs genuinely new counters. |
The reconcile diff's showConfig | The cache is invalidated by the agent's own commits only, so a change made over SSH — the thing drift detection exists to notice — would stay invisible for up to an hour. |
GET /api/apps/plan also reads show container image uncached, because it is asked
right after a pull and the three-second cache would answer with the list from before it.
Two cautions#
See also#
- Which page writes which subtree
- Endpoint index — the agent endpoint each read is behind.
- Prometheus metrics —
wheelhouse_vyos_*and the cache counters. - The desired-state file — the uncached diff.
- Ports and listeners — where the router's API listens.
- The agent and the router
- Every command is shown
- Built on VyOS
Checked against#
agent/vyos.go (post, postJSON, clientFor, ShowConfig,
ShowConfigCommands, ShowConfigUncached, ShowConfigRaw, Show, ShowTTL,
ShowUncached, Configure, ConfigureConfirm, Confirm, LoadConfig, SaveConfig,
AddContainerImage, Image, Traceroute, Reboot, Poweroff, routerBudget),
agent/cache.go (configTTL, opModeTTL, interfaceDetailTTL),
agent/primer.go,
agent/opmode.go,
agent/main.go (handleStream, handleRollback, archiveDir,
handleStage),
agent/apps.go (showTextUncached, imagePresent),
agent/metrics.go (History.collect),
agent/wan.go,
docs/api-cookbook.md,
docs/deploy.md "What the agent costs the router",
PLAN.md §3.