Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0162BnMpUXscPsUB1cU8Tr6K
24 KiB
TESTRUN — full-stack no-mercy campaign (2026-06-29)
Adversarial integration/chaos validation of everything shipped this session, on the live demo stack
(felhom-pve, guest 9201, USB drives felhom-usb/felhom-flash — expendable). No production code
changed. Destructive steps were always recovered; the stack ended at known-good parity (§B re-run).
Scope note (honest coverage): this run executed the headline + highest-value NEW tests with
full evidence and recovery — the controller-swap happy/rollback (the deferred Scenario D), and the
capstone (leaf-regeneration → channel-health detection → recovery) — plus baseline, multi-drive
re-propagation, and the swap negative controls. Behaviors already live-proven during this session's
implementation tasks (multi-drive concurrency + other-drive-survival; capability degraded detection;
channel-health unreachable/transient/recovered) are referenced, not re-ground exhaustively. Deferred
items (host reboot, full felhom-host-install.sh reinstall, exhaustive flap loops, DNS/construction
live-alert) are listed in Coverage gaps with rationale.
§B Baseline (oracle) — captured
| Item | Value |
|---|---|
| Versions | agent 0.45.0, controller 0.90.0, hub 0.20.0 |
| Leaf fp (agent) == pinned fp (controller.yaml) | 60b5974d…279d8245 == 60b5974d…279d8245 ✓ |
| Capabilities self-check | 45/45 ok, 0 degraded |
| Drives | felhom-usb + felhom-flash both state=attached, bound_under_parent=True |
| Host parent peer group | shared:1 (== root's — the §4 "doubling" still present; host not rebooted since make-private granted) |
| Guest propagation | guest sees both drives, master:51/master:49 (propagation alive) |
| Apps | nextcloud (on felhom-usb) + controller healthy |
| Channel-health | steady up (no transitions) |
Final parity re-run (end of campaign): identical — agent 0.45.0 / controller 0.90.0, leaf==pin
60b5974d…, capabilities 45/45, both drives bound, nextcloud+controller healthy, channel up (0 down
events in 90 s), dashboard banner clear. Stack restored to known-good.
§C Multi-drive — PASS
- Concurrency: both drives
bound_under_parent=True+state=attachedconcurrently throughout (baseline + every post-churn check). Guest mountinfo shows both with livemaster:propagation tags. - Guest reboot resilience (NEW):
pct reboot 9201→ controller healthy, both drives re-propagated (bound_under_parent=True, guest sees both), nextcloudUp (healthy), and 0drive ABSENTgate events (no flapping). The C1 pre-start hook fired (placeholder for the momentarily-absent bootstrap mount) as designed. - Other-drive-survival (the original headline bug — detach one, the other survives) was live-proven during the sudoers task (both drives Aktív concurrently; the gate RETURNED both without cross-impact) — referenced, not re-run here.
Deferred: host reboot (would prove the "doubling" self-corrects to its own peer group now that
make-private is granted) — needs a maintenance window on the non-expendable host; the §4 doubling is
latent/harmless meanwhile. Mid-write detach flap loops — referenced via the prior chaos campaigns.
§E Controller-swap — happy PASS; 1 FINDING on rollback
- Happy swap (real agent
POST /controller/swappipeline): 0.90.0 → 0.89.0. Evidence: thetee-written/etc/felhom-controller-imageis byte-exact (od -c:…controller:0.89.0\n, trailing newline); agent loggedcontroller swap requested→controller-swap: new controller healthy; container came up 0.89.0 Up (healthy). Swapped back 0.89.0 → 0.90.0 cleanly. The stdin-teewrite + the 5 narrow grants + bootstrap restart all work live. PASS. - Negative controls (scoping): as
felhom-agent,sudo -n -lofpct exec 9201 -- {bash -c id, rm -rf /tmp/x, docker rm -f felhom-controller, tee /etc/shadow}→ all DENIED (exit 1); the realtee /etc/felhom-controller-image→ permitted (0). PASS. - Rollback — FINDING (recorded, not fixed): swapped to a deliberately-bad image (alpine tagged
felhom-controller:9.9.9). Expected health-fail → rollback; instead the swap marked it healthy in ~4 s and did NOT roll back. Root cause: alpine has noHEALTHCHECK, so the swap's "running + no-healthcheck = healthy" rule (a deliberate feature for images that legitimately lack a healthcheck) passed on a transient "running" instant — then the container crash-loops (Restarting (0)). Severity: low for the real controller (its image has a healthcheck, so a broken real build would report unhealthy/never-running → rollback; the deterministic rollback path is unit-proven byTestControllerSwap_RollbackOnUnhealthy). Gap: a controller image that has no healthcheck AND crash-loops would be marked "done" by the point-in-time verify and not roll back. Suggested hardening (operator decision): the verify should re-check health over a few seconds / rejectRestartCount>0, so a fast start-then-exit can't pass. Recovered cleanly by swapping back to 0.90.0 (has a healthcheck → healthy); bad tag removed.
Deferred: a clean live rollback needs a present-but-healthcheck-failing controller image, which can't be manufactured without building one (out of scope — "no new builds"). Rollback logic is unit-proven; the live finding above is the more interesting result.
§F + §K Channel-health & CAPSTONE — PASS (the headline)
Capstone (§K): original-incident class → DETECTED in ~25 s
Reproduced the exact failure that started this work: with the controller running (channel seeded up),
moved the agent leaf+key aside and restarted the agent → it regenerated a new leaf
(60b5974d… → 911d703c…), so the agent serves a cert the controller doesn't pin. Agent stayed
active, capabilities 45/45 ok (leaf change doesn't affect capabilities).
Detection (the whole point):
[channel] agent channel DOWN (up→down:pin_mismatch): agentapi: GET /storage: … TLS pin mismatch …
Event pushed: agent_channel_pin_mismatch (error) — … the agent's leaf cert no longer matches … (HTTP 200)
dashboard banner: "A tárolókezelő ügynök tanúsítványa megváltozott"
DETECTED ~25 s after the regeneration (one channel cycle) — vs the original silent multi-day disk-UI outage. The operator alert fired with the correct event type + the dashboard pin-mismatch banner appeared.
Recovery (R1): restored the backed-up leaf+key → restart agent → serves 60b5974d… again →
[channel] agent channel recovered (was down:pin_mismatch) + Event pushed: agent_channel_recovered
- both drives
bound_under_parent=True+ banner cleared. ~one cycle after the restart.
§F other modes
- Unreachable / transient / recovered were live-proven during the v0.90.0 task (and the
recovered path again here): a clean
systemctl restart felhom-agent→ no alert (debounce);systemctl stop→transient down (unreachable, 1/2) — suppressedthenDOWN (up→down:unreachable)Event pushed: agent_channel_unreachable(HTTP 200) + Hungarian banner;start→ recovered. Referenced.
- 401 / DNS-misconfigured / construction — classification is unit-proven (
channelhealthtests). Live alert for these is harder to induce without a controller restart, which reseeds state (first-obs = seed, no alert) — see Coverage gaps.
§K verdict: the original-incident class is now DETECTED within a cycle by the channel-health
check (PASS). Prevention recommendation (still worth doing, operator decision): ship
--preserve-state-from (carry local-api.{crt,key} + local-tokens.log across a reinstall) +
loud-on-regenerate, to turn the now-detected-but-disruptive reinstall into a non-event rather
than a detect-then-R1-recover. Detection is the safety net; preservation is the fix.
Defects / findings (separated from EXPECTED outcomes)
| # | Finding | Severity | Repro |
|---|---|---|---|
| F1 | Swap verify passes a no-healthcheck image that crash-loops (transient "running" + no-healthcheck rule → false "done", no rollback) | low (real controller has a healthcheck) | tag alpine as felhom-controller:X.Y.Z, swap to it → marked healthy ~4 s, then Restarting (0) |
F1 RESOLVED 2026-06-29 (agent v0.47.0): the swap verify now reads {{.RestartCount}}
(running && rc>0 → not healthy) and requires a 3-poll stability dwell for a no-healthcheck image
(a real healthcheck is trusted immediately). Live re-test: the same alpine :9.9.9 swap now reaches
state: failed ("did not become healthy within timeout") and rolls back to 0.91.0 — no false
"done". The docker inspect -f * grant is unchanged (* spans the extended template, exit-0 confirmed).
See felhom-agent/REPORT.md + CHANGELOG v0.47.0.
EXPECTED (not defects): the capstone leaf regeneration → pin mismatch (the prevention fix was
never shipped — regeneration is expected; detection is the pass criterion, and it passed). The §4
host-side "doubling" persisting until a clean host reboot (expected; make-private is granted, applies
next fresh boot).
Coverage gaps (couldn't test safely / deferred — with rationale)
- Host reboot (§C) — would confirm the parent self-corrects to its own peer group; needs a maintenance window on the non-expendable host. Deferred.
- Full
felhom-host-install.shreinstall (§K) — reinstalls the agent on felhom-pve (non- expendable); the equivalent leaf-regeneration was exercised instead (same incident class, same detection, recoverable). The full reinstall is the operator's call in a window. - 401 / DNS / construction live alert (§F) — inducing these needs a controller-config change + restart, which reseeds the channel state (first-obs seeds silently, no alert). Classification is unit-proven; the dashboard would show the state, but the up→down notification for these isn't cleanly inducible live without an agent-side trick (pin/unreachable have one; 401 would need a token-store edit). Deferred as lower-value (unit-covered).
- Capability live scratch-drop → hub alert (§D) — the lxc-info-degraded path + drive flap was the
live incident the capability task already diagnosed/fixed; the hub
HostCapabilityCheckertransition is unit-proven. A live drop requires editing the certified sudoers (sudoers can't "override-to-deny" via a drop-in cleanly) — deferred to avoid mutating the freshly-certified allowlist for a unit-covered path. - Exhaustive flap/mid-write/3rd-drive loops (§C/§G adversarial) — referenced via the prior chaos campaigns (#1–#3, all FIXED); not re-run.
F2 CLOSED (2026-06-29) + prevention shipped
The testrun surfaced F2: a channel/host failure present at startup/reseed alerted only on a live
transition, so a born-down (e.g. a controller booting into pin_mismatch) was dashboard-only, no
operator email. Closed — controller v0.91.0 (alerted-flag rework) + hub v0.21.0 (checkers
seed only healthy; born-degraded/stale emits on first Check, cooldown dedups). Live-validated: a
controller restarted into a regenerated-leaf channel logged [channel] agent channel DOWN (unseeded->down:pin_mismatch) and pushed agent_channel_pin_mismatch (~60 s) — the capstone's
missing half (detection at startup). Prevention for the original incident also shipped: agent
v0.46.0 loud-WARNs a regenerated leaf (leaf REGENERATED — pins now INVALID), and
felhom-host-install.sh gained --preserve-state-from (keeps the leaf/pin stable across a reinstall)
- a populated-host guard (refuse to regenerate the leaf on a host with guests). The §K prevention
recommendation is now implemented; the full live reinstall is the only piece awaiting a supervised run.
(F1 swap-rollback
RestartCounthardening remains backlog.)
Coverage gaps — NOW RESOLVED (supervised follow-up, 2026-06-29)
Baselines: agent v0.48.0, controller v0.91.0, hub v0.22.1; leaf==pin 60b5974d…; caps 45/45; both
drives bound; channel up.
Phase 1 — capability→hub operator alert, LIVE — PASS (the path that never fired in the field)
Induced a minimal-blast-radius degrade: a scratch deny drop-in (/etc/sudoers.d/zz-felhom-testdeny,
!…pct exec [0-9]* -- tee /etc/felhom-controller-image, visudo-clean) + agent restart.
- Agent stayed up (serve-degraded):
capabilities self-check ok=44 total=45 degraded=1+capability DEGRADED … controllerswap-write … critical=true reason="sudo policy denied". - The box kept working — both drives
bound_under_parent=Truethroughout (lxc-info still granted). - Hub fired the alert (~24s):
Host capability: demo-felhom-01 ok → degraded (agent_capability_degraded); the saved event message is English and names the cap+feature: "agent privileged capability degraded — controllerswap-write (impairs: controller-swap / managed auto-update)" (severity warning). - Recover: removed the scratch deny +
visudo -cf /etc/sudoersOK + restart →ok=45/45→Host capability: demo-felhom-01 degraded → ok (agent_capability_recovered). - Note (not a defect): a literal operator email was not dispatched — the demo hub has no
operator email address configured (no "Operator email sent/failed" log →
processOperatorreturns before the send). In prod with the operator address set, the same path sends via FormatOperatorEmail + Resend (RESEND key IS injected). The alert path itself (probe→report→checker→event+dispatch) is proven.
Phase 2 — host-reboot "doubling" self-correction — 2 FINDINGS (drive recovery PASS)
Rebooted felhom-pve (uptime was 5d, no boot since the make-private grant). After boot:
- PASS — drive recovery + no false page: the boot ran
felhom-shared-parent.service; both drives re-propagated into the guest (bound_under_parent=True,master:tags alive), nextcloud + controller recovered healthy, leaf==pin MATCH60b5974d…, caps 45/45. Zero spurious alerts on the clean boot (drive-ABSENT 0, channel DOWN 0, hub capability/leaf/stale 0) — confirms F2 first-obs-seed doesn't false-page a normal reboot. - FINDING F2-a (doubling did NOT self-correct): the host parent
/mnt/felhom-drivesis stillshared:1(== root's peer group) and the drives are still doubled (each under parent 149 AND root 33) — identical to the pre-reboot state. The §4 hypothesis is falsified: the boot scriptfelhom-shared-parent.shruns as root (systemd), so it never needed the sudoersmake-privategrant — yet itsmake-private+make-sharedsequence does not isolate the self-bind from root'sshared:1. Harmless functionally (drives work), but the doubling persists across a clean boot. Severity: low (cosmetic/correctness; the agent'sAttachDrivealready converges binds). Repro: reboot the host, thengrep ' /mnt/felhom-drives ' /proc/self/mountinfo→shared:1. Root-cause not pursued (won't churn the live parent — would orphan the guest slave). A fix should assert the parent's group ≠ root's after the boot script and investigate the make-private timing/semantics for a subdir-of-root self-bind. - FINDING F2-b (reboot during backup → stuck guest): a vzdump backup was in flight at reboot
(snapshot @ 23:43:45); the reboot interrupted the
snapshot-deletecleanup, leaving a stalelock: snapshot-delete+ a danglingvzdumpsnapshot. Guest 9201 therefore failed to autostart (onboot:1can't start a locked CT: "Starting CT 9201 failed: CT is locked (snapshot-delete)"). Recovered withpct unlock 9201+pct start+pct delsnapshot 9201 vzdump. The "power-loss/reboot during a backup" edge — a host reboot should check for an in-flight backup, or boot-time recovery should clear a stale vzdump snapshot lock + auto-start. Severity: medium (an unattended host reboot during the daily backup window would leave the customer box down until manual unlock).
Phase 3 — host_leaf_changed LIVE on the wired hub (v0.22.1) — PASS (referenced)
Already live-proven against the wired v0.22.1 in the leaf-fp task: a leaf regen produced the controller
agent_channel_pin_mismatch AND (on restore) the hub host_leaf_changed
(82078fab…→60b5974d…) — two independent detections of the same re-key (controller-local +
hub-proactive). Not re-run here (would require another regen + R1). See
felhom.eu/REPORT.md (HostLeafChecker) + the §"Self-health arc COMPLETE" note in the channel-health
spike doc.
Coverage-gaps verdict
All three deferred gaps now exercised: Phase 1 fired live (event captured; email is operator-config-
gated, not a code defect); Phase 2 drive-recovery PASS but surfaced two findings (doubling NOT
self-corrected — §4 hypothesis falsified; reboot-during-backup stale lock); Phase 3 referenced. Scratch
artifacts removed (zz-felhom-testdeny; the dangling vzdump snapshot); /etc/sudoers re-validated.
Final parity (post follow-up) — known-good
agent v0.48.0 / controller v0.91.0 / hub v0.22.1; leaf==pin 60b5974d…; caps 45/45; both drives
bound_under_parent=True; channel up; nextcloud + controller healthy. (Persistent-but-harmless: the
§4 host-side doubling — finding F2-a.)
Findings closed — operator email + F2-a + F2-b (2026-06-30, agent v0.49.0 + hub config)
The two Phase-2 findings (F2-a, F2-b) and the Phase-1 operator-email gap are now fixed. Diagnose-
before-fix on both risky parts; both root causes confirmed live on felhom-pve before any code change.
Part A — operator email delivery (the previously-unproven hop) — PROVEN end-to-end
The Phase-1 "Note (not a defect)" gap is closed. Root cause was hub-config lacking
operator_email/operator_enabled (so Dispatcher.processOperator returned before the send). Fix =
config-only (manifests/hub.yaml ConfigMap: operator_email: admin@felhom.eu + operator_enabled: true),
hub pod restarted to reload; the mounted config now shows both (Resend key NOT printed).
- Operator path: scratch-denied the new
stalelock-unlockgrant → agentdegraded=1→[INFO] Host capability: demo-felhom-01 ok → degraded (agent_capability_degraded)→[INFO] Operator email sent for demo-felhom/agent_capability_degraded(this line prints ONLY after Resend returns 2xx — it never appeared before, because the path was gated off). Restored the grant →agent_capability_recovered. - Customer path:
POST /api/v1/notify(event_type:test) →{"sent":true}→[INFO] Notification email sent to nagyfenyvesi.viktor@gmail.com for demo-felhom/test(samesendEmail→ Resend). Both channels of the dispatcher now proven to the Resend send.
Part B — reboot-during-backup stale-lock recovery (F2-b) — FIXED + live-validated
agent v0.49.0: at startup, Server.RecoverStaleLockedGuests clears a stale vzdump lock — pct unlock
→ delete the dangling vzdump snapshot → start iff onboot and not already running — only when no
vzdump is genuinely in-flight (the invariant; fail-safe otherwise). New narrow grant FELHOM_STALELOCK = pct unlock [0-9]* + Critical capability stalelock-unlock (caps 45→46, all green). Two real issues
surfaced by live validation and fixed: PVE 9.x rejects ?running=1 (→ ?source=active); the LXC-start
WARNINGS: 1 nesting advisory false-failed the start (→ AllowWarnings).
- Live (B.3): induced a
snapshot-deletelock on scratch guest 9999 (onboot:1, stopped) → agent restart →stale-lock: clearing a stale backup lock→started CT after clearing the stale lock (onboot)→ lock CLEARED, guest running. Invariant guard proven against a REAL backup: a genuine agent-scheduled vzdump of 9201 was in-flight during the same restart →stale-lock: a vzdump backup is genuinely in-flight — leaving the lock (NOT stale)→ 9201's lock untouched; the real backup completed and self-cleared. (Note: PVE reserves the snapshot namevzdump, so a dangling one can't be hand-made — the delsnapshot-against-a-real-snapshot path is unit-tested and live-fires in the §D real reboot.)
Part C — host-side doubling (F2-a) — root cause FOUND; fix shipped; red-proof PASSED (§D)
Reproduced the exact pathology in a scratch dir at /mnt (where the live parent lives): the stale
sequence (bind + make-shared, no make-private) leaves the self-bind in root's shared:1 → sub-bind
count 2 (doubling); the correct sequence (bind → make-private → make-shared) gives an own group
(shared:525) → count 1. The live boot script was the pre-v0.36.6 body (no make-private).
Root cause: EnsureSharedParent gated the (re)install on the unit file only, so the v0.36.6
script-only fix never deployed to a host whose unit was current. Fix (agent v0.49.0): the new
sharedParentInstallStale compares both the script and the unit. Boot-time-only — it rewrites the
on-disk script; it does not churn the live mount (still guarded on !isHostMountpoint).
- Deploy fix proven: on the v0.49.0 startup the corrected script (with
make-private) was installed to/usr/local/sbin/felhom-shared-parent.sh(sudo log shows the install). The live mount was left untouched until the reboot (/mnt/felhom-drivesstillshared:1, doubled — by design). - RED-PROOF PASSED (§D, two reboots):
/mnt/felhom-drivescame up asshared:94— its OWN peer group, NOT root'sshared:1— and each drive has exactly 1 host bind (doubling GONE; was 2 each). The guest still sees both drives (bound_under_parent=true; apps all healthy). Consistent across both reboots. (Separate, pre-existing, harmless observation: the GUEST shows 2 mounts per drive — two parent mounts in the guest namespace, same/dev/sdX1 /felhom-datasource,master:slaved correctly; NOT a regression of this fix, which only corrected the host parent's peer group. Worth a future look.)
§D — supervised host reboot — DONE (PASS)
Run in a supervised window (user-authorized). Two reboots:
- Reboot mid-backup (caught the
backup/tar phase): PVE auto-cleared thebackuplock and started 9201 normally (no stuck symptom in that phase); the agent recovery saw no lock → correctly skipped. The C red-proof passed (parentshared:94, no doubling). A realvzdumpsnapshot remained. - Reboot with the EXACT F2-b state (induced
lock: snapshot-delete+ the real danglingvzdumpsnapshot from reboot 1): pve-guests FAILED to start the locked CT — "starting CT 9201: CT is locked (snapshot-delete)" → "Starting CT 9201 failed" — the genuine stuck symptom. The agent then auto-recovered:clearing a stale backup lock(lock=snapshot-delete) →removed dangling vzdump snapshot(the real delsnapshot path) →started CT after clearing the stale lock (onboot). 9201 running, lock cleared, snapshot deleted, no error (the?source=active+AllowWarningsfixes held). Zero spurious operator pages across both reboots (no host_down/stale/capability/leaf events — only the controller's benign notification-prefs re-register). The interrupted-backup temp.datwas cleaned.
Final parity (post-fix, post-§D) — known-good
agent v0.49.0 / controller v0.91.0 / hub v0.22.1 (+ operator-email hub-config); leaf==pin 60b5974d…;
caps 46/46; /mnt/felhom-drives is its own peer group shared:94 with no doubling (1 bind
each); both drives bound + 12 healthy containers; scratch guest 9999 back to baseline; the scratch deny
removed + /etc/sudoers valid; 9201 lock cleared + dangling snapshot deleted. Both findings F2-a and
F2-b are now CLOSED and live-validated. No secrets recorded (tokens read out-of-band, never printed;
leaf fps + the operator email are not secret).