# REPORT — Hub v0.85.0: the Network card (2026-07-31) **Overwritten** per the standing rule. Pairs with **agent v0.119.0** (see `felhom-agent/REPORT.md`); neither half is useful alone. Live evidence: `documentation/audits/host-addresses-visible-2026-07-31.md`. > The earlier v0.84.0 work this file previously described (break-glass Console access card) shipped, > deployed and validated in the same session; its durable record is `hub/CHANGELOG.md` v0.84.0 plus > `CONTEXT.md` ruling **S-4** and `documentation/backlog/` **R-133**. ## 0. Follow-up shipped the same session — hub v0.86.0 (Copy without reveal) **The operator hit a real defect in the v0.84.0 Console access card and it cost a login.** Copy was `disabled` until a Reveal, so clicking it did **nothing, silently**; the clipboard kept its previous contents — **another host's console password** — which was pasted into demo-hp's PVE login. It failed with `password check failed for user (root)`, which reads exactly like a stale credential and sends you diagnosing the wrong thing. Diagnosis, read-only first: the vaulted password **matched the box's `/etc/shadow` hash**, the account was unlocked and non-expiring, no TFA, no `pam_faillock` — and then it **minted a real PVE ticket** (`POST /api2/json/access/ticket` → HTTP 200, `root@pam`, 367-char ticket). So the credential was always good; only the clipboard was wrong. **That also closes the leg §7 of this report listed as operator-only** — the capability-map row now records it. **v0.86.0** makes Copy work without revealing (the *safer* default — the secret never renders, so it cannot be shoulder-surfed or screenshotted) and closes three silent-failure branches in one eight-line function: the disabled no-op, a missing `navigator.clipboard`, and an **ignored `writeText()` rejection** that let the operator believe a refused write had succeeded. The success message now names the host, because the clipboard is fleet-wide and "copied" alone cannot say for which box. Tests 566 → 568, both pinning the regression; red-proof: re-adding `disabled` reproduces the shipped bug. Deployed and live-verified (`Synced/Healthy`, image `:0.86.0`, button served without `disabled`, retrieval URL defined exactly once, all four outcome messages present). Commits `670ec35` + `80f4739`. ## 1. Baselines | Repo | `main` @ start of this half | Version before → after | |---|---|---| | felhom.eu (hub) | `b4edc087fa9a` | hub **v0.84.0 → v0.85.0** | | felhom-agent | `6b5dade4dcc5` | agent **0.118.1 → 0.119.0** | ## 2. The ask, and what it turned out to be *"Show the LAN and WireGuard IPs on the hosts page — it looks like it is not visible anywhere."* Nearly exact, and the two halves were very different jobs: - **WireGuard** — the hub already had it (`wg_peers.assigned_ip`, `GetWGPeerForHost`, **no UI consumer**). It was visible in exactly one place, `/offsite`'s peer table keyed by pubkey, so you could go peer→host and never host→peer. - **LAN** — **not renderable at all.** `HostReport`/`HostMetrics` carried no address of any kind, so this needed a cross-repo agent release, not an edit. Confirmed against the struct and against a live box before proposing anything. Operator chose: both together as one arc, with the `addresses[]` (iface + cidr) shape. ## 3. Files (hub half) - `hub/internal/web/hosts.go` — `parseHostAddresses`, `hostAddressView`, `hostNetworkView`, `hostNetwork`, `minAgentForAddresses`, `hostDetailData` +1 key - `hub/internal/web/templates/host_detail_body.html` — the Network card - `hub/internal/web/hosts_network_test.go` (**new**) - `hub/internal/api/testdata/host-report.golden.json` — the cross-repo contract, moved in lockstep - `hub/CHANGELOG.md`, `REUSE.md`, `manifests/hub.yaml` - `documentation/architecture/00-capability-map.md` (new row), `documentation/audits/host-addresses-visible-2026-07-31.md` (new) ## 4. Commits pushed to `main` | Repo | Commit | What | |---|---|---| | felhom-agent | `14642e3` | v0.119.0 — the host report carries the box's addresses | | felhom-agent | `4663df7` | REPORT | | felhom.eu | `e07d90f` | hub v0.85.0 — Network card | | felhom.eu | `37f7ff6` | manifests: hub 0.84.0 → 0.85.0 | ## 5. Tests + red-proofs (hub half) Suite **559 → 566** (+7). `go build ./... && go vet ./... && go test ./...` **rc=0**, run separately from the commit per standing rule 1. | Test | Asserts | Result | |---|---|---| | `TestNetwork_A_LANAddressRendered` | the LAN address + its interface reach the page | PASS | | `TestNetwork_B_WireGuardAllocatedAndConfirmed` | WG shown once, marked confirmed, not duplicated as a plain row | PASS | | `TestNetwork_C_AllocatedButBoxDoesNotHoldIt` | the drift case renders `not confirmed by the box` | PASS | | `TestNetwork_D_OldAgentSaysUnknownNotNone` | an old agent renders UNKNOWN, never "no addresses" | PASS | | `TestNetwork_E_NoPeerAllocated` | no peer → says so; the box's wg address is not silently dropped | PASS | | `TestNetwork_F_NoReportYet` | never-reported host says "waiting", not "no addresses" | PASS | | `TestParseHostAddresses_RealWire` | parses the real wire; empty/malformed degrade without panic | PASS | **The report fixture is the REAL wire** — the `addresses` block copied out of `felhom-agent --selftest=hub` on demo-felhom running 0.119.0. A hand-written shape would have proved only that the parser matches my own idea of the format. | # | Mutation | Observed | Reverted | |---|---|---|---| | A | `hostDetailData` ships an empty `hostNetworkView{}` (the inert-seam shape) | `FAIL … the LAN address is not on the host page — the feature shows nothing` | yes | | B | `WGConfirmed` set unconditionally from the allocation | `FAIL … an allocated-but-unheld peer renders as healthy — the drift is invisible` | yes | | D | delete the `AgentTooOld` branch | `FAIL … an old agent's silence is not explained` | yes | **Red-proof A took two attempts, and the first one proved nothing.** The initial mutation (`_ = network` inside a map literal) did not compile, so the "failure" was a build error, not a test result. Recorded because a red-proof that fails to build is indistinguishable from one that lands if you only read the exit code. ## 6. Deployed Image `gitea.dooplex.hu/admin/felhom-hub:0.85.0` built on DooPlex and pushed; `manifests/hub.yaml` bumped in git; ArgoCD hard-refreshed and **deliberately synced** (never `kubectl set image`). - ArgoCD app `felhom`: **Synced / Healthy** - `deploy/hub` image: **`gitea.dooplex.hu/admin/felhom-hub:0.85.0`** - Rollout: `deployment "hub" successfully rolled out` (rc=0) - Startup log: `2026/07/31 08:50:57 [INFO] felhom-hub 0.85.0 starting` **One sequencing note worth keeping:** the first sync fired before ArgoCD had re-read git, so it reported `Synced/Healthy` while still running 0.84.0 — a green pair that meant nothing. The fix is to wait until `.status.sync.revision` equals the pushed commit **before** trusting the sync, which the final run did. ## 7. Live validation **Method: endpoint-level** (no browser on DooPlex), against the deployed hub, cross-checked against `ip addr` read directly over SSH on both boxes. | Host | Agent | Network card, live | Matches ground truth? | |---|---|---|---| | `demo-felhom-8363b5` | 0.119.0 | WG **10.77.0.2 confirmed**; `vmbr0 192.168.0.162/24`, `tailscale0 100.70.170.35/32` + ULA | **yes, exactly** | | `demo-hp-bb76ea` | 0.119.0 | WG **10.77.0.3 confirmed**; `vmbr0 192.168.0.87/24`, `tailscale0 100.76.96.79/32` + ULA | **yes, exactly** | | `drill-r50-0a4f9a` | 0.113.0 | WG 10.77.0.4 **allocated**; *"agent (0.113.0) does not report its addresses — they are unknown, not absent. Needs 0.119.0 or newer."* | correct UNKNOWN rendering | | `sess-f-2670b5` | old | same UNKNOWN rendering | correct | Correctly absent from every card: `lo`, `vmbr9`'s `169.254.253.1` island literal, every `fe80::`. **A fifth host 404'd and it was not a bug.** `sess-g-e687c3` appeared in a host list I read 30 minutes earlier and returned 404 when I fetched its page; the concurrent session had deleted it as part of its scratch-customer cleanup. Chased to ground before reporting rather than filed as a defect. ## 8. Not yet validated / not covered - **A box with two LAN bridges** — handled by design (one row per interface) and unit-tested; no such box exists to observe. - **A real WireGuard drift** — the `not confirmed` branch is unit-tested and red-proofed but never seen in production, because no live peer is currently unapplied. - **IPv4-only / IPv6-only hosts** — every box here is dual-stacked via tailscale's ULA. ## 9. Teardown This run provisioned nothing — no guest, no VM, no scratch customer, no external resource. The only mutations are two in-place agent upgrades (previous binaries retained as `.bak-0.118.1` on both boxes), the Day-0 manifest agent vouch, and the hub image bump. ## 10. Backlog - **Opened: none.** **Closed: none.** - **R-115 / R-121 touched, not closed.** This release walked all three legs deliberately — deployed, **published** (round-trip verified) and **vouched** (read back) — so no new drift instance was created. The finding those rows record is that nothing *notices* when the legs diverge, which is unchanged. One small contribution: the Network card now surfaces "this host's agent is older than 0.119.0" per host, which is a per-field hint, not the installed-vs-vouched checker R-121 asks for. - **Concurrent session:** every path was staged explicitly; no foreign WIP was swept into any commit.