`127.0.0.54` is systemd-resolved's DNS *proxy* stub. The module asserted it was free, in a comment that read as reasoned — "not .53, that is systemd-resolved's" — and it was simply wrong: resolved holds both. dnsmasq could not create the socket and never started. Nothing in a unit test could have caught it. They checked the module names an address and that the asking modules point at the same one, and all of that passed while the daemon could not start. Only a machine knows which addresses are spare, which is the argument for proving a module that asserts facts about machines on a machine, before believing the assertions. So it moves to .55, and says what that is: a convention, not a reservation. If a future systemd takes it, this line changes and nothing else does. The tests now derive the address from the serving module and check the two asking modules agree with it, rather than naming it a fourth time — that fourth place is the one nobody would think to change. And the lab assigns `resolved-split-dns` rather than `resolv-conf`: those machines run systemd-resolved, which owns the file. The two claim the same thing precisely so the wrong choice is a refusal rather than a fight, and picking the wrong one was testing the fight.
Example modules
Manifests, not programs. They are here because the contract is easier to read as something that works than as a description of something that would.
Third-party software runs on the mesh, not of it (novox/hq ADR 0001). dnsmasq is not the mesh's, and neither is systemd-resolved — what is the mesh's is the fact only it can know, which is which machines exist and where they are. So the mesh writes that to a file and these read it.
Resolving a service named under a machine
postgres.novox.internal, plex.ace.internal. The first label is the service and the rest is the
node, so anything under a node's name must resolve to that node and a proxy there routes by
the name it was asked for. That routing is a separate concern and stays separate.
Two roles, and they are genuinely different things:
| claims | ||
|---|---|---|
| serving | the-dns-port |
answers the wildcards — dnsmasq.json |
| asking | the-resolver-configuration |
decides what the machine asks — resolved-split-dns.json, resolv-conf.json |
systemd-resolved cannot serve a wildcard, so it is only ever an asking module: it routes the mesh's suffix to something that can. Treating the two roles as one would produce a module that cannot work, which is the mistake worth naming.
Assign one of each. Two of either is refused by the mesh rather than fought over on the machine:
resolved-split-dns and resolv-conf both claim "the-resolver-configuration", and only one thing may hold it per node
ADR 0009's table names that resource /etc/resolv.conf, which is what it is, the way it writes
the seat. A claim is a name in the catalogue's own form, so it is written as one.
Why neither needs to know the machine's address
Both would ordinarily need it — a resolver must bind somewhere, and a stub must be pointed somewhere — and a static manifest cannot know it.
Neither does, because both name things the mesh itself named: the private network's interface
is mesh0 on every machine, and the address a resolver listens on for the machine's own use is
127.0.0.54 on every machine. A name the mesh chose is a name a manifest can use.