ADR 0100 after re-review: the bus and registry stay reachable for enrolment; the mesh guards the store in a table that only refuses; a machine in use defined; the flip refuses while a found container is held; held containers and returning to adopted spelled out

This commit is contained in:
2026-09-22 16:32:37 +02:00
parent 02c40bcab4
commit f3152d827f
7 changed files with 112 additions and 74 deletions
+8 -2
View File
@@ -139,8 +139,9 @@ name, that the host's store has no record of writing. On an adopted node the hos
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
other. What is held is never removed, even when its module is unassigned, and a held file or
container that changes while held is reported as changed by something else, not reverted or
restarted. 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
@@ -212,6 +213,11 @@ ready; the current bundle simply does not. **All of them are built:**
| `container` | **built** | pinned by digest ([ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md)); identified by a label carrying a digest of the declaration that made it, because a runtime normalises what it is given and that is indistinguishable from drift |
| `action` | **built** | bundle-only ([ADR 0005](../../02-DECISIONS/0005-the-node-host.md)); verify is mandatory and is the idempotency check as well as the read-back |
One more shape is decided and not yet built: **`opening`**, on adopted nodes only
([ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md)) — a port
made reachable, from where, on the incoming or forwarded path, through the firewall found on the
machine, marked as the mesh's and re-checked on every reconcile.
**A service says what it must reflect, and that is declared state rather than a command.**
`restart-on` names files whose change means the unit must be restarted — because a running service
does not re-read its configuration, and replacing a file, finding the service already running and
+4 -2
View File
@@ -178,8 +178,10 @@ port never answers, the bus's does.
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
firewall, and a table of the mesh's that only refuses keeps the store's port and the broker's
management port from anyone off the private network — the same promise, the store's port never
answering from outside, kept by other means. The bus and the registry stay reachable from anywhere,
as they are here, because a node enrols and pulls before it has a private-network address. 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.
+25 -17
View File
@@ -537,24 +537,32 @@ 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. 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
loads no table there that drops by default or that accepts — 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, and
an accept in one would open nothing the found firewall drops. 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.
sees it. The controller derives them from what the filter would be derived from: the assigned
modules' `listens`, the hub's port, the foundation's ports. 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 for
longer than one reconcile. An opening is state, not a command, so it travels over the link like any
other resource. **The mesh guards its own ports in a table of its own that only refuses** —
accepting by default and holding nothing but refusals, so it cannot close what the machine serves,
and the found firewall's reload does not touch it. It refuses the store's port and the broker's
management port from outside the private network, matched on the port the packet was sent to; the
bus, the registry and the hub's port stay reachable from anywhere, as a node enrols and pulls
before it has a private-network address. One kind of found firewall is spoken; a machine with none
needs no openings, and a machine with another kind is refused adoption. Converging the node refuses
while a found container is still held; otherwise it previews what is reachable now — listening
sockets and published ports — what will close and which modules it will take, then loads the
derived filter in place of the refusal-only table 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 before and after
the found firewall reloads, that a machine not yet enrolled reaches the bus, 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
+4 -5
View File
@@ -391,12 +391,11 @@ should be and sends it; the host applies the difference and removes what is no l
The host removes what it *made* and leaves what it merely *configured*. Uninstalling a container
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.
what the host is holding was found, not made, so nothing held is ever removed: a held file or
container whose module is unassigned stays where it is.
---
## enrolled ⇄ disconnected
+5 -5
View File
@@ -110,11 +110,11 @@ ruleset, the private network's endpoint, the addresses consumers are given — r
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
**does not load the base ruleset**: the machine's own firewall already filters; the mesh opens its
foundation's ports through it and keeps the store from outside with a table of its own that only
refuses ([08-connectivity](08-connectivity.md)). **A converged genesis refuses a machine in use** —
a container running, or a port listening that is neither ssh nor the operating system's own — 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.