Wheelhouse docs

On-router, off-router, fleet#

The agent is one binary that talks to a router over that router's HTTP API. Where it runs is a deployment decision, and there are three answers. On the router is the product: the agent, the web UI and the router in one image. Off the router is the development path, unsupported, and it exists because the agent has no need to be on the box it manages. Fleet is one agent holding credentials for several routers, gated on a licence feature. Almost every support question that begins "the address in the documentation does not work" is really a question about which of these you are running.

The two install paths, side by side#

Wheelhouse OS applianceDeveloper install
How it arrivesThe bootable image, or the .deb on a routerA checkout, built or copied onto a host
Web UIhttps://<router>:8443https://127.0.0.1:8090
Binary/usr/bin/wheelhouse-agent/opt/wheelhouse/wheelhouse-agent
UI assets/usr/share/wheelhouse/ui/opt/wheelhouse/ui
systemd unitpackaging/wheelhouse-agent.service, installed by the packageinstall/wheelhouse-agent.service, copied by hand
Secrets/config/wheelhouse//etc/wheelhouse/
Agent state/config/wheelhouse/state.json/config/wheelhouse/state.json
Supportedyesno — development and lab use only

Anything in this documentation naming port 8090 or /etc/wheelhouse is the developer path. On an appliance, read 8443 and /config/wheelhouse (docs/deploy.md).

A. On the router — the product#

The .deb installs the agent, the UI, a systemd unit binding 0.0.0.0:8443 with a self-signed certificate, a first-boot unit that mints the agent's own router API key and pins the router's HTTPS API to loopback, and a console-banner timer that prints the web UI address above every login. The unit's ExecStart names every secret as a file, never on the command line, because ExecStart is world-readable through /proc — and the agent refuses to read a secret file that is group- or world-readable.

--api-url https://127.0.0.1
--api-key-file /config/wheelhouse/api-key
--admin-token-file /config/wheelhouse/admin-token
--license-key-file /config/wheelhouse/license-key
--addr 0.0.0.0:8443
--tls-self-signed
--ui-dir /usr/share/wheelhouse/ui
--data-dir /config/wheelhouse

The unit is also where the process is confined: NoNewPrivileges, ProtectSystem=strict with /config as the only writable path, PrivateTmp, the kernel-tunable and module protections, a MemoryMax of 200 MB and a CPUQuota of 300%. The memory ceiling is load-bearing rather than decorative — the agent buffers a request body before the rate limiter sees it, and one anonymous POST was measured taking it to 2.2 GB, so the kernel killing it and systemd restarting it beats the box going to swap.

Everything works in this mode. It is the only mode the documentation describes unless it says otherwise.

B. Off the router — development and lab only#

install/install.sh puts the binary and the UI under /opt/wheelhouse, keeps secrets in /etc/wheelhouse, and binds 127.0.0.1:8090. The agent points at a router somewhere else with --api-url and that router's API key. It is genuinely useful — zero-risk development against a lab router — and it is not the product.

bash
./install/install.sh --api-url https://192.0.2.1 --api-key "$(cat /path/to/key)"
journalctl -u wheelhouse-agent | grep "shown once"   # the generated admin password

Two things behave differently, and both say so rather than pretending:

  • App volume directories cannot be created. POST /api/apps/prepare answers on_router: false and hands back the exact mkdir -p command to run on the router instead (agent/apps.go, handleAppPrepare).
  • App health probes report nothing rather than something false. The agent has no route to the router's container bridge, so a failed probe would say nothing about the app. It reports healthy: null with not probed — this agent runs off-router (docs/apps.md).

install.sh keeps the previous binary beside the new one, health-checks after a restart and restores the old one if the check fails. That is a lab convenience, not how the product upgrades: on an appliance an agent upgrade is a .deb replacement and a full upgrade is an image change.

There is also --demo, which starts the agent with no router credentials at all. Every router read then fails until --api-url and --api-key are supplied. It exists so the process can be started for UI work; it is not a demonstration mode with fake data.

C. Fleet — one agent, several routers#

--fleet-config <file> names a JSON file of routers. Each entry holds an id, a label, a url and that router's API key (agent/fleet.go):

/config/wheelhouse/fleet.json
{"routers": [
  {"id": "lon-edge", "label": "London edge", "url": "https://192.0.2.1", "key": "..."},
  {"id": "ams-edge", "label": "Amsterdam edge", "url": "https://198.51.100.1", "key": "..."}
]}

Each entry is a router API key, which is root-equivalent on that router, so the file is a credential store and gets the same treatment as every other secret the agent reads: it must be mode 0600 or the agent refuses it. An entry with an empty id is refused because fleet routes are addressed by id, and a duplicate id is refused because it would silently replace the first client while both still appeared in the status list.

What fleet mode offers:

RouteWhat it does
GET /api/fleetEvery router's status, checked concurrently: online, version, or the error. A bad key answers HTTP 200 with an error field on this platform, so that case is treated as offline rather than as an online router with an unknown version.
GET /api/fleet/{id}/configThat router's configuration, redacted below admin
GET /api/fleet/{id}/versionThat router's version
POST /api/fleet/{id}/configureApply operations to that router — audited, naming the actor, the router id and the outcome

Fleet is a licence feature, not a second key: the routes are wrapped in requireFeature("fleet", …) and answer 402 with the fleet feature is not part of this licence's plan without it. The fleet file loads regardless of the licence; the routes are what is gated.

Choosing#

  • Buying and running a router: A, and nothing here applies to you.
  • Developing against a lab router, or driving a router you cannot install on: B, knowing it is unsupported.
  • Watching several routers from one place: A on each, plus C on one of them or on a separate host.

See also#

Checked against#

docs/deploy.md · agent/main.go · agent/fleet.go · agent/apps.go · packaging/wheelhouse-agent.service · install/wheelhouse-agent.service · install/install.sh · docs/apps.md · PLAN.md §5

Updated 2026-09-02 concepts deployment fleet