136 lines
8.5 KiB
Markdown
136 lines
8.5 KiB
Markdown
---
|
|
topic: what runs on it
|
|
status: accepted
|
|
date: 2026-09-26
|
|
deciders: jochen
|
|
reconstructed: false
|
|
extends: 0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
|
---
|
|
|
|
# 117. A machine's uplink is a seat: the mesh configures the manager, never the link
|
|
|
|
## Context
|
|
|
|
The mesh installs on top of a machine's own networking. The private network's generator says
|
|
so in as many words: a machine has an address and a route to the broker *before* the mesh
|
|
exists, the broker's address travels in the enrolment token rather than being resolved, and the
|
|
private network is something the mesh installs on top, like anything else. Nothing in the mesh
|
|
says who manages that uplink, or what the mesh needs from whoever does.
|
|
|
|
Adopting the first workstations showed that the mesh does need something from it, and gets it
|
|
by accident:
|
|
|
|
- **The resolver the mesh owns depends on a file the mesh does not.** `resolv-conf` writes
|
|
`/etc/resolv.conf` and names the mesh's resolver. On a machine running NetworkManager, the
|
|
manager rewrites that file on every connectivity change unless it is told `dns=none`; on one
|
|
running dhcpcd, every lease renewal rewrites it unless it is told `nohook resolv.conf`. On
|
|
the adopted machines both settings exist only because the predecessor wrote them. No module
|
|
declares them. Remove the predecessor's file and the mesh's resolver is silently replaced the
|
|
next time a laptop changes network, while every surface of the mesh still reads green.
|
|
- **`resolv-conf` cannot declare them itself.** Which setting is needed depends on which
|
|
manager runs, and a `service` resource for a manager that is not installed fails the
|
|
declaration. A resolver module that knew about network managers would be the wrong module
|
|
knowing the wrong thing.
|
|
- **The private network's interface is exposed to the manager.** A manager that considers
|
|
every interface its own may try to configure `mesh0`, or tear it down on a profile change.
|
|
Nothing tells it not to.
|
|
- **Two managers on one machine go unnoticed.** Among the machines adopted so far, one runs
|
|
NetworkManager *and* dhcpcd at once: two programs that each believe they own the machine's
|
|
addresses and its resolver file.
|
|
Nothing detected it, because nothing in the mesh knows the role exists.
|
|
|
|
The machines differ in a way that matters: servers are wired and never move, while
|
|
workstations join wireless networks, captive portals and phone hotspots wherever they are.
|
|
|
|
## Considered Options
|
|
|
|
**1. The mesh manages the uplink: links, addressing, wireless networks and their
|
|
credentials.** Rejected. The mesh reaches a machine only over that link. A declaration that
|
|
gets it wrong — a mistyped network, a stale credential, a manager that fails to start — takes
|
|
the machine off the network, and with it the only channel a fix could arrive on. That is the
|
|
one failure the sshd module's `listens` rule forbids the firewall to arrange; a mesh that owned
|
|
the link could arrange it with any push. And a wireless network is joined at the machine, by
|
|
the person using it, in the moment. A declaration composed elsewhere cannot answer a captive
|
|
portal.
|
|
|
|
**2. Leave the uplink unmanaged; accept the implicit dependency.** Rejected. It keeps the
|
|
resolver working only for as long as a predecessor's file survives, and it leaves two managers
|
|
on one machine undetectable.
|
|
|
|
**3. The uplink is a seat. The module holding it configures the manager's relationship to the
|
|
mesh, and never the link.** Chosen.
|
|
|
|
## Decision
|
|
|
|
**`the-uplink` is a node-scoped seat** in the closed set ([ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md)).
|
|
It delivers no provision. It is held by the module for the program that manages the machine's
|
|
own network, one per manager: `networkmanager`, `systemd-networkd`, and `dhcpcd` for a machine
|
|
with nothing more. Assigning a second is refused, naming the first.
|
|
|
|
**What a holder declares** — only what keeps the manager and the mesh from contradicting each
|
|
other:
|
|
|
|
- the manager's package, present — and its service **with no state**: the manager's lifecycle is
|
|
the machine's. The mesh never starts, stops, enables or disables it, because stopping it takes
|
|
the link down, and a holder unassigned by mistake — or the wrong holder assigned — must not be
|
|
able to do that, nor start a second manager beside the one the machine runs. The service is
|
|
declared only so a change to the holder's settings reaches a *running* manager;
|
|
- the manager's own configuration that leaves the resolver file to the mesh (`dns=none` for
|
|
NetworkManager, `nohook resolv.conf` for dhcpcd, and nothing for systemd-networkd, which
|
|
never writes the resolver file);
|
|
- the manager's own configuration that leaves the private network's interface alone
|
|
(NetworkManager's `unmanaged-devices` naming `mesh0`; dhcpcd's `denyinterfaces mesh0`; for
|
|
systemd-networkd a network file of the module's matching `mesh0` as `Unmanaged=yes`);
|
|
- each as a drop-in beside the manager's main file where the manager reads one, and written
|
|
*into* a shared file otherwise, as a marked region the host owns
|
|
([ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md)'s idea for text files),
|
|
placed where the manager reads it as global — at the start of `dhcpcd.conf`, above any
|
|
`interface` line, because every line after one belongs to that interface;
|
|
- the service **reloaded** when a drop-in changes, never restarted — a restart drops the link,
|
|
and the link is the mesh's own channel to the machine. **A manager that cannot reload is not
|
|
restarted instead:** its setting takes effect at the manager's next start. Measured on the
|
|
adopted machines: NetworkManager (1.58) and systemd-networkd (systemd 261) both report
|
|
`CanReload=yes`; dhcpcd (10.3) reports `CanReload=no`, so its module declares no trigger at
|
|
all. Whether each setting is actually *applied* by a reload is confirmed on a machine before
|
|
the module is taken there, not assumed.
|
|
|
|
**What a holder never declares:** a link, an address, a route, a connection profile, a
|
|
wireless network or its credentials. Those are the operator's, in the sense of
|
|
[ADR 0051](0051-shared-data-is-the-operators.md): the mesh does not create, change or delete
|
|
them, and the module's `access`, if it needs one, is read-only.
|
|
|
|
## Consequences
|
|
|
|
- `resolv-conf` stays generic. The condition it could not express — "only if NetworkManager
|
|
runs" — is expressed by assigning the module for the manager that does.
|
|
- The dependency on the predecessor's `dns=none` file becomes a declared resource. On an
|
|
adopted node the holder's drop-in arrives beside the predecessor's; both say the same thing,
|
|
and the predecessor's is retired by hand after the take, like any other file the mesh
|
|
replaced under another name.
|
|
- A setting a manager reads only at its start is not in force until then. On an adopted machine
|
|
the predecessor's identical line normally already is; on a machine that was not adopted,
|
|
dhcpcd's resolver hook keeps rewriting the resolver file until dhcpcd next starts, and the
|
|
operator restarts it once, in a window of their choosing.
|
|
- A machine running two managers is found at assignment: the second holder is refused, and the
|
|
operator decides which manager the machine keeps before either module is taken.
|
|
- Workstations keep joining networks the way they always have. Under NetworkManager and
|
|
systemd-networkd the host already cooperates with the manager — its dispatcher hook wakes it
|
|
on every connectivity change — and nothing here changes that. A dhcpcd-only machine has no
|
|
such hook, and nothing here adds one.
|
|
- The seat table gains one entry: `the-uplink`, node scope, delivering nothing, decided here.
|
|
- **Not decided here:** whether the mesh should ever *offer* known networks to a machine — a
|
|
sealed, add-only list the operator curates once for all workstations. That is a different
|
|
question (the mesh holding credentials for links it must never be able to break) and gets its
|
|
own record if it is wanted.
|
|
|
|
## References
|
|
|
|
- [ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md): the closed set this seat joins;
|
|
[to-be 26](../03-DESIGN/01-to-be/26-the-seats.md): the seat table
|
|
- [ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md): written into, never over
|
|
- [ADR 0051](0051-shared-data-is-the-operators.md): what is the operator's stays the operator's
|
|
- mesh-controller `internal/catalogue/seats.go` (the seat), `internal/overlay/generator.go` (the
|
|
mesh installs on top of the machine's own networking)
|
|
- mesh-catalog `modules/networkmanager`, `modules/systemd-networkd`, `modules/dhcpcd`
|
|
- mesh-host `internal/apply/block.go` (a file written into a marked region, `at` start or end)
|