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 with 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. Fleet is one agent holding credentials for several routers, gated on a licence feature.
The two install paths, side by side#
| Wheelhouse OS appliance | Developer install | |
|---|---|---|
| How it arrives | The bootable image, or the .deb on a router | A checkout, built or copied onto a host |
| Web UI | https://<router>:8443 | https://127.0.0.1:8090 |
| Binary | /usr/bin/wheelhouse-agent | /opt/wheelhouse/wheelhouse-agent |
| UI assets | /usr/share/wheelhouse/ui | /opt/wheelhouse/ui |
| systemd unit | packaging/wheelhouse-agent.service, installed by the package | install/wheelhouse-agent.service, copied by hand |
| Secrets | /config/wheelhouse/ | /etc/wheelhouse/ |
| Agent state | /config/wheelhouse/state.json | /config/wheelhouse/state.json |
| Supported | yes | no — development and lab use only |
Anything naming port 8090 or /etc/wheelhouse is the developer path. On an appliance, read
8443 and /config/wheelhouse.
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/wheelhouseThe 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 matters:
the agent buffers a request body before the rate limiter sees it, so one large anonymous
POST can drive it to gigabytes. The kernel killing the process and systemd restarting it
beats the box going to swap.
Everything works in this mode.
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 for development against a lab router, and
it is not the product.
./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 passwordTwo things behave differently, and both say so:
- App volume directories cannot be created.
POST /api/apps/prepareanswerson_router: falseand hands back the exactmkdir -pcommand to run on the router instead. - 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: nullwith not probed — this agent runs off-router.
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 is there 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:
{"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:
| Route | What it does |
|---|---|
GET /api/fleet | Every 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}/config | That router's configuration, redacted below admin |
GET /api/fleet/{id}/version | That router's version |
POST /api/fleet/{id}/configure | Apply 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#
- What the agent asks the router — the API relationship itself.
- Apps are configuration — the two off-router differences.
- What the licence gates — the
fleetfeature. - The developer install — the lab path, step by step.
- Agent flags — every flag named above.
- The agent's files — what lives in each directory.