hub v0.85.0 — Network card: a host's addresses are visible at last

Pairs with agent v0.119.0 and is useless without it.

A managed box's LAN IP was not shown anywhere in the hub, because nothing
reported it — the host report carried no address of any kind. The only IP
reachable from the UI at all was the WireGuard one, on /offsite's peer table
keyed by pubkey, so an operator could go peer->host and never host->peer, which
is the direction anyone actually asks in.

The host page grows a Network card: every routable address the box holds, one row
per (interface, address), plus a WireGuard row. On demo-felhom that is vmbr0
192.168.0.162/24 and tailscale0 100.70.170.35/32 — with the PVE web console at
https://<the LAN address>:8006, the thing the operator wanted and could not get.

WireGuard is rendered as TWO facts, deliberately. WGAssignedIP is the hub's own
allocation (wg_peers, authoritative desired state); WGConfirmed is whether the box
reports actually holding it. Showing the allocation alone would make a peer that
was never applied look healthy — the same shape as reading a timestamp that
records an attempt as if it recorded a result.

The split is keyed on the ALLOCATION, not the interface name: wg-felhom is the
agent's current unit name, and a UI keyed on that string would silently
mis-render the day it changes.

An old agent renders UNKNOWN, never "no addresses". Below agent 0.119.0 the field
is absent from the wire, and an absent signal is not a negative result — the page
says so and names the version needed. Rendering an empty list there would have
stated something false about the host.

No new store table and no new ingest path: the report is already stored opaquely
and GetWGPeerForHost already existed with no UI consumer. This is parse + render.

The report fixture in the tests is the REAL wire — the addresses block copied out
of `felhom-agent --selftest=hub` on demo-felhom running 0.119.0.

Tests 559 -> 566; four red-proofs (inert view-model, unconditional confirmation,
the old-agent branch, and the drift case) each run, observed failing, reverted.
This commit is contained in:
2026-07-31 08:49:44 +02:00
parent b4edc087fa
commit e07d90f0f4
6 changed files with 438 additions and 0 deletions
+42
View File
@@ -1,3 +1,45 @@
## v0.85.0 — Network card: a host's addresses are visible at last (2026-07-31)
**Pairs with agent v0.119.0 and is useless without it** — the agent is what reports the addresses.
**A managed box's LAN IP was not shown anywhere in the hub, because nothing reported it.** The host
report carried no address of any kind. The only IP reachable from the UI at all was the WireGuard
one, on `/offsite`'s peer table keyed by pubkey — so an operator could go peer→host and never
host→peer, which is the direction anyone actually asks in.
**The host page grows a `Network` card:** every routable address the box holds, one row per
(interface, address), plus a WireGuard row. On demo-felhom that is `vmbr0 192.168.0.162/24` and
`tailscale0 100.70.170.35/32` — with the PVE web console reachable at
`https://<the LAN address>:8006`, which is the thing the operator wanted and could not get.
**WireGuard is rendered as TWO facts, deliberately.** `WGAssignedIP` is the hub's own allocation
(`wg_peers` — desired state, authoritative) and `WGConfirmed` is whether the box reports actually
holding it. Showing the allocation alone would make a peer that was never applied look healthy —
the same shape as reading a timestamp that records an *attempt* as if it recorded a *result*. A
mismatch renders `not confirmed by the box`; there is a test for exactly that case, and a red-proof
that pins it (hard-wiring `WGConfirmed = true` turns it red).
**The split is keyed on the ALLOCATION, not on the interface name.** `wg-felhom` is the agent's
current unit name; a UI keyed on that string would silently mis-render the day it changes. Comparing
the reported address against the hub's allocated one uses the identity that survives a rename.
**An old agent renders UNKNOWN, never "no addresses".** Below agent `0.119.0` the field is absent
from the wire, and an absent signal is not a negative result — the page says *"this host's agent does
not report its addresses — they are unknown, not absent"* and names the version needed. Rendering an
empty list there would have stated something false about the host. Red-proofed: deleting the branch
makes the page claim the host has no routable address.
**No new store table and no new ingest path** — the report is already stored opaquely, and
`GetWGPeerForHost` already existed with no UI consumer. This is parse + render.
Files: `hub/internal/web/hosts.go` (`parseHostAddresses`, `hostNetworkView`, `hostNetwork`,
`hostDetailData`), `hub/internal/web/templates/host_detail_body.html`,
`hub/internal/api/testdata/host-report.golden.json` (the cross-repo contract, moved in lockstep with
the agent's copy).
Tests 559 → 566; four red-proofs (the inert view-model, unconditional confirmation, the old-agent
branch, and the report fixture being the REAL wire from `--selftest=hub`) each run, observed failing,
and reverted.
## v0.84.0 — Break-glass console credential on the host page (2026-07-31)
**The credential existed and was not reachable when it was wanted.** Every Felhom-installed box has