TASK A — close out the reboot story (agent half). Root cause (pinned live on felhom-pve): an enrolled .mount unit left `disabled` by a prior detach never auto-mounts at boot, and kernel re-enumeration can move a drive's node (/dev/sdb->sdc). Fix re-asserts every enrolled mount by filesystem UUID at startup + on the periodic tick. - ResolveStorageDevice: resolve uuid:<fs-uuid> -> current /dev node via /dev/disk/by-uuid (never a cached node); errors if absent. - parseFelhomMountUnit: pure inverse of renderMountUnit (marker-gated). - (*SudoHostOps).ReassertEnrolledMounts: re-run EnsureMount (enable --now) for any enrolled unit not in /proc/mounts; idempotent, skips mounted/absent. - main.go: runs before ReassertGuestBinds at startup + on the 20s tick. - tests (Linux, seam=device resolution): letter-move tolerated (sdb->sdc) + red-proof companion, absent/scheme rejection, render->parse round-trip. TASK A2 verdict: enrolling a NEW drive does NOT need an LXC restart — the path lands on the live AttachDrive (shared parent, named live slots), not RebootGuest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.1 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 not in/proc/mounts, re-resolve by UUID and re-runEnsureMount(idempotentsystemctl enable --now). Re-enables a disabled unit AND mounts the current device by UUID; already-mounted drives skipped (no daemon-reload churn), 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.
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.