ADR 0100 after review: found means unrecorded; assigning prepares, taking cuts over; openings through the found firewall on both paths; the mesh guards its own ports; ports kept as node settings; a converged genesis refuses a machine in use; designs 05, 07, 08, 09 and 17 in step

This commit is contained in:
2026-09-22 16:28:31 +02:00
parent 111456abb5
commit 02c40bcab4
7 changed files with 250 additions and 136 deletions
+14 -9
View File
@@ -133,16 +133,21 @@ is the component; that one is what happens to it.
## What it finds, on an adopted node
*2026-09-22, [ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md).* A declaration says whether the node is adopted. On an adopted node
the host does not replace a file it finds at a declared path: it records what was there — its
content, so the original is kept before anything is written — and holds the file as found. A
module's files are converged when that module is assigned, because assigning a module is
migrating it; the rest when the node is converged. The host's report says which files it is
holding rather than converging, so an adopted node never reads as converged. This is the
*2026-09-22, [ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md).* A declaration says whether the node is adopted, and which of its
modules have been **taken**. *Found* is a file at a declared path, or a container at a declared
name, that the host's store has no record of writing. On an adopted node the host keeps what it
found for any module not yet taken: it records a found file's original content before anything
else, and it reports the file or container as held — a report that says what it holds, so an
adopted node never reads as converged. Once the module is taken, its resources converge like any
other. A held file that changes while held is reported as changed by something else, not
reverted; a held file is never removed, even when its module is unassigned. The host also
converges a new resource, the **opening** — a port made reachable through the firewall it found
([08-connectivity](08-connectivity.md)) — and reports which firewall it found. This is the
companion the host's ownership rule needed: *never touch what you did not create, unless adoption
made it yours — and while the node is adopted, keep it as you found it.* *How it is checked:*
unit tests hold the host to keeping a found file on an adopted node and replacing it on a
converged one, and the adoption bed asserts a found file byte for byte unchanged.
made it yours — and while the node is adopted, not until its module is taken.* *How it is
checked:* unit tests hold the host to keeping a found file and container, converging them once
taken, never removing a held file and reporting one that changed; the adoption bed asserts a found
file byte for byte unchanged until its module is taken.
## Where a declaration comes from
+12 -1
View File
@@ -11,8 +11,9 @@ code:
- mesh-catalog modules/postgres
- mesh-catalog modules/lavinmq
- mesh-lab test/integration/mesh.test.ts (a bare machine becomes a mesh)
updated: 2026-09-21
updated: 2026-09-22
decisions:
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
- 02-DECISIONS/0088-the-foundation-filters-before-anything-listens.md
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
- 02-DECISIONS/0078-the-store-and-broker-are-modules.md
@@ -172,6 +173,16 @@ ruleset the machine loaded. **Checked** by the installer's bundle test (order an
the genesis bed, which probes the machine from outside for the length of the install: the store's
port never answers, the bus's does.
**Except on a machine raised adopted**
([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)). A
machine already serving under a predecessor has a firewall of its own, and a second, stricter
table would close everything it serves. There the base ruleset is not loaded and the filter module
is not assigned until the node converges; the foundation's ports are opened through the found
firewall from the private network and refused from anywhere else on the forwarded path, which
keeps the same promise — the store's port never answers from outside — by other means. The
foundation's ports themselves are the node's, given at genesis and kept as its settings. **Checked**
by the adoption bed, which makes the same outside probe.
## Raising it
The order, from [research 011](../../01-RESEARCH/011-the-module-graph/worked-provider.md):
+20 -11
View File
@@ -442,7 +442,8 @@ stopgap until the manifest layer can carry a label and a domain separately
**Derived from what is assigned here, and from the overlay's shape** — a node's open ports are a
consequence of what runs on it and who must reach it, not an independent declaration to keep in
step by hand.
step by hand. That is true of a converged node; an adopted one keeps the firewall it was found
with until it converges (below).
**A rule names its source** ([ADR 0007](../../02-DECISIONS/0007-connectivity.md)).
A rule with no source is open, and must say so rather than appear to restrict something. `scope:`
@@ -536,16 +537,24 @@ is why the check reads packets.*
### On an adopted node
*2026-09-22, [ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md).* **The firewall found on the machine stays in force**, and the mesh
loads no table of its own there — neither genesis's base ruleset nor the derived one. The kernel
runs every table at a hook and a drop in any is final, so a second, stricter table would close
every port the machine serves. What the mesh needs reachable — its foundation's ports and each
migrated module's `listens` — it opens *through the found firewall*, as rules marked as the mesh's,
and it removes only rules it marked. Converging the node replaces the found firewall with the
derived filter in one step, after previewing which open ports will close. The mesh must speak the
found firewall in its own terms; one kind is found on the machines measured, and a machine with
another is not covered until someone writes for it. *How it is checked:* the adoption bed asserts
the found firewall's rules differ only by the mesh's marked rules, that a second machine enrols
through them, and that after the flip the declared port is open and the undeclared one closed.
loads no table of its own there — neither genesis's base ruleset nor the derived one. Every base
chain at a hook runs and a drop in any is final, whatever the other firewall is written in, so a
second, stricter table would close every port the machine serves. What the mesh needs reachable
it declares as **openings**: a port, from where, on the incoming path or the forwarded path — a
published container port is forwarded, and a firewall that filters only incoming traffic never
sees it. The host converges each opening through the found firewall in that firewall's own terms,
marks it as the mesh's, removes only what it marked, and re-checks every opening on each reconcile
so a reload or a reboot does not lose it. An opening is state, not a command, so it travels over
the link like any other resource. **The mesh protects its own ports itself**: its foundation's ports
are opened from the private network and refused from anywhere else on the forwarded path, so the
store stays unreachable from outside whether or not the found firewall filters forwarded traffic.
One kind of found firewall is spoken; a machine with another is refused adoption rather than
adopted unprotected. Converging the node previews what is reachable now — listening sockets and
published ports — and what will close, then loads the derived filter and disables the found
firewall without flushing it. *How it is checked:* the adoption bed asserts the found firewall's
rules differ only by the mesh's marked rules, that the store is unreachable from off the private
network, that a second machine enrols through the openings before and after a reload and a reboot,
and that after the flip the declared port is open and the undeclared one closed.
## 5 — Certificates
+24 -10
View File
@@ -292,14 +292,18 @@ until its migration is done and the operator converges it. A machine that was em
converged, as before
([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md)).
On an adopted node the firewall found there stays in force and the mesh opens what it needs
through it ([08-connectivity](08-connectivity.md)); a file found at a path the mesh declares is
kept until the module declaring it migrates ([05-the-node-host](05-the-node-host.md)).
**Converging is one act per node, previewed**: it lists every port the found firewall allows and
whether an assigned module declares it or it will close, then loads the mesh's own filter, retires
the found one and converges what was kept. *How it is checked:* a lab bed prepares a machine the
way a predecessor leaves one and asserts nothing on it changes until the flip, and that the flip
closes exactly what the preview said.
The operator says a node is adopted — at genesis for the control-node, in the enrolment token for
the others — and the controller records it and says so in every declaration, with the modules
**taken** on that node. On an adopted node what is found is held until its module is taken
([05-the-node-host](05-the-node-host.md)): assigning a module prepares it, taking it is its
cutover. The firewall found there stays in force and the mesh opens what it needs through it
([08-connectivity](08-connectivity.md)). **Converging is one act per node, previewed**: it lists
what is reachable on the machine now — listening sockets and published ports — and whether an
assigned module declares each or it will close, then takes every module not yet taken, loads the
mesh's own filter and disables the found one without flushing it. Returning a converged node to
adopted enables the found firewall again. *How it is checked:* a lab bed prepares a machine the way
a predecessor leaves one and asserts nothing that serves changes until a module is taken or the
node is converged, and that the flip closes exactly what the preview said.
A candidate machine is not empty. It has a package manager, probably a container runtime,
configuration somebody chose. [ADR 0005](../../02-DECISIONS/0005-the-node-host.md)
@@ -316,11 +320,16 @@ an installation. This is a *never* rule, and it earns that from the worst loss i
a tool acting on a path it did not own.
**On conflict, the machine's configuration wins.** Adoption always completes; the conflict is
flagged and reconciled afterwards. A machine in use keeps working exactly as it did.
flagged and reconciled afterwards. A machine in use keeps working exactly as it did. Two things are
not conflicts in this sense ([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)):
a module the operator has *taken* replaces what it found, because that is its cutover; and genesis
refuses a port the foundation needs that something else holds, because a foundation that cannot
bind is not adopted but broken.
**Adoption produces a briefing**, not just a result: what it found, what it took over, and what
it could not resolve — with each line marked `ok`, `kept`, `unknown` or `failed`, and the overall
outcome **derived** from the worst line rather than stated alongside it.
outcome **derived** from the worst line rather than stated alongside it. A file or container the
host is holding on an adopted node is a `kept` line for as long as it is held.
---
@@ -384,6 +393,11 @@ runtime because a declaration changed would stop every container on the node.
---
*On an adopted node* ([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)),
what the host is holding was found, not made, so nothing held is ever removed: a held file whose
module is unassigned stays where it is.
## enrolled ⇄ disconnected
Not a failure. Not degraded. A situation
+21 -11
View File
@@ -101,16 +101,23 @@ that runs it.
### Genesis on a machine in use
*2026-09-22, [ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md).* A control-node that is already running a predecessor mesh is raised
**adopted**. The predecessor's control on it is stopped first — the daemons that write its
configuration — and its services keep running. **Every port the foundation binds is an input to
genesis**, not a constant in the bundle or the catalogue, and genesis checks each is free before it
raises anything, refusing with the name of what holds it. On such a machine the store's and the
bus's usual ports were free and the registry's, the broker's management port and the private
network's port were held. Genesis adopted **does not load the base ruleset**: the machine's own
firewall already filters, and the mesh opens its foundation's ports through it
([08-connectivity](08-connectivity.md)). *How it is checked:* the adoption bed raises genesis
with the registry's port held and asserts the refusal names its holder, then with another port
given asserts the foundation comes up and the machine's service is still reachable.
**adopted**, said so by the operator. The operator stops the predecessor's control on it first —
the daemons that write its configuration — and its services keep running. **Every port the
foundation binds is an input to genesis**, checked free before anything is raised, refused with the
name of what holds it; the ports given become the node's settings for the foundation's modules, so
adopting the foundation as modules keeps them, and every reader of them — the filter, the base
ruleset, the private network's endpoint, the addresses consumers are given — reads them there. On
the control-node measured, the store's and the bus's usual ports were free and the registry's, the
broker's management port and, as the private network's hub, its port were held. Genesis also
checks the private network's range does not overlap a tunnel the predecessor runs. Genesis adopted
**does not load the base ruleset**: the machine's own firewall already filters, and the mesh opens
its foundation's ports through it and refuses them from outside itself
([08-connectivity](08-connectivity.md)). **A converged genesis refuses a machine in use** — an
active firewall or listeners that are not the mesh's — so a forgotten flag cannot close a working
machine. *How it is checked:* the adoption bed raises genesis converged on a machine in use and
asserts the refusal, then adopted with the registry's port held and asserts the refusal names its
holder, then with another port given asserts the foundation comes up, stays on that port once
adopted as modules, and the machine's service is still reachable.
## After the pivot, and still part of installing
@@ -157,7 +164,10 @@ What remains after *that* belongs to somebody else: adding machines, and decidin
A machine joins with the host binary and a token. It does not raise a foundation, does not install a
registry, and is never enrolled twice. The mesh already knows how to tell a machine what to be;
joining is the point at which a machine starts listening.
joining is the point at which a machine starts listening. A machine in use joins **adopted**: the
token says so, the operator has stopped the predecessor's control on it first, and from then on it
keeps what it has until each module is taken
([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)).
## Where the line falls