|
|
|
@@ -7,15 +7,15 @@ code:
|
|
|
|
|
- mesh-controller internal/identity/authority.go
|
|
|
|
|
- mesh-controller cmd/mesh-controller/plan.go (the names the roster publishes)
|
|
|
|
|
- mesh-controller internal/catalogue/zones.go (the zones a module answers, ADR 0199)
|
|
|
|
|
- mesh-controller internal/catalogue/seats.go (mesh-dns-resolver, node-hosts-file, node-resolver-config needing node-uplink)
|
|
|
|
|
- mesh-controller internal/catalogue/seat_dependencies.go (a seat's need checked at assignment, ADR 0220)
|
|
|
|
|
- mesh-controller internal/catalogue/seats.go (mesh-dns-resolver, node-hostname, node-uplink)
|
|
|
|
|
- mesh-controller internal/catalogue/resolve.go (one owner per path, a rendered fact's included, ADR 0223)
|
|
|
|
|
- mesh-controller cmd/mesh-controller/holdings.go (the holders of a replicated seat, ADR 0223)
|
|
|
|
|
- mesh-controller internal/catalogue/roster.go (each replicated seat's holders, for a template)
|
|
|
|
|
- mesh-catalog modules/dnsmasq (the mesh's resolvers)
|
|
|
|
|
- mesh-catalog modules/resolv-conf (what a node asks)
|
|
|
|
|
- mesh-catalog modules/hosts (a node's /etc/hosts)
|
|
|
|
|
- mesh-catalog modules/networkmanager, modules/systemd-networkd, modules/dhcpcd (what a node asks, written by its uplink's holder)
|
|
|
|
|
- mesh-catalog modules/hostname (a node's /etc/hostname and /etc/hosts)
|
|
|
|
|
- mesh-host internal/identity/serving.go
|
|
|
|
|
- mesh-host internal/apply (the service that reflects a rule set)
|
|
|
|
|
- mesh-host internal/apply (the service that reflects a rule set; a whole file handed to its new owner)
|
|
|
|
|
updated: 2026-10-05
|
|
|
|
|
decisions:
|
|
|
|
|
- 02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md
|
|
|
|
@@ -379,40 +379,52 @@ the module not on record stands beside them, eligible and silent, and a seat hel
|
|
|
|
|
once. *Checked by the controller's resolution tests with two holders on record, with two claimants
|
|
|
|
|
and nothing on record, and on a seat held once.*
|
|
|
|
|
|
|
|
|
|
**Every node asks them for everything, and nothing else.** The module holding
|
|
|
|
|
`node-resolver-config` writes `/etc/resolv.conf` listing every holder's private address — the holder
|
|
|
|
|
on the machine itself first if it is one, then the rest by name — with a short timeout and two
|
|
|
|
|
attempts, and **no public resolver**. ADR 0196 listed a public resolver second, for the anchor being
|
|
|
|
|
unreachable; a C library that asks every listed server at once and takes the first reply — musl, so
|
|
|
|
|
every Alpine container — took the public resolver's "no such name" for a mesh name, and every build on
|
|
|
|
|
the home server failed. With only the mesh's resolvers listed, whichever answers first gives the one
|
|
|
|
|
answer. The cost is stated: a machine that reaches no mesh resolver has no names until it does, and
|
|
|
|
|
with the anchor down the second resolver is reachable only on its own machine and its own LAN.
|
|
|
|
|
Containers take the same lines from their machine, the runtime copying non-loopback resolvers into
|
|
|
|
|
every container, so the runtime is given no `dns` of its own
|
|
|
|
|
**Every node asks them for everything, and nothing else.** `/etc/resolv.conf` lists every holder's
|
|
|
|
|
private address — the holder on the machine itself first if it is one, then the rest by name — with a
|
|
|
|
|
short timeout and two attempts, and **no public resolver**. ADR 0196 listed a public resolver second,
|
|
|
|
|
for the anchor being unreachable; a C library that asks every listed server at once and takes the
|
|
|
|
|
first reply — musl, so every Alpine container — took the public resolver's "no such name" for a mesh
|
|
|
|
|
name, and every build on the home server failed. With only the mesh's resolvers listed, whichever
|
|
|
|
|
answers first gives the one answer. The cost is stated: a machine that reaches no mesh resolver has no
|
|
|
|
|
names until it does, and with the anchor down the second resolver is reachable only on its own machine
|
|
|
|
|
and its own LAN. Containers take the same lines from their machine, the runtime copying non-loopback
|
|
|
|
|
resolvers into every container, so the runtime is given no `dns` of its own
|
|
|
|
|
([ADR 0196](../../02-DECISIONS/0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md),
|
|
|
|
|
replacing ADR 0194's per-node `systemd-resolved` stub). `resolv-conf` is the one module the catalogue
|
|
|
|
|
offers for it; the systemd-resolved split-DNS module that once claimed the same seat is gone
|
|
|
|
|
([ADR 0220](../../02-DECISIONS/0220-what-a-machine-asks-needs-its-uplink-held-and-the-retired-resolver-pieces-go.md)).
|
|
|
|
|
*Checked by the controller's composition tests on both holders and on a third machine, and live by
|
|
|
|
|
each machine's `/etc/resolv.conf` and an Alpine container on the home server resolving the anchor's
|
|
|
|
|
name every time.*
|
|
|
|
|
replacing ADR 0194's per-node `systemd-resolved` stub). *Checked by the controller's composition tests
|
|
|
|
|
on both holders and on a third machine, for every uplink module, and live by each machine's
|
|
|
|
|
`/etc/resolv.conf` and an Alpine container on the home server resolving the anchor's name every
|
|
|
|
|
time.*
|
|
|
|
|
|
|
|
|
|
**Next, decided and not yet built** ([ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md)):
|
|
|
|
|
`/etc/resolv.conf` becomes the file of the `node-uplink` holder, given the resolvers by the mesh —
|
|
|
|
|
NetworkManager through its global DNS configuration, dhcpcd through static nameservers,
|
|
|
|
|
systemd-networkd's module declaring the file — and `resolv-conf`, `node-resolver-config` and its need
|
|
|
|
|
for the uplink below retire. And a machine's own names become one seat's: the `hosts` module is
|
|
|
|
|
renamed `hostname` and holds `node-hostname`, writing `/etc/hostname` and the machine's `127.0.1.1`
|
|
|
|
|
line and keeping the operator's lines.
|
|
|
|
|
**The file is the uplink's holder's** ([ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md)).
|
|
|
|
|
A network manager rewrites `/etc/resolv.conf` on every connectivity change unless it is told not to
|
|
|
|
|
([ADR 0117](../../02-DECISIONS/0117-a-machines-uplink-is-a-seat.md)), so the module holding
|
|
|
|
|
`node-uplink` — the one telling it — writes the file itself, and there is one owner for it, the one
|
|
|
|
|
whose program would otherwise overwrite it. Each of the catalogue's three managers' modules
|
|
|
|
|
(NetworkManager, systemd-networkd, dhcpcd) renders the same template from the resolver's holders and
|
|
|
|
|
requires the mesh's resolver, so a machine is refused when nothing in the mesh resolves rather than
|
|
|
|
|
given a file listing nothing. The managers' own mechanisms were weighed and not used: NetworkManager's
|
|
|
|
|
global DNS and dhcpcd's static nameservers each write the file in their own form — their own header,
|
|
|
|
|
their own options line — so neither can write the mesh's file byte for byte, and dhcpcd reads its
|
|
|
|
|
configuration only at its next start; each manager is told to keep off the file and the module
|
|
|
|
|
declares it. No other module may write that path, as a file or as a rendered fact: two modules on one
|
|
|
|
|
node declaring one path are refused. The module that wrote the file before, its seat
|
|
|
|
|
`node-resolver-config` and that seat's need of the uplink beside it
|
|
|
|
|
([ADR 0220](../../02-DECISIONS/0220-what-a-machine-asks-needs-its-uplink-held-and-the-retired-resolver-pieces-go.md))
|
|
|
|
|
retire. The file changes owner in one apply on each machine: the node-engine hands a whole file to the
|
|
|
|
|
resource declaring its path now rather than removing it first, so a machine is never without it.
|
|
|
|
|
*Checked by the controller's tests that the three modules carry one identical template and that
|
|
|
|
|
nothing else in the catalogue writes the path, a resolution test refusing a second writer, and the
|
|
|
|
|
node-engine's handover test, in which the file is present at every step of the apply.*
|
|
|
|
|
|
|
|
|
|
**That file stays the mesh's only beside the uplink.** A network manager rewrites `/etc/resolv.conf`
|
|
|
|
|
on every connectivity change unless it is told not to, and the module holding `node-uplink` is what
|
|
|
|
|
tells it ([ADR 0117](../../02-DECISIONS/0117-a-machines-uplink-is-a-seat.md)). So
|
|
|
|
|
`node-resolver-config` needs `node-uplink` held on the same node: assigning the resolver file to a
|
|
|
|
|
machine with no uplink holder is refused, naming the managers' modules that could hold it, and taking
|
|
|
|
|
the last uplink holder from beneath it is refused too
|
|
|
|
|
([ADR 0220](../../02-DECISIONS/0220-what-a-machine-asks-needs-its-uplink-held-and-the-retired-resolver-pieces-go.md)). *Checked by the controller's seat-dependency tests and by `assign` live.*
|
|
|
|
|
**A machine's names are one seat's** ([ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md)).
|
|
|
|
|
The `hosts` module is renamed `hostname` and holds `node-hostname`, the seat once named
|
|
|
|
|
`node-hosts-file`, whose former name resolves to it. It writes `/etc/hostname` from its `hostname`
|
|
|
|
|
setting — with no default: what a machine calls itself is the operator's, and the mesh's name for a
|
|
|
|
|
machine and its own need not agree — and the machine's `127.0.1.1` line, keeping the operator's lines.
|
|
|
|
|
A new name takes effect at the next boot; nothing sets it live, because a graphical session's X
|
|
|
|
|
authority is keyed by the name the session started under. *Checked by a resolution test that a
|
|
|
|
|
module claiming the old name and one claiming the new are one seat on one machine, and a composition
|
|
|
|
|
test that `/etc/hostname` is the setting, left out naming the key when nothing sets it.*
|
|
|
|
|
|
|
|
|
|
**No node holds a copy of its own.** The two holders hold the same rendering of one roster, never a
|
|
|
|
|
list anyone edits. The per-node resolver, its zones file and the mesh's region of `/etc/hosts`
|
|
|
|
@@ -428,7 +440,7 @@ setting) and the listen that answers it; the controller hands the `mesh-dns-reso
|
|
|
|
|
zone with its module's node address and published port, and each holder forwards that zone there and
|
|
|
|
|
answers nothing in it itself — the lab answers `<machine>.incus` for its running scenarios this way.
|
|
|
|
|
An operator's own names, unrelated to the mesh, live in `/etc/hosts`'s kept region, held per node by
|
|
|
|
|
the `node-hosts-file` seat's holder and changed through its tools; the controller holds none of them
|
|
|
|
|
the `node-hostname` seat's holder and changed through its tools; the controller holds none of them
|
|
|
|
|
([ADR 0199](../../02-DECISIONS/0199-a-module-that-answers-names-declares-its-zone-and-a-nodes-hosts-file-is-one-modules.md)).
|
|
|
|
|
*Checked by the holder's configuration carrying one forwarding rule per declared zone, and by a push
|
|
|
|
|
leaving the hosts file's operator region byte for byte.*
|
|
|
|
|