Files
felhom-agent/REPORT.md
T
admin 703db166e7
gates / gates (push) Successful in 8s
v0.127.0: a mount Felhom itself made is not 'something else' (R-220)
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.
2026-08-06 12:55:28 +02:00

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.