After a rebuild the customer's own drives could not be re-attached: candidates returned initialize:[] attach:[] while both drives sat there, and the deploy refused with 'choose an attached drive from the list' — a list that was empty. Measured live three times. Mechanism: enrolment mounts a drive TWICE, at /mnt/felhom-drives/<name> and at the raw /mnt/<name> it creates on the host. The host survives a guest rebuild; the controller's registry does not. So classifyClaim saw a mount outside the managed prefix and concluded 'claimed by something else' — about our own mount. The fix is CORROBORATED, not a widened prefix: a non-managed mountpoint is forgiven only when the SAME device is also mounted under the managed path, a pairing only our enrolment produces. A disk another system uses — /srv/data, /media/x, even /mnt/someone-elses-disk — has no counterpart and is STILL refused, with its own test and a red-proof showing an over-wide fix offering it for formatting. Read from /proc/mounts deliberately: the lsblk invocation is pinned verbatim in configs/felhom-agent.sudoers, so using the plural MOUNTPOINTS would have coupled this to a sudoers rollout. /proc/mounts is world-readable — no sudo, no new allowlisted command, no config change. Fail-safe: an unreadable mount table corroborates NOTHING, so the device classifies exactly as before. 'Could not corroborate' must never read as 'ours'. 29 packages ok, vet clean, agent gates OK.
2.1 KiB
REPORT — felhom-agent v0.127.0: a mount Felhom made is not foreign (R-220)
Scope: the host half of R-220. The customer-facing refusal message is the controller's half and ships as felhom-controller v0.203.0.
What changed
| File | Change |
|---|---|
internal/storage/claim.go |
claimFacts.felhomOwnedMounts; classifyClaim forgives a non-managed mountpoint only when corroborated; felhomOwnedMounts() + procMounts() |
internal/storage/hostops.go |
mountTable seam (nil ⇒ real /proc/mounts) |
internal/storage/claim_r220_test.go |
new — the own-drive case, the fence, and the corroboration's four edges |
The shape chosen, and why (§7.3)
Candidate (b): the claimed check distinguishes a mount Felhom made from a foreign one — the task called it "nearer the truth" and it is, because the host and its knowledge survive the rebuild while the guest's registry does not. Candidate (a) — having the rebuild path clear the raw mounts — would have made correctness depend on a cleanup step running, and a cleanup that does not run leaves exactly today's defect.
The discriminator is corroboration, not a path prefix: the same device must ALSO be mounted under
/mnt/felhom-drives. Only enrolment produces that pairing.
/proc/mounts rather than lsblk MOUNTPOINTS, because the lsblk invocation is pinned verbatim in
the sudoers file; changing it would have coupled this fix to a config rollout. /proc/mounts is
world-readable and needs neither.
Green gate
go build · go vet clean · go test ./... → 29 packages ok · agent_gates.py --fast → all OK.
| Red-proof | Result |
|---|---|
remove the felhomOwnedMounts exemption |
FAILS — "device is mounted at /mnt/adatok (sdb)", the pre-fix refusal |
over-widen the exemption to any /mnt/* |
FAILS — "/mnt/someone-elses-disk was offered for formatting" |
Not changed
No sudoers, no allowlisted command, no PVE surface, no format path. Every other claim signal (system disk, read-only, LVM PV, ZFS member, member FSTYPEs, empty-topology backstop) is untouched.