The skip-if-mounted optimization defeated the actual root cause: felhom-usb is mounted now but its unit is `disabled`, so a host reboot would not auto-mount it. ReassertEnrolledMounts now skips ONLY the durable steady state (mounted AND enabled) via the pure shouldReassertMount; a mounted-but-disabled unit is re-asserted so enable --now re-creates the wants-symlink. Enabled-state read by privilege-free Lstat of the multi-user.target.wants symlink (unitEnabled) — no systemctl is-enabled subprocess, no new sudoers entry. Tests: TestShouldReassertMount (4 combos), TestUnitEnabled (wants-symlink). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.6 KiB
REPORT — felhom-agent v0.37.0 (host-reboot remount by durable-id/UUID)
TASK — Close out the reboot story (agent half: Task A + Task A2).
Task A — host-reboot remount resolves by filesystem UUID, never a cached node
Diagnose-first (root cause, pinned LIVE on felhom-pve)
On a host reboot the kernel re-enumerates block devices; a drive can move node (felhom-usb
/dev/sdb→/dev/sdc). Two things could leave an enrolled drive unmounted after a reboot:
- A systemd
.mountunit leftdisabledby a prior detach (Unmount= stop + disable) never gets amulti-user.target.wantssymlink, so it does not auto-mount at boot — and nothing in the agent re-asserted it at startup. - Any remount that trusted a remembered
/dev/sdXnode would target the WRONG device after a letter move.
Confirmed live: felhom-usb's mount unit was disabled (systemctl is-enabled → disabled,
no wants-symlink) while felhom-flash's was enabled. The What= line was already correct
(/dev/disk/by-uuid/<UUID>), so the fix is to re-assert the unit by UUID at startup, not to
rewrite the unit.
Fix
storage.ResolveStorageDevice(durableID)(durable_device.go) — resolves the enrolleduuid:<fs-uuid>storage scheme to its current backing/devnode by re-scanning/dev/disk/by-uuid(EvalSymlinks); rejects any non-uuid:scheme (anti-node-binding) and errors when the UUID is absent so a caller skips a gone drive instead of fail-mounting.storage.parseFelhomMountUnit(mountunit.go) — pure inverse ofrenderMountUnit, gated on aManaged by felhom-agentmarker; extracts Name/UUID/Where/Type/Options, ignores foreign units.(*SudoHostOps).ReassertEnrolledMounts(ctx)(hostops.go) — for each enrolled.mountunit, re-resolve by UUID and re-runEnsureMount(idempotentsystemctl enable --now). Skips ONLY the durable steady state (mounted AND enabled), via the pureshouldReassertMount; a mounted-but-DISABLED unit (the exact live felhom-usb state — serves now, but a reboot would not auto-mount it) is still re-asserted so the wants-symlink is re-created. Enabled-state read with a privilege-freeos.Lstatof themulti-user.target.wantssymlink (unitEnabled) — nosystemctl is-enabledsubprocess / no new sudoers entry. Absent UUIDs skipped (retry next tick).main.go— runsReassertEnrolledMountsat startup beforeReassertGuestBinds, and again ahead of the bind re-assert on the periodic 20s tick, so host mounts are live before the guest binds re-establish.
Tests (non-hollow, seam = device resolution; Linux)
TestResolveStorageDevice_ToleratesDeviceLetterMove— fake/dev/disk/by-uuid/<uuid>symlink pointed atsdc(moved from the enroll-timesdb); assertsResolveStorageDevicereturns the CURRENT nodesdc. Companion (red-proof): asserts the cached enroll-time node (sdb) differs from the freshly-resolved node — a node-trusting remount would mount the wrong device.TestResolveStorageDevice_AbsentAndScheme— absent UUID errors; onlyuuid:is resolvable (/dev/sdb1,store:,byid:,""all rejected).TestParseFelhomMountUnit—renderMountUnit→parseFelhomMountUnitround-trip + rejects a non-felhom unit.TestShouldReassertMount— the four mounted/enabled combinations; pins that mounted-but-disabled (the live bug) re-asserts and only mounted+enabled is skipped.TestUnitEnabled— wants-symlink presence ⇒ enabled (privilege-freeLstat).
Task A2 — verdict: enrolling a NEW drive does NOT need an LXC restart
NO. Source-verified the enroll path: /disks/guest-attach → handleDiskGuestAttach →
GuestBinder.AttachDrive (intermediary-mount model, "no pct, no reboot") — a live bind under the
single shared parent /mnt/felhom-drives, which provides unbounded named live slots. It does
NOT take the legacy RebootGuest branch. The operator's pre-created-slot-pool idea is unnecessary.
Gate
go build ./..., go vet ./..., go test ./... all green locally (Windows: the symlink tests
skip — they assert on Linux, the agent's OS). Linux green gate + companion red-proof run on the
build server (192.168.0.180, go1.26.0) — see live-acceptance section.
Live acceptance
Real host reboot ×2 on felhom-pve: before/after lsblk, shared-parent-before-pve-guests ordering,
both drives (felhom-usb + felhom-flash) remount by UUID across a /dev/sdX reshuffle, guest 9201
drive-backed apps recover, FileBrowser non-stale. (Captured during the supervised run.)
Secrets (local_api token / leaf fingerprint) handled out-of-band — none committed.