Files
hq/02-DECISIONS/0117-a-machines-uplink-is-a-seat.md
T

6.8 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-09-26 jochen false 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. Of the four machines adopted so far, one runs systemd-networkd, two run NetworkManager, and 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). 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, and its service running and enabled at boot;
  • the manager's own configuration that leaves the resolver file to the mesh (dns=none for NetworkManager, nohook resolv.conf for dhcpcd, and for systemd-networkd the equivalent that stops it handing DNS to a resolver the machine does not use);
  • the manager's own configuration that leaves the private network's interface alone (NetworkManager's unmanaged-devices naming mesh0; for systemd-networkd, no network file of the module's matches it);
  • each as a drop-in beside the manager's main file where the manager reads one, and written into a shared file otherwise (ADR 0102);
  • 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: 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 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. The host already cooperates with the manager — its dispatcher hook wakes it on every connectivity change — and nothing here changes that.
  • 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.