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.
128 lines
8.0 KiB
Markdown
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`".
|