1a1e42addb
Correction: campaign-2's "R1" (bind-mount restore needs root) was a SYMPTOM of an empty felhom pool -> agent lacked VM.Audit on 9201 -> restore-test's existing, correct bindMountOverrides never ran. Live-proven: pool healed (Part A) -> restore-test PASSES (neutralizes 2 binds, boot+running, clean teardown, 4m35s). Report rec #1 (make pct restore work on bind mounts) superseded. Fixes shipped: agent v0.74.0 (pool re-assertion), controller v0.103.0 (F-C2-1). Spike (SPIKE-dr-bindmount-source-2026-07-07): DR bring-up source options — PBS pct.conf.blob blocked by encryption under the non-root token; DR recipe lacks mpN; mp8/mp9 are STRUCTURAL CONSTANTS -> recommend a small known-constant override reusing bindMountOverrides. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
315 lines
20 KiB
Markdown
315 lines
20 KiB
Markdown
# CAMPAIGN 2 — DR primitives · self-update · escrow · storage · faults · endurance (2026-07-07)
|
||
|
||
> Unattended full-system validation on the demo node (felhom-pve / guest 9201), run during the day
|
||
> 2026-07-07 (07:20–14:11 CEST). **No production code changed.** Throwaway harness + evidence bundle
|
||
> live on DooPlex (`~/campaign2-nomercy/`); the committed artifacts are this document + the evidence
|
||
> snapshot under `documentation/tests/campaign2-evidence-2026-07-07/`.
|
||
>
|
||
> **Headline — read this first.** The raw tally looks alarming (≈45 FAIL) but is **misleading**: it
|
||
> decomposes into **one genuine, pilot-critical product finding** plus a **single dominant harness
|
||
> bug** that mis-scored most of P3–P9, plus a handful of smaller harness-calibration issues. The
|
||
> product, wherever it was actually exercised with a valid session or through the agent directly,
|
||
> was **healthy**: auth+CSRF enforced, backups sound, the PBS-verify + crash-recovery primitives
|
||
> work, fault recovery works, and **zero resource leaks** across a ~7 h run.
|
||
>
|
||
> **The one real finding (R1, headline):** the non-root host agent **cannot restore guest 9201**
|
||
> because the standard Felhom guest has **bind-mount mountpoints** (`mp8: /mnt/felhom-drives`,
|
||
> `mp9: …/bootstrap`), and `pct restore` refuses bind mounts unless root. This breaks **both** the
|
||
> product's own restore-verification primitive (`--selftest=restore-test`) **and** the offsite DR
|
||
> bring-up (`--selftest=bring-up -mode dr`). *A backup you cannot restore is not a backup* — and
|
||
> right now, non-root, we cannot restore the demo guest. This is exactly what "exercise the real
|
||
> primitives" was meant to surface.
|
||
|
||
---
|
||
|
||
## Run header
|
||
|
||
| Item | Value |
|
||
|---|---|
|
||
| Date | 2026-07-07, 07:20–14:11 CEST |
|
||
| Host / Guest | felhom-pve (192.168.0.162), node demo-felhom / guest 9201 (LAN 192.168.0.127) |
|
||
| Controller / Agent / Hub | **v0.102.0** (`cd0ebd2`) / **v0.73.0** (`e04b75e`) / v0.35.0 |
|
||
| Catalog | `2ebe082` (healthcheck-swept) |
|
||
| Auth mode | **ON** for the run (dashboard password + CSRF, via `settings.json`); restored **OFF** at P10 |
|
||
| Scratch band | agent-config band 990000–990009 (restore-test) + granted 9601–9620 (DR) — asserted empty at P0 |
|
||
| Restore points | **R0** `ct/9201/2026-07-06T22:34:15Z` verify PASS; **R1** taken by P7 |
|
||
| Soak / wallclock | 4 h soak cap / 8 h30 m wallclock (finished well inside) |
|
||
| Seed | 20260707 |
|
||
| Escrow R | provided at kickoff (chmod-600, never echoed) — but the escrow **blob was not locatable** by the harness, so the unwrap did not run (H5) |
|
||
| D1 envelopes | signed + verified through the **real** `authz.Verifier` pipeline at P0; **moved early** (after P3) to stay inside the 10:07Z TTL — see note below |
|
||
|
||
**D1-early adaptation (logged):** the envelopes had a 12 h TTL from the previous evening (expire
|
||
2026-07-07T10:07Z). The as-ordered D1 slot (after the 4 h soak) would have run past expiry. Because
|
||
0.73.1 is the **same source tree** as the deployed 0.73.0 (only the `-X main.version` string differs),
|
||
running P4–P9 on 0.73.1 would have been functionally identical, so D1 was moved to run right after P3.
|
||
(In the event D1 could not be delivered at all — see H3 — so this was moot; the agent stayed 0.73.0.)
|
||
|
||
---
|
||
|
||
## Result tally (raw, then triaged)
|
||
|
||
| Status | Raw |
|
||
|---|---|
|
||
| PASS | 15 |
|
||
| FAIL | 45 |
|
||
| SKIP | 3 |
|
||
| SOAK "breaks" | 170 (all one artifact) |
|
||
|
||
**After triage the 45 FAIL decompose as:**
|
||
- **1 genuine product finding** (R1/R2, surfaced by 3 independent agent-side tests: T-SELFRESTORE,
|
||
T-DR-BRINGUP, and contributing to the P9/DR chain).
|
||
- **~30 mis-scored by one harness bug** (H1, auth-session expiry — every controller POST 403'd on
|
||
CSRF and every GET returned login HTML from P3 onward).
|
||
- **4 = one harness bug** (H3, D1 injection had no `sqlite3` in the hub pod → self-update never
|
||
delivered; agent correctly stayed 0.73.0).
|
||
- **2 = stale dryrun journal entries** (H4).
|
||
- **1 = missing test fixture** (H5, escrow blob not locatable → SKIP mis-marked FAIL).
|
||
- The remainder are the soak (H2) and derived/dependent skips.
|
||
|
||
---
|
||
|
||
## THE genuine finding — R1 + R2 (pilot-critical)
|
||
|
||
### R1 — non-root `pct restore` of a bind-mount guest fails ("only possible for root")
|
||
- **Symptom (verbatim, 3 tests):**
|
||
`unable to restore CT <vmid> - restoring 'mp8' to bind mount is only possible for root`
|
||
- `T-SELFRESTORE` — `--selftest=restore-test` into scratch 990000 → **FAIL in 2.2 s**
|
||
(`evidence/T-SELFRESTORE/restore-test.out`, `pass:false`).
|
||
- `T-DR-BRINGUP` — `--selftest=bring-up -mode dr` into scratch 9601 → **FAIL**
|
||
(`evidence/T-DR-BRINGUP/bringup.out`).
|
||
- **Root cause (confirmed):** guest 9201's config carries **bind-mount mountpoints**:
|
||
```
|
||
mp8: /mnt/felhom-drives,mp=/mnt/felhom-drives # the enrolled-drives bind
|
||
mp9: /var/lib/felhom-agent/guests/9201/bootstrap,mp=/etc/felhom-bootstrap,ro=1
|
||
```
|
||
Proxmox `pct restore` refuses to restore a bind-mount entry unless it runs as **root**. The agent
|
||
runs **non-root** (`felhom-agent`), by design (de-privilege). So the restore of the *standard
|
||
Felhom guest topology* fails.
|
||
- **Blast radius:** this is not a scratch-guest quirk — it is the **restore path itself**. The
|
||
product's own "is this backup actually restorable?" primitive and the offsite guest-loss DR
|
||
bring-up **both fail on any guest that has a bind mount**, which the standard Felhom guest always
|
||
has (drives bind mp8 + bootstrap bind mp9). **This is the single most important thing to know
|
||
before pilot.**
|
||
- **Not fixed** (report-only). Candidate directions for the follow-up task (design decision, not
|
||
prescribed here): rewrite bind-mount `mpN` entries to a restorable form during restore (needs R2's
|
||
config-read), restore the bind mounts via a root-scoped helper (break-glass-style, narrow), or
|
||
restore the rootfs+managed volumes only and re-materialise binds post-restore. **Cross-check with
|
||
the offsite-WG DR design docs before choosing.**
|
||
|
||
### R2 — agent token is missing `VM.Audit` (and `VM.Allocate` on scratch) — links into R1
|
||
- `restore-test: could not read source config for mp overrides (restoring as-is)` …
|
||
`GET /nodes/demo-felhom/lxc/9201/config -> HTTP 403: permission denied at /vms/9201 (missing
|
||
privilege VM.Audit)`. Because the agent could not read the source config, it restored **as-is**
|
||
and hit R1. **R2 plausibly *enables* R1's fix**: with config-read the agent could rewrite `mp8`
|
||
before restoring.
|
||
- `DELETE /nodes/demo-felhom/lxc/990000 … missing privilege VM.Allocate` — scratch teardown 403'd;
|
||
the leaked scratch was **left for Recover**, which did reconcile it (`recover: leaked-scratch
|
||
entry resolved; guest already gone`) — so **no leak survived**, but teardown is not clean.
|
||
- **Live ACL confirmation:** `pveum user permissions felhom-agent@pve --path /vms/9201` shows only
|
||
`Datastore.Audit` + `Sys.Audit` — **not** `VM.Audit`. The `FelhomAgentGuest` role *does* carry
|
||
`VM.Audit`/`VM.Allocate`, but the grant on `/pool/felhom` is not resolving to `VM.Audit` at the
|
||
per-guest path the restore path checks. (Related to the A1 pool-audit thread.)
|
||
|
||
---
|
||
|
||
## Other real observations
|
||
|
||
- **F-C2-1 (kickoff, confirmed):** setting the dashboard `password_hash` in **`controller.yaml`**
|
||
does **not** take effect — the config loader runs `os.ExpandEnv` over the YAML, and a bcrypt hash
|
||
(`$2a$…`) contains `$…` sequences that get mangled to empty. Worked around by writing the hash to
|
||
**`settings.json`** (which is not env-expanded). Product-relevant: any operator putting a bcrypt
|
||
hash in `controller.yaml` gets silent auth-bypass. Report-only.
|
||
- **T-P9-DESTROY-RESTORE** ran (gated on R1-verify + healthy agent) and the local PBS restore of 9201
|
||
*itself* completed at the Proxmox level, but the harness's **post-restore verification** went
|
||
through the (dead-session) controller and mis-scored — see H1. The vzrestore of a *whole-guest*
|
||
PBS backup via `pct restore` on the **host** (P9 finale path) is a different code path from the
|
||
agent's non-root `--selftest` restore and did not hit R1; the destroy→restore of 9201 recovered
|
||
the guest (it is running and healthy at end-state).
|
||
|
||
---
|
||
|
||
## Strong positive results (real signal — do not lose these under the FAIL count)
|
||
|
||
### No resource leaks — the endurance headline (flight recorder, 351 samples over ~7 h)
|
||
| Metric | Start | End | Min | Max | Verdict |
|
||
|---|---|---|---|---|---|
|
||
| agent RSS (KB) | 22 116 | 19 936 | 17 924 | 22 792 | **stable, trended down** |
|
||
| agent fd | 79 | 54 | — | 83 | no leak |
|
||
| agent sockets | 70 | 46 | — | 74 | **no idle-socket leak** (the agentapi precedent is absent) |
|
||
| controller fd | 0* | 15 | 0* | 30 | no runaway (*0 = sampler pgrep miss) |
|
||
| controller latency | — | — | 0.008 s | 0.079 s | responsive all run |
|
||
|
||
The controller answered every flight-recorder probe in **8–79 ms** across the whole run; only ~10 of
|
||
351 samples were 502/000 (during **deliberate** restarts/reboots), all recovered. (The 341× `401`
|
||
is the flight recorder itself being unauthenticated — the controller *responded*, fast, it just
|
||
required auth.)
|
||
|
||
### What actually passed (valid-session or agent-direct — trustworthy verdicts)
|
||
- **P1:** all pages 200; **vaultwarden fresh-deploys healthy** (`_ENABLE_SMTP=false`, restarts=0 —
|
||
F1 fix re-confirmed); canary plant; lifecycle; sync-idempotent; and the **new auth+CSRF grant is
|
||
enforced** — `T-AUTH-1` (wrong-pw refused, unauth API blocked), `T-CSRF` (tokenless POST → 403,
|
||
tokened accepted), `T-AUTH-2` all **PASS**.
|
||
- **P2 backup suite:** `T-BAK-FULL` (DB dumps restorable + volume tars carry canaries bit-identical),
|
||
`T-BAK-API`, `T-BAK-SINGLEFLIGHT` (409), `T-BAK-PBS` (namespace-pinned) all **PASS**.
|
||
- **P5 restore primitives:** `T-PBS-VERIFY` **PASS** (`verify_state: ok`, 13 snapshots over 2
|
||
datastores, via the agent selftest — immune to the auth bug); `T-RESTORE-TEST-CRASH` **PASS** —
|
||
the journaled engine's **`Recover` reconciled a leaked scratch after a kill** (`leaked-scratch
|
||
entry resolved; guest already gone`). *Caveat:* because restore-test now fails fast on R1, the
|
||
crash-window was small — Recover is proven, but a full mid-restore crash was not deeply exercised.
|
||
- **P6 faults:** `T-FLT-KILL9` (controller SIGKILL → restart-policy recovery, API 200),
|
||
`T-FLT-CF` (cloudflared down → external down → recovered), `T-FLT-AGENT` (agent SIGKILL → systemd
|
||
restart, clean binds) all **PASS**.
|
||
- **P9-DR:** `T-DR-PBSVERIFY` **PASS** — an offsite snapshot exists and verifies over the tunnel.
|
||
|
||
---
|
||
|
||
## Harness bugs found (the real story behind the FAIL count — product NOT implicated)
|
||
|
||
- **H1 — auth-session expiry (DOMINANT).** The harness logs in **once** (P1) and never
|
||
re-authenticates. Over a multi-hour run the session cookie expired; from **P3 onward** every
|
||
state-changing controller POST returned `{"ok":false,"error":"CSRF token missing or invalid"}`
|
||
(403) and every canary GET returned login-page HTML (empty parse). Proof:
|
||
`evidence/T-STG-ENROLL/init1.txt` (CSRF-403), `evidence/T-RST-VW/restore_resp.txt` (302→login),
|
||
`canary_check.txt` (`got=` empty). **Mis-scored:** all `T-RST-*`, all `T-STG-*`,
|
||
`T-FLT-{DOCKERD,HUB-OUT,GITEA-OUT,CRASHLOOP,POWERLOSS}`, all `T-RBT-*`, `T-RES-*`, and the
|
||
post-restore checks of `T-P9-DESTROY-RESTORE`. **Fix for harness v-next:** `cc_ensure_auth` before
|
||
every phase's ops (re-login on any 401/302), and re-scrape the session-bound CSRF token after any
|
||
controller restart/reboot.
|
||
- **H2 — soak responsiveness probe (`cc_code /` expects 200; auth → 302→login).** 170/170 soak
|
||
iterations flagged `responsive(code=302 ms=45)` — the controller *was* responsive (45 ms), the
|
||
probe just wasn't authed. `T-SOAK-LOOP` FAIL is entirely this. Fix: probe an authed page (or
|
||
accept 302).
|
||
- **H3 — D1 injection has no `sqlite3` in the hub pod.** All 4 `T-D1-*` failed at the injection step
|
||
(`exec: "sqlite3": executable file not found in $PATH`). The self-update was **never delivered**,
|
||
so it was **not tested** (agent correctly stayed 0.73.0). The signed envelopes themselves were
|
||
valid (verified at P0 through the real `authz.Verifier`). Fix: inject via a hub API/endpoint or a
|
||
pod with sqlite, or bake the job in a way that survives a minimal image.
|
||
- **H4 — dryrun polluted the journal.** The pre-launch `run.sh dryrun` wrote synthetic-state FAILs
|
||
for `T-SEC-PROTECTED` and `T-REMOVE-REDEPLOY` to the **same** `journal.tsv`; the resume-aware
|
||
overnight run saw them terminal and **skipped re-running them**, preserving the stale FAILs. Fix:
|
||
fresh journal per run (or a dryrun-scoped journal).
|
||
- **H5 — escrow blob not locatable.** `T-DR-ESCROW` → `SKIP-reason: no escrow blob reachable
|
||
(local+hub)`. R was provided, but the harness looked in the wrong place (agent state dir + a hub
|
||
`/escrow` endpoint that isn't the real path), so the **crown-jewel unwrap + wrong-R red-proof did
|
||
not run**. Marked FAIL, really a SKIP. Fix: locate the actual escrow blob (hub admin path with the
|
||
operator key, or the real on-host escrow artifact).
|
||
- **H6 — flight-recorder probe unauthenticated** (401 for the run) and **`c_rss` sampler flaky**
|
||
(guest-side `pgrep` interpolation often empty). Cosmetic — latency/leak signal still usable.
|
||
|
||
---
|
||
|
||
## Restore-primitive evidence (the campaign's purpose — verdicts)
|
||
|
||
| Primitive | Verdict | Evidence |
|
||
|---|---|---|
|
||
| `--selftest=pbs-verify` | **WORKS** | `verify_state: ok`, 13 snapshots / 2 datastores |
|
||
| journaled `engine.Recover` (leaked-scratch reconcile) | **WORKS** | `T-RESTORE-TEST-CRASH` — reaped a leaked scratch after kill |
|
||
| `--selftest=restore-test` (restore→boot→verify→teardown) | **BROKEN on bind-mount guests (R1)** | fails in 2.2 s: `mp8 … only possible for root` |
|
||
| `--selftest=bring-up -mode dr` (offsite guest-loss DR) | **BROKEN on bind-mount guests (R1)** | fails: `mp8 … only possible for root` |
|
||
| `escrow.Consume(R)` unwrap (+ wrong-R red-proof) | **NOT RUN** (H5 — blob not located) | — |
|
||
| local whole-guest PBS destroy→restore (host `pct restore`, P9) | guest recovered | 9201 running + healthy at end-state |
|
||
|
||
---
|
||
|
||
## Delta vs Campaign 1
|
||
|
||
| Dimension | Campaign 1 (2026-07-06) | Campaign 2 (2026-07-07) |
|
||
|---|---|---|
|
||
| Auth | OFF (XFAIL) | **ON — enforcement proven** (T-AUTH/T-CSRF PASS) … but broke the harness mid-run (H1) |
|
||
| Restore primitives | not exercised | **exercised — surfaced R1** (the headline) |
|
||
| Resource leaks | not sampled | **sampled — none** (agent RSS/fd/sockets stable) |
|
||
| DR bring-up / escrow | not attempted | attempted — R1 blocks bring-up; escrow blocked by H5 |
|
||
| Self-update (D1) | shipped v0.70.0 (prior) | **not delivered** this run (H3) |
|
||
| Dominant mis-score cause | ~18 harness bugs (auth-off era) | **1 dominant harness bug** (auth-on session expiry) |
|
||
|
||
---
|
||
|
||
## What this campaign STILL does not prove
|
||
|
||
- **The async restore family end-to-end through the UI under load** — H1 killed the harness's restore
|
||
tests. (It *was* proven in the P1–P3 re-run 2026-07-06: 0.018 s internal / 0.235 s external,
|
||
canary bit-identical.)
|
||
- **Storage F2 lifecycle at scale** — all `T-STG-*` are H1 artifacts. (F2 eject/decommission were
|
||
proven live in the v0.73.0 acceptance 2026-07-06.)
|
||
- **True offsite guest-loss DR** — blocked by R1; the crown-jewel path did not complete.
|
||
- **Escrow recovery-code unwrap** — not run (H5).
|
||
- **Agent self-update** — not delivered (H3).
|
||
- True CGNAT; multi-customer hub scale; real hardware failure.
|
||
|
||
---
|
||
|
||
## End state (demo node healed)
|
||
|
||
- **Guest 9201 running + healthy**, controller v0.102.0 healthy, **25 apps up, 0 unhealthy**.
|
||
- **Agent v0.73.0 active** (self-update never applied — H3 — so no version drift; the 0.73.1 artifact
|
||
remains published in Gitea, dormant).
|
||
- **Auth restored OFF** (pre-campaign state; root page 200). *Note: the "dashboard public + unauth"
|
||
item remains a pre-existing known security topic, unchanged by this run.*
|
||
- **Notifications restored** to the exact pre-state (email `nagyfenyvesi.viktor@gmail.com`, 11 events,
|
||
cooldown 6 h) — the harness P10 SKIPped it (no P0 snapshot in its state dir), restored manually.
|
||
- **No residue:** 0 scratch guests in 990000–990009 / 9601–9620; no stray timers/rules.
|
||
- Controller auto-update was already `false` pre-campaign (unchanged).
|
||
|
||
## Follow-ups (for project-Claude / a future task — not done here)
|
||
|
||
1. **R1 — bind-mount restore** (pilot-blocking): make `--selftest=restore-test` + DR bring-up work on
|
||
a bind-mount guest under the non-root agent. Design decision — cross-check the DR docs.
|
||
2. **R2 — grant the agent `VM.Audit`** (+ `VM.Allocate` on the scratch band) so restore-test can read
|
||
source config and tear down cleanly; likely a prerequisite for R1's fix.
|
||
3. **Harness v-next:** H1 (re-auth per phase), H2 (authed soak probe), H3 (hub-native D1 injection),
|
||
H4 (fresh journal), H5 (real escrow-blob locate) — then a clean re-run of P3–P9 + D1 + escrow-DR.
|
||
4. **F-C2-1:** either make the controller config loader not `os.ExpandEnv` the `password_hash`, or
|
||
document that bcrypt hashes must live in `settings.json`.
|
||
|
||
---
|
||
|
||
# Root-cause correction (2026-07-07) — R1 was a symptom of an empty pool; restore-test was never broken
|
||
|
||
> This section ANNOTATES the report above (history preserved). Live diagnosis after publication
|
||
> proved the R1/R2 causation runs the OTHER way, and the "fix R1" recommendation is superseded.
|
||
|
||
**What R1 actually was.** The report named R1 ("non-root agent can't `pct restore` a bind-mount guest
|
||
— architectural, pilot-blocking") as the headline. That inverts symptom and cause. The real chain,
|
||
every link verified live:
|
||
|
||
1. `pvesh get /pools/felhom` → `members: []`. **The felhom pool was EMPTY.**
|
||
2. Pool-scoped ACL grants (`FelhomAgentGuest` @ `/pool/felhom`, carrying `VM.Audit`/`VM.Allocate`)
|
||
reach only pool MEMBERS. With 9201 not a member, the agent had only the `/`-base grant on
|
||
`/vms/9201` (`Datastore.Audit`/`SDN.Use`/`Sys.Audit`) — **no `VM.Audit`**.
|
||
3. `restore-test` calls `GuestConfig(9201)` to read the mp layout → **403 VM.Audit** → falls to the
|
||
"restore as-is" fallback.
|
||
4. Because the source config was never read, `bindMountOverrides` (`restoretest.go:334`) — **which
|
||
already exists and is correct** — never ran. Raw bind mounts reached PVE → `mp8 … only possible
|
||
for root`.
|
||
|
||
**So restore-test's bind-mount handling was never broken — it just never executed.** The role and the
|
||
ACL binding were both correct; the fault was **pool membership**.
|
||
|
||
**Why the pool went empty:** `pct restore --pool` sets membership only at CREATE; a restore OVER AN
|
||
EXISTING VMID (an earlier destroy-restore drill) does NOT re-apply it, and nothing re-added the guest
|
||
— so 9201 silently dropped out (the 07:41 `VM.Audit` 403 predates the 14:09 finale, consistent with
|
||
an earlier drill).
|
||
|
||
**Verified fix + live proof (2026-07-07):**
|
||
- **Part A (heal now):** `pveum pool modify felhom --vms 9201` → 9201 is a member; `pveum user
|
||
permissions felhom-agent@pve --path /vms/9201` now shows **`VM.Audit` + `VM.Allocate` + `VM.Backup`**.
|
||
- **Live restore-test then PASSED for the first time** (agent v0.74.0): `neutralizing source
|
||
bind-mount mountpoints … bind_mounts=2` → scratch 990000 restored + booted + **verified
|
||
boot+running** + **torn down clean (no `VM.Allocate` 403)** in 4m35s. This confirms **B3** (the
|
||
scratch-teardown 403 was a CASCADE of the failed bind-mount restore, not an independent gap) and
|
||
that restore-test needs **no** bind-mount code change.
|
||
- **Part B (stop the regression):** `felhom-agent v0.74.0` re-asserts pool membership after any
|
||
restore-with-`Pool` (`Client.PoolAddVMID` + a bring-up call, warn-not-fail). A destroy-restore no
|
||
longer silently drops membership.
|
||
|
||
**Corrected fix order + superseded recommendation:**
|
||
- The report's follow-up **#1 ("make `pct restore` work on bind-mount guests without root") is NOT
|
||
needed** for restore-test and is **superseded** — the existing override handling works once the
|
||
config read succeeds (i.e. once membership is present).
|
||
- (A) pool one-liner heals today; (B) v0.74.0 stops the recurrence; restore-test unchanged.
|
||
- DR `bring-up -mode dr` has a **separate, real** gap (it passes no overrides and its source guest is
|
||
gone) — scoped and resolved in `SPIKE-dr-bindmount-source-2026-07-07.md` (recommendation: a small
|
||
known-constant override reusing `bindMountOverrides`, since mp8/mp9 are structural constants).
|
||
- **F-C2-1** is fixed in `felhom-controller v0.103.0` (config loader no longer `os.ExpandEnv`-corrupts
|
||
a bcrypt hash; live-proven).
|