Files
felhom-agent/REPORT.md
T
admin a621f4c5a0 agent v0.37.0: host-reboot remount re-resolves enrolled drives by fs-UUID
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>
2026-06-16 17:49:00 +02:00

70 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
1. A systemd `.mount` unit left **`disabled`** by a prior detach (`Unmount` = stop + disable)
never gets a `multi-user.target.wants` symlink, so it does **not** auto-mount at boot — and
nothing in the agent re-asserted it at startup.
2. Any remount that trusted a remembered `/dev/sdX` node 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 enrolled
`uuid:<fs-uuid>` storage scheme to its **current** backing `/dev` node 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 of `renderMountUnit`, gated on a
`Managed by felhom-agent` marker; extracts Name/UUID/Where/Type/Options, ignores foreign units.
- `(*SudoHostOps).ReassertEnrolledMounts(ctx)` (`hostops.go`) — for each enrolled `.mount` unit
not in `/proc/mounts`, re-resolve by UUID and re-run `EnsureMount` (idempotent
`systemctl 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` — runs `ReassertEnrolledMounts` at startup **before** `ReassertGuestBinds`, 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 at `sdc` (moved from the enroll-time `sdb`); asserts `ResolveStorageDevice` returns the
CURRENT node `sdc`. **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; only `uuid:` is resolvable
(`/dev/sdb1`, `store:`, `byid:`, `""` all rejected).
- `TestParseFelhomMountUnit``renderMountUnit``parseFelhomMountUnit` round-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.