docs: v0.86.0 copy-without-reveal + the break-glass credential leg is now proven (PVE ticket minted)

This commit is contained in:
2026-07-31 09:24:24 +02:00
parent 80f473999e
commit 5825ceeabf
2 changed files with 25 additions and 1 deletions
+24
View File
@@ -8,6 +8,30 @@ neither half is useful alone. Live evidence:
> 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 |
@@ -118,7 +118,7 @@
| Multiple household users / per-person accounts | — | **MISSING** | — | Single dashboard password; acceptable for alpha → R-15 |
| WireGuard base infra always-on; OOB operator access (felhom-sshd, /32 peer) | agent v0.72, hub v0.35 | **IMPLEMENTED** | `SPIKE-oob-wg-operator-peer-2026-07-05`, `SPIKE-felhom-sshd-2026-07-05` | Mutual-repair desired-state arc not built → R-13 |
| The operator can see WHERE a managed host is — its LAN address and its WireGuard address, on the host page | agent **v0.119.0**, hub **v0.85.0** | **PROVEN-LIVE** (2026-07-31) | `audits/host-addresses-visible-2026-07-31.md` | Before this the LAN IP was **not reportable at all**`HostMetrics` carried no address of any kind — and the WG IP existed only in `/offsite`'s peer table keyed by pubkey (peer→host, never host→peer). New wire field `addresses[]`, one row per (interface, address); `IsGlobalUnicast()` is the whole filter, chosen by MEASURING both demo boxes, and it needs no veth/fwbr denylist because that plumbing carries no IP. Rendered live on both 0.119.0 hosts matching their `ip addr` ground truth exactly. **Two honesty properties carry the risk and are both red-proofed:** WireGuard shows the hub ALLOCATION and whether the box CONFIRMS holding it (allocation alone cannot tell a live tunnel from a peer never applied), and an agent below 0.119.0 renders **UNKNOWN, never "no addresses"** — proven live on `drill-r50-0a4f9a` (0.113.0). **Not covered:** a two-LAN-bridge box and a real WG drift, neither of which exists to observe |
| Break-glass management-plane recovery | agent v0.71, hub v0.84 | **IMPLEMENTED** | `runbooks/break-glass.md` | hub v0.84.0 adds an **operator-SESSION** retrieval path (host page → Console access → Reveal; `POST /hosts/{id}/reveal-recovery-credential`, CSRF-gated, writes a customer-visible `recovery_credential_revealed` event) beside the pre-existing **global-key** one (`GET /api/v1/admin/hosts/{id}/recovery-credential`), which is untouched and stays the route for when the hub UI itself is down. Still IMPLEMENTED, not PROVEN-LIVE: the UI path has not been exercised on a real lockout, and that the revealed password authenticates at `:8006` is operator-verified only. The vaulted secret is plaintext at rest → **R-133** |
| Break-glass management-plane recovery | agent v0.71, hub v0.84 | **IMPLEMENTED** | `runbooks/break-glass.md` | hub v0.84.0 adds an **operator-SESSION** retrieval path (host page → Console access → Reveal; `POST /hosts/{id}/reveal-recovery-credential`, CSRF-gated, writes a customer-visible `recovery_credential_revealed` event) beside the pre-existing **global-key** one (`GET /api/v1/admin/hosts/{id}/recovery-credential`), which is untouched and stays the route for when the hub UI itself is down. **The credential half is now PROVEN (2026-07-31):** the vaulted `demo-hp-bb76ea` password was verified against the box's own `/etc/shadow` hash AND minted a real PVE ticket — `POST /api2/json/access/ticket`**HTTP 200, `root@pam`, 367-char ticket**, the exact API the login form submits to. Still IMPLEMENTED rather than PROVEN-LIVE overall, because the path has not been exercised on a REAL lockout (SSH was available throughout). Discovered during that check: the card's Copy button shipped `disabled` until a Reveal and silently no-opped, leaving ANOTHER host's password in the clipboard — fixed in hub v0.86.0. The vaulted secret is plaintext at rest → **R-133** |
## F. Notifications & monitoring