4.2 KiB
status, opened, located-in
| status | opened | located-in | ||
|---|---|---|---|---|
| located | 2026-09-26 |
|
128 — the machine's hosts file is written whole, and on a workstation it is shared
What was observed
The private network asks for the node-names fact, and the mesh delivers it as
/etc/hosts. nodeNames composes a complete file — its own header, localhost, the
machine's own name, and every name in the mesh — and the host writes it over whatever is there.
On an adopted workstation the file the mesh holds contains, besides the predecessor's block of mesh names:
- the distribution's own lines (
localhost, the machine's.localdomainname); - two marked blocks (
# BEGIN … # END …) maintained by a local-development tool, pointing a dozen development hostnames at127.0.0.1— rewritten by that tool whenever its project list changes; - hand-added entries of the operator's.
Today the file is only held (ADR 0100): the private network was assigned, not yet taken, so nothing was lost. Taking it — or converging the node, which takes everything — replaces the file. The development tool's entries disappear, its projects stop resolving, and every later write it makes is overwritten at the next change to the mesh's names (a machine joins, a route is contributed), silently and without a failure anywhere: the development tool thinks it wrote its block, and the mesh thinks it owns the file.
This is ADR 0102's
failure exactly — a file the mesh shares with software it did not install, written over — in a
file 0102 did not name, because its merge verb is structured (into: json) and a hosts file is
not JSON.
A second, smaller finding from the same reading: the fact's contents depend on which machines
hold the private network. A machine that is enrolled but not yet assigned the private network
is in neither node-names nor node-zones; its name resolves on the others only for as long
as a predecessor's hosts block survives. Taking the hosts file before every machine is on the
private network loses that name too.
What would have prevented it
- A marked-region merge in the host's vocabulary:
into: "block"(or similar) — the host owns only the lines between its own begin and end markers, keeps everything outside them byte for byte, records what the region held before, and on undeclare removes the region and nothing else. The shape local tools already use for this very file. - The
node-namesfact written as that region — no header of its own, nolocalhost, no machine name — so the distribution's lines and every other tool's stay where they are. - A converge preview that names a held file the take would replace whole, with its line count before and after, so a person sees "hosts: 31 lines → 12" before the flip.
The fix, as built (in review)
- Host: a file resource may say
"into": "block". The host owns only the lines between# BEGIN mesh <id>and# END mesh <id>and keeps everything outside them byte for byte. A new region goes at theendby default, or at thestart("at": "start") for files where a line's meaning depends on what stands above it; a region already present is never moved. Undeclared, what the region held before is put back, or the region is removed and nothing else. Replacing nothing, it is written on an adopted node without being held — so a machine gets the mesh's names before its private network is taken. - Controller:
node-namesis a fact written into a shared file, emitted as that region: the mesh's names only, no header, nolocalhost, no127.0.1.1line. - Order: a host older than the block mode refuses the whole declaration on an unknown
into, so hosts are upgraded before the controller that emits it.
Evidence to carry into diagnosis
internal/catalogue/facts.go,nodeNames: the complete file is built here.- The host's file resource supports
into: "json"only; anything else is a whole write. node show <node>on the adopted workstation:holds file /etc/hosts mesh-wireguard.fact-node-names, original kept.