--- topic: the mesh status: accepted date: 2026-09-22 deciders: jochen reconstructed: false extends: 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md --- # 103. What an adopted node holds, and what its guard refuses ## Context [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md) holds a found file or container until its module is taken. It also has the mesh guard the store's port and the broker's management port with a table that only refuses. Two independent reviews of the build found both rules drawn too narrowly and, for the guard, in the wrong place: 1. **Other kinds of resource reach what was found.** An untaken module's directory is re-owned and re-moded if a predecessor's directory is already at its path, and a database refuses to start on a data directory whose mode changed. A service of the same name as a predecessor's unit is started, stopped or re-enabled. An action run *in* a held container runs inside the predecessor's service. A container that is not found under its own name is created, and can mount the predecessor's data beside the predecessor's own container. 2. **The guard's two ports are not the only ones a found firewall misses.** A published container port is forwarded, not received, and a firewall that filters only incoming traffic never sees it (ADR 0100's own Context). The broker's plaintext port is published on every interface and admitted by the filter from the private network only. So on an adopted node it is reachable from anywhere. The same holds for any published port a module restricts to the private network. 3. **The guard refuses by port alone.** On a machine that routes for others, such as a predecessor's private-network hub, a packet for another machine's database port is refused as well. And a port the guard refuses for a module that is assigned but not taken may still be the predecessor's own, serving the predecessor's other machines. ## Decision **Found covers every kind that can reach what the machine already has.** On an adopted node, for a module not yet taken: - a **directory** present with no record is held: its mode and owner are left, and nothing in it is touched; - a **service** whose unit is present with no record is held: its state and whether it starts at boot are left. A reload named by the module still happens, since a reload stops nothing ([ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md)); - a **container** not found by its name is still held if it would mount a path or a volume that is present with no record, because it would share the predecessor's data; - an **action** or one-off step run *in* a held container is held until the module is taken. What is held is reported as held and is never removed, as ADR 0100 decides for files and containers. **The guard refuses what the found firewall may miss, for taken modules only.** Its ports are derived, not listed. They are every machine port a *taken* module publishes that the filter would admit from the private network only, together with the store's port and the broker's management port. A port of a module that is assigned but not taken is not guarded: it may still be the predecessor's. The guard matches only packets addressed to this machine. Traffic the machine routes for others is never its business. **An opening already answered by a found rule is not added.** If the found firewall already admits what an opening says, the opening is reported as satisfied by the found rule, and the mesh adds nothing and later removes nothing. The found firewall treats two rules differing only in their comment as one rule, so adding the mesh's would take over the operator's. **A machine raised adopted stays adopted if genesis is run again.** The installer reads the node's mode from what the machine records, not only from the operator's flag. A run without the flag on an adopted machine is refused. ## Consequences - An untaken module on an adopted node can come up only beside what was found, never on top of it: whatever would share the predecessor's data waits for the cutover. - The guard grows with the modules taken, and a module's published private-network port is protected on an adopted node the way the derived filter protects it on a converged one. - Guarding only taken modules means a foundation port on a node joining adopted is guarded once its module is taken, not when it is assigned. Genesis takes the foundation's modules, so the control-node's store is guarded from the first push. ## How it is checked Unit tests hold the host to holding a found directory, a found service and a container that would mount found data, to deferring an action run in a held container, and to adding no opening a found rule answers. They hold the controller to deriving the guard from taken modules' private-network published ports, and the guard's text to matching only this machine's addresses. The installer's tests hold a re-run without the flag on an adopted machine to a refusal. The adoption lab bed asserts that the broker's plaintext port is unreachable from outside the private network, and that an operator's own rule for a port the mesh opens survives the opening being removed. ## References - [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md), [ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md)