Files
hq/02-DECISIONS/0163-taking-a-module-over-is-a-comparison.md
T

12 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
the mesh accepted 2026-10-01 jochen false 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md

163. Taking a module over is a comparison: what it compares, what it refuses, and what it carries

Context

On an adopted machine the mesh holds what it finds until the module is taken, and taking is the cutover (ADR 0100). The whole-node flip is previewed and confirmed by digest; the per-module cutover, the step that actually replaces a running service, previews nothing. take names the held things the next push will replace and where each original is kept. It does not say how the module's version of each differs from what runs. Ten issues from the first migrations are the same omission seen from ten sides:

  • a port narrowed from everywhere to the private network, unannounced (086);
  • a configuration file replaced whole, dropping the one line that was the installation's own (098);
  • an image pin that had aged into a downgrade, discovered by three minutes of outage (099);
  • a secret minted for a service that already had one, with no way to carry the existing value in because it was a required secret and not the module's own (100);
  • a container moved onto the module's own network, out of reach of the neighbour that called it by name (101);
  • a resource whose target changed, leaving the old container running with no record naming it (097);
  • a volume path that changed without the running container noticing, because the host does not compare that field (126);
  • a build that deployed at once because the module's policy said so, racing a data move (126);
  • a setting accepted where it was set and refusing the whole machine where it was read (096);
  • a module that could not take over what genesis raised, because the two differed in name, network, data and image (090);
  • a successor that could not stand beside its predecessor at all, answered by ADR 0104's adapter (093).

What the host records of a found thing is enough to compare from: a file's original, kept, with its digest, mode and owner; a container's id and whether it ran; whether anything changed it since. What it does not yet record is what a comparison needs most: the found container's image and when that image was made, the networks it is on and who else is on them, what it mounts, what it publishes. And the controller's rule that a machine is told everything or nothing turns one impossible statement into a machine nobody can talk to.

Decision

1. A take is previewed, and the preview is a comparison. For every held thing the module would replace, take puts what runs beside what the module declares and says the difference:

  • a container: its image against the module's, with each image's creation date so older and newer have a meaning; its name; its networks, and the other containers on each found network that is not the module's; its published ports and the reach of each, found firewall and guard included; its mounts against the module's volumes and paths;
  • a file: the kept original against the declared content, as a difference, not two digests;
  • a secret the module takes that the mesh minted and nobody accepted, when the service's data was found — a service that already runs already has a value;
  • the module's settings on that machine, composed against its definition.

take without --yes prints the comparison and stops; take --yes <digest> cuts over exactly what was previewed, the way the flip is confirmed, and a preview whose account of the machine is older than the flip allows is refused the same way. The host supplies the facts in its report of what it holds: the found container's image and its creation date, its networks and their members, its mounts and published ports.

2. Three differences refuse by default, each overridden by naming it. An image older than the one running, by creation date — --downgrade, said once and recorded. A declared file that differs from the kept original — --replace <path>, or the module declares the file partially and writes into it (ADR 0102), which is the right answer wherever the file is the service's own and the format allows it. A minted, unaccepted secret for a service whose data was found — accept the value first, or --mint <name> to say the service shall take a new one. Two differences are said and not refused: a port whose reach narrows, and a found network whose other members may reach the container by name, each member named; both are the operator's to weigh, and the words are there to weigh them.

3. A secret the mesh would mint may be accepted instead, own or required. secret accept reaches a module's required secrets, not only its own: the value is a fact about the machine, and the mesh's job at a take is to learn it. The accepted value is sealed to the module as a minted one would be, and the provider that would have minted it is told it has one. Whether one accepted value should reach every consumer of a provider at once is issue 165's question and the next group's.

4. A taken container may keep a found network, for a while, by a setting. A per-machine setting names a found network the module's container also joins, so a neighbour that resolves it by name keeps resolving it. It is migration scaffolding in the sense of ADR 0104: assigned only on an adopted machine, reported while it stands, removed when the neighbours are taken, and the preview names it. Taking a group of modules at once is not decided here; the setting makes the order free.

5. The host compares every field it writes, and removes what it can no longer name. A container is current when every field the host would write agrees with the one running — volumes and paths included; a field the host cannot compare recreates rather than passes. The host's record keeps a resource's former targets: a container or file the host wrote under a name or path the declaration no longer names is removed on the next apply and said; what was found is never removed, as ADR 0100 says. And the host answers the question nothing answered on 2026-09-23: its report lists what runs on the machine that the mesh neither wrote nor holds — containers and listeners — as strays, so a thing left behind is seen the day it is left.

6. A setting is judged where it is stored, and an impossible one costs a module, not a machine. Storing a setting composes it against the module's current definition and refuses with the node, module, layer and key when it cannot work. A definition that later moves under a stored setting makes composition leave that module out of the machine's declaration — its held things kept, its containers untouched — and say the statement by name; the machine is still told everything else. A machine is told everything or nothing about what it is told; what it is not told is said.

7. What genesis raises, it raises as the module that succeeds it declares — name, network, data directory and image — so the module adopts it by the found rule that already exists, and a module meant to succeed a bootstrap service that it cannot adopt is a fault of genesis, found by a test that raises and then assigns. build says when a policy will act on its result, so a person choreographing a data move knows which module will not wait; under ADR 0162 the roll-out is the plan's, and the plan says it too.

Consequences

  • take becomes the per-module twin of the flip: preview, digest, confirm. The flip's own preview gains the same comparisons for every module it takes.
  • The host's report of what it holds grows by the found container's image and creation date, networks and members, mounts and published ports; its store keeps former targets and strays.
  • Issues 086, 098, 099, 100, 101 close on rule 1 and 2; 097 and 126 on rule 5; 096 on rule 6; 090 on rule 7; 093 is closed by ADR 0104's adapter, which runs.
  • Nothing here changes what an adopted machine keeps or when: found stays held, held is never removed, the original is kept before anything is written.

How this is checked

Rule Checked by
The host reports a found container's image and creation date, networks and their members, mounts and published ports host unit tests over a fake runtime; the adoption bed's report
take without --yes previews every held thing's difference and changes nothing; --yes with the digest cuts over; a stale account is refused controller tests over a fixture report: a differing file, an older image, a narrowed port, a shared network, a minted secret
An older image, a differing file and a minted secret for found data refuse without their override the same tests
A found network kept by a setting is joined, reported and named in the preview a host test and a controller resolution test
secret accept takes a required secret an inventory test; the provider is told
Every container field is compared; a former target the host wrote is removed and said; what was found is not host tests: a volume path change recreates; a renamed container's predecessor is removed; a found one under the old name is kept
Strays are reported a host test over a fake runtime with a container nobody declared
A setting that cannot compose is refused where stored, naming node, module, layer, key; a definition moving under one leaves that module out and says so controller tests
Genesis raises the forge as its module declares it a genesis test that raises, assigns, and finds the module holding rather than raising a second
Live the next cutover on an adopted machine: take shows the comparison, refuses the downgrade if there is one, and the service keeps its configuration and its secret

References