The per-node resolvers 0194 retires held two kinds of names that are neither nodes nor routes: the lab's scenario machines and an operator's own lines. A zone a module declares is forwarded by the mesh's resolver to that module; /etc/hosts is held per node through node-hosts-file, the operator's lines in its kept region. Research 023 parks seats that define what their holder owns.
8.0 KiB
topic, status, date, deciders, reconstructed, extends
| topic | status | date | deciders | reconstructed | extends |
|---|---|---|---|---|---|
| the tiers | accepted | 2026-10-03 | jochen | false | 0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md |
199. A module that answers names declares its zone, and a node's hosts file is one module's
Context
ADR 0194 and ADR 0196 leave one resolver holding the nodes' internal domains, and retire the resolver every node ran. Two kinds of names lived in those per-node resolvers that are neither a node nor a route, and both were found on the workstation on 2026-10-03:
- Names a module answers. The lab raises scenario machines and gives them addresses from its
scenario files — the anchor's stand-in at a documentation address, the home server's on the LAN —
and the workstation resolved
<machine>.incusthrough two wildcard lines in a drop-in file its resolver read. The lines were written by hand; the addresses are the lab's, known only while a scenario runs. - The operator's own names, unrelated to the mesh. Twelve
<loopback> <name>lines for a client's development hosts, kept in/etc/hostsand again in/etc/hosts.local, which the per-node resolver read as additional hosts.
A manifest never names an address, a node or a domain (ADR 0112).
So the lab cannot list <machine>.incus → <address> in its definition, and the operator's twelve lines
are not any module's to define.
Considered Options
For a module's names:
- The manifest lists its records. Refused by ADR 0112: the addresses are the lab's runtime facts and the scenario's choice.
- The module reports its records at runtime to the mesh's resolver, which writes them into its configuration. It works, and it makes the resolver hold every module's runtime state and decide, per call, whether the caller may write the name it sent — authorisation for a write, on the one server every node depends on.
- The module declares the zone it answers and the listen that answers it; the mesh's resolver forwards that zone there. The definition names a zone (from a setting) and one of its own listens, which ADR 0112 allows; the address and the port are the mesh's facts. The records stay where they are known — in the module, at runtime. Chosen.
For the operator's names:
- Records the controller holds, served by the mesh's resolver. They are not the mesh's: a client's development hosts on one machine are nothing any other node should resolve, and the controller would become the keeper of a workstation's private notes.
- A node-scoped module owns
/etc/hosts, and the operator's lines live in its kept region (ADR 0174), changed through that module's tools on that machine. Chosen.
Decision
1. A module that answers names declares a zone. Its definition names the zone — a single label or a dotted name, from a setting, never a domain the mesh knows — and the listen that answers DNS for it. The controller refuses two modules in the mesh declaring one zone, and a zone that is the mesh's suffix, under it, or one of a node's public domains: a module may not shadow names the mesh or the public DNS answers.
2. The mesh's resolver forwards each zone to the module that declared it. The controller hands the
holder of mesh-dns-resolver every declared zone with the private address of the node its module runs
on and the port that listen is published on; the holder places one forwarding rule per zone into its
configuration and answers nothing in that zone itself. What names exist in the zone, and their
addresses, are the module's — answered by its own long-running code
(ADR 0198),
from its own state, as they change. Whether an answered address is reachable from the asking node is
the module's matter, not the resolver's.
3. A node's /etc/hosts is held by one module, through a node seat, node-hosts-file. The seat is
the definition: its holder owns /etc/hosts, and implements three verbs — MCP tool definitions served
as <node>/node-hosts-file.<verb> (ADR 0195):
entries (the file's lines, the module's and the operator's, each marked whose), add (one
address and its names, into the operator's region) and remove (one name or address from it). The
module writes the machine's own lines — loopback and the machine's name — and keeps a region for the
operator, which survives every push and is given back when the module goes. Its tools change that
region on that machine, escalating as the packet filter's do
(ADR 0175 §4). The
controller holds none of it: an operator's line is the machine's, not a record.
4. No other module writes /etc/hosts. The private network's region goes, as ADR 0194 already has
it; a module that once wrote a line there asks the mesh's resolver instead.
Consequences
- The lab's names follow its scenarios. A scenario raised is resolvable from every node at once; a scenario torn down is gone, with no line left behind in any file.
- The mesh's resolver holds no module's state. It holds the nodes' domains and a table of who answers which zone, both composed by the controller; nothing writes to it at runtime.
- A module answering a zone needs a DNS answerer of its own — a long-running bundle, or a resolver it runs. The lab gains one.
- The operator's names reach the machine's own programs, not its containers. A container does not
read the machine's
/etc/hosts. For names unrelated to the mesh that is the right boundary; a name a container needs belongs in a zone. - Taking
/etc/hostskeeps what is there. The first time the module writes the file, every line that is not the machine's own goes into the operator's region, so a workstation's twelve lines survive the take — the same adoption ADR 0102 gives every shared file.
How each is checked:
- Zones: the controller's catalogue tests refuse a second module declaring a zone, a zone under the mesh suffix, and a zone equal to a node's public domain.
- Forwarding: on the holder, the resolver's configuration carries one forwarding rule per declared zone, at the declaring node's private address and published port; asking any node's resolver for a name in the lab's zone while a scenario runs returns the scenario's address.
- The hosts file: a push leaves the operator's region byte for byte;
addfollowed byentriesshows the line as the operator's; unassigning the module gives the region back.
References
- ADR 0194, ADR 0196 — the one resolver and how nodes ask it.
- ADR 0112 — why a definition names no address.
- ADR 0174, ADR 0102 — kept regions and shared files.
- ADR 0198 — where a zone's answerer runs.
- Connectivity §2 and the seats, amended alongside.
- Research 023 —
the general form of decision 3's "the holder owns
/etc/hosts".