E-2: file the remaining work, three Phase 0 findings, and the parked Peti risk

E-2 is partially shipped (hub v0.81.0 + controller Part 1). Filing the rest so a
foundation with no UI cannot quietly become a sixth seam-built-but-never-wired.

  E-2   remaining: installer Case A/B, the offer + agent-side move, the degraded
        banner, the controller half of the signal, red-proofs E/F, live validation.
        Phase 0 INVERTED the emphasis: the installer has no drive-enrollment step,
        so the common case at install is system-drive-only and Part 3 (drive added
        later) is the PRIMARY path, not Case A.
  E-2a  the move needs a root-fenced wrapper -- the agent holds neither
        Datastore.Allocate at /storage nor Permissions.Modify, and its sudoers has
        no pvesm and no pveum. Use the guarded-wrapper pattern; do NOT widen the
        agent's PVE role.
  E-2b  NotifyStorageDisconnected/Reconnected are defined and called NOWHERE, so a
        drive going absent emits no event at all. Hub side is already plumbed, so
        wiring needs no hub change.
  E-2c  E-1 put the whole-guest backups on a drive POST /disks/eject will eject
        (RoleForStorage returns user-data for a local-dir on a non-system device).
        Guard the eject specifically -- reclassifying the drive RoleBackup would
        block legitimate ejects, since it is also the enrolled user-data drive.
  PETI  peti-felhom deliberately NOT migrated; drive failure there is offsite-only
        recovery. Accepted until the operator's reinstall; re-evaluate if that
        slips past ~2026-09-01.
This commit is contained in:
2026-07-29 08:01:42 +02:00
parent 1257014c2b
commit 2508788d38
+5 -1
View File
@@ -39,7 +39,11 @@ State: `BLOCKED` · `READY` · `WAITING-ON-OPERATOR` · `WATCHING`. Every row ha
| **F-REBOOT** | ~~A guest rebooted during its backup does not come back — shutdown completes, start never happens, no self-heal; 9m47s total appliance outage with every alarm silent~~ | **SHIPPED + PROVEN-LIVE** (agent v0.107.0, 2026-07-28) | — | 60 s guest-power watchdog; `onboot` is the deliberate-stop discriminator (already the stale-lock path's, and what `pve-guests` consults), retry bounded 3x/1m-2m-4m then escalates once. Live on demo-hp: **120 s unattended** vs the incident's 587 s with a human; Scenario B proven (an `onboot:0` guest left stopped) | — |
| **F-LEAK** | ~~A failed restore-test cannot destroy its own scratch guest (403 `VM.Allocate`); the 10-slot VMID band shrinks silently~~ | **SHIPPED + PROVEN-LIVE** (agent v0.110.0 + host-install v1.21.0, 2026-07-28) | — | **Three attempts, two refuted live.** (1) Pool adoption: `PUT /pools/{pool}` also needs `VM.Allocate` on the VM — membership cannot bootstrap its own authority. (2) Per-path `/vms/990000..990009` ACLs: work, but PVE's destroy calls `remove_vm_access` (`LXC.pm:906`) which deletes every ACL at `/vms/<vmid>`**consumed by the op it authorises**, one use per slot. (3) SHIPPED: 4th root-fenced exception, band enforced in sudoers **literally** (`pct destroy 99000[0-9] --purge`) + in code + at the caller; API destroy still tried first. Live: band PERMITTED, `9201`/`9100`/`9999`/`990010`/`1` REFUSED, and `pct start 990000` REFUSED too | — |
| **F-OBS** | ~~`deadapp-check` leaves NO positive observable on a default (info-level) box — "no alarms" was indistinguishable from "never ran"~~ | **SHIPPED + PROVEN-LIVE** (controller v0.180.0 + agent v0.109.0, 2026-07-28) | — | INFO summary every 20th scan carrying scans/evaluated/down. **Agent v0.109.0 fixes the same shape in the guest-power watchdog shipped hours earlier in v0.107.0** — it logged only at startup and when it acted, so its health could be read only from absence | — |
| **E-2** | Drive-role machinery around the moved vzdump target: backup-target role on `StoragePath`, wizard assignment (never by transport/`removable`), no automatic roles, stickiness, `felhom-host-install.sh` creating the target **+ its `FelhomAgentStore` ACL**, absent-target policy, retention/space on a shared drive, honest single-drive label, remaining fleet migration | **READY (M)** | — | Architecture change PROVEN by hand on both demo boxes 2026-07-28 (`runbooks/RUNBOOK-vzdump-target-move-2026-07-29.md`); this is the machinery around a proven thing. Flips matrix row 4 | CC |
| **E-2** | **IN PROGRESS 2026-07-29.** SHIPPED: hub v0.81.0 (`backup_target_absent`/`_restored` in allowedEventTypes + Hungarian customerMessages, 3 tests, red-proofed) and controller Part 1 (`StoragePath.BackupTarget` role + `SetBackupTarget`/`ClearBackupTarget`, 5 invariant tests, red-proof C). **REMAINING: Part 2 installer Case A/B, Part 3 the offer + the agent-side move, Part 4 the degraded banner, Part 5 the controller half of the signal, red-proofs E/F, live validation.** | **READY (M) — partially shipped** | — | **Phase 0 inverted the emphasis: the installer has NO drive-enrollment step (`/mnt/felhom-drives` is agent-installed at runtime), so at install time the common case is system-drive-only and Part 3 (drive added later) is the PRIMARY path, not Case A.** Scope + rationale: `runbooks/RUNBOOK-vzdump-target-move-2026-07-29.md` §7. Flips matrix row 4 | CC |
| **E-2a** | **The target move needs a root-fenced wrapper — the agent cannot do it.** Creating a PVE `dir` storage needs `Datastore.Allocate` at `/storage` and the ACL grant needs `Permissions.Modify`; the agent holds neither by design (blast-radius containment), and its sudoers is a literal-argument `Cmnd_Alias` model with **no `pvesm` and no `pveum`**. The established pattern is a guarded wrapper (`felhom-mkfs-guarded`, `felhom-pbs-apply` — the latter already has the exact `grant <id>` dual-grant verb E-2 needs) | READY (M) | blocks E-2 Part 3 | Add `felhom-backup-target-apply` (create-dir-storage with `is_mountpoint 1` + grant) + its literal sudoers rule + installer install step, then the agent endpoint. **Do NOT widen the agent's PVE role instead** | CC |
| **E-2b** | **`NotifyStorageDisconnected`/`NotifyStorageReconnected` are defined and called NOWHERE** — a drive going absent emits no event on any channel. Detection works (`SetDisconnected`, apps stopped, `[WARN]` log, UI badge); the notification is dead code. Verified against the gitignored-`cmd/` trap with a positive control | **READY (S) — real silence** | — | Fifth instance of seam-built-but-never-wired. Hub side is fully plumbed already (allowedEventTypes + Hungarian message + DefaultEnabledEvents), so wiring needs **no hub change**. A backup-target-only drive has no apps to stop → today it is entirely silent | CC |
| **E-2c** | **E-1 put the whole-guest backups on a drive the customer can self-serve eject.** `RoleForStorage` returns `RoleUserData` for a `local-dir` on a non-system device, so `POST /disks/eject` permits ejecting `/mnt/nvme-1tb` / `/mnt/hdd_1` — now the backup targets | READY (S) | — | Do NOT simply reclassify the drive `RoleBackup`: on both demo boxes it is ALSO the enrolled user-data drive, so that would block legitimate ejects (over-correction). Guard the eject specifically when the mount is the assigned backup target, and make it surface the consequence | CC |
| **PETI** | **`peti-felhom` deliberately NOT migrated.** Its whole-guest backup still shares a device with its guest, so a drive failure there is **offsite-only recovery** | **ACCEPTED RISK — parked** | operator's next visit (tester reinstalling from scratch) | **Accepted until the reinstall; re-evaluate if that slips past ~2026-09-01.** Do not migrate, do not touch | operator |
| **R-109** | **The DR recipe records no backup target.** It lists every storage's name/type/content but never which one holds the local archives — and each demo box now carries TWO `content=backup` dir storages, `felhom-backup` (live) and `local` (frozen 2026-07-28 archives) | READY (XS) | — | Add the resolved `BackupTarget()` to the host-half. Third recipe-completeness defect beside R-105/R-106 | CC |
| **R-89** | Retention as a per-customer **commercial** policy on the hub | READY (increment 2) | — | Policy object + reconciler → ep0 prune job; keep box tokens write-only | CC |
| **R-92** | Hub PBS-DR gauge is 0.1 GB-granular — small deltas unverifiable | READY (XS) | — | Widen precision when retention becomes customer-visible | CC |