Files
mesh-controller/examples/modules
jschoubben ab1dd34d12 The resolver does not ask itself for upstreams
dnsmasq read /etc/resolv.conf to find where to forward. Whatever points a
machine at the mesh writes its own address into that file — so dnsmasq's
upstream was dnsmasq, and every query it could not answer locally looped. Its
receive queue filled with 15KB of them and every lookup on the machine hung,
which is why this arrived as a thirty-second timeout rather than a wrong
answer.

It needs no upstream at all: the asking module routes only the mesh's suffix
here and leaves everything else where the machine already sent it. And it names
none, because choosing one would send every query this machine makes somewhere
nobody agreed to.

Also corrected: the comment claiming it takes only 127.0.0.55. Listening on a
loopback address makes dnsmasq take the rest of loopback with it, 127.0.0.1
included — which is what claiming `the-dns-port` already says, and which the
comment was quietly denying. That is the same comfortable claim as ".54 is
free", in the same file, made twice.
2026-08-31 14:09:50 +02:00
..

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.