Files
hq/02-DECISIONS/0199-a-module-that-answers-names-declares-its-zone-and-a-nodes-hosts-file-is-one-modules.md
T
jschoubben 8184585213 ADR 0199: a module that answers names declares its zone, and a node's hosts file is one module's
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.
2026-10-03 23:19:36 +02:00

128 lines
8.0 KiB
Markdown

---
topic: the tiers
status: accepted
date: 2026-10-03
deciders: jochen
reconstructed: false
extends: 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](0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md) and
[ADR 0196](0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md) 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>.incus` through 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/hosts` and 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](0112-a-module-definition-names-no-node-mesh-or-path.md)).
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:**
1. **The manifest lists its records.** Refused by ADR 0112: the addresses are the lab's runtime facts
and the scenario's choice.
2. **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.
3. **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:**
1. **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.
2. **A node-scoped module owns `/etc/hosts`, and the operator's lines live in its kept region**
([ADR 0174](0174-a-node-varies-a-module-through-settings-and-kept-regions-never-an-edit.md)), 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](0198-a-modules-long-running-code-is-launched-by-the-node-runtime-and-reaches-the-bus-through-it.md)),
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](0195-the-meshs-tools-are-found-by-address-not-announced-whole.md)):
**`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](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) §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/hosts` keeps 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](0102-the-mesh-writes-into-a-shared-file-never-over-it.md) 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; `add` followed by `entries`
shows the line as the operator's; unassigning the module gives the region back.
## References
- [ADR 0194](0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md),
[ADR 0196](0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md) —
the one resolver and how nodes ask it.
- [ADR 0112](0112-a-module-definition-names-no-node-mesh-or-path.md) — why a definition names no address.
- [ADR 0174](0174-a-node-varies-a-module-through-settings-and-kept-regions-never-an-edit.md),
[ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md) — kept regions and shared files.
- [ADR 0198](0198-a-modules-long-running-code-is-launched-by-the-node-runtime-and-reaches-the-bus-through-it.md) —
where a zone's answerer runs.
- [Connectivity §2](../03-DESIGN/01-to-be/08-connectivity.md) and [the seats](../03-DESIGN/01-to-be/26-the-seats.md),
amended alongside.
- [Research 023](../01-RESEARCH/023-a-seat-protocol-that-defines-what-its-holder-owns/00-overview.md) —
the general form of decision 3's "the holder owns `/etc/hosts`".