Records say what the build does: 0101 names only measured daemons; 0102 adds to lists and keeps what it writes over; 0103 names every held kind, the found-service rule, conflicting found rules, guards, and what of 0100 it replaces; designs 05, 09 and 17 in step; issue 084's diagnosis in its own file
This commit is contained in:
@@ -42,9 +42,8 @@ habit.
|
||||
## Decision
|
||||
|
||||
**A listener held by one of the operating system's own network daemons does not make a machine
|
||||
in use.** The daemons are the name resolver, the network manager, the address-configuration
|
||||
client and the time client, named in the installer's code beside the measurement that found
|
||||
them. Everything else in ADR 0100's definition stands: a running container, or any other
|
||||
in use.** The daemons are the ones the measurement found: the name resolver and the network
|
||||
manager, named in the installer's code beside that measurement. Everything else in ADR 0100's definition stands: a running container, or any other
|
||||
listener on an address other than loopback that is not ssh's, makes the machine in use, and
|
||||
genesis still names every one it counted.
|
||||
|
||||
|
||||
@@ -42,10 +42,18 @@ the runtime.
|
||||
**A file the mesh shares with software it did not install is written into, never over.** A
|
||||
file resource may say it is written *into* a structured file. The host then reads what is there,
|
||||
sets only the keys the mesh declares, keeps every other key as it found it, and records what each
|
||||
of its keys held before. Undeclared later, each key goes back to what it held, and a file the
|
||||
mesh created is removed only if nothing but its own keys is left. A file written into replaces
|
||||
nothing, so on an adopted node it is never held: it is written whether or not its module has
|
||||
been taken.
|
||||
of its keys held before. **A list is added to, never replaced**: where the machine already has a
|
||||
list under a key the mesh declares, the mesh's members are added to it and the host records
|
||||
exactly which members it added — setting the key would replace the operator's own list, the harm
|
||||
this record exists to prevent. Undeclared later, each key goes back to what it held and each
|
||||
added member is taken out again, and a file the mesh created is removed only if nothing but its
|
||||
own keys is left. A file written into replaces nothing, so on an adopted node it is never held:
|
||||
it is written whether or not its module has been taken.
|
||||
|
||||
**Whatever the host writes over without a record of it, it keeps first.** On any node, adopted
|
||||
or converged, before the host writes a file over one it has no record of making, it keeps the
|
||||
original once and says where; if it cannot keep it, it does not write. A file the mesh takes over
|
||||
is then never lost, whatever put it there.
|
||||
|
||||
**A service that re-reads its configuration on a reload is reloaded, not restarted.** A service
|
||||
resource may name what it must be *reloaded* on, beside what it must be restarted on. The
|
||||
@@ -70,8 +78,9 @@ and stays held until networking is taken; a converge preview names it among the
|
||||
## How it is checked
|
||||
|
||||
Unit tests hold the host to setting only the declared keys and keeping the rest, restoring each
|
||||
key and removing only a file it created when the resource is undeclared, refusing a file that is
|
||||
not a JSON object rather than overwriting it, never holding a file written into on an adopted
|
||||
key and removing only a file it created when the resource is undeclared, adding to a list and
|
||||
removing only the members it added, keeping the original of a file it writes over without a
|
||||
record, refusing a file that is not a JSON object rather than overwriting it, never holding a file written into on an adopted
|
||||
node, and reloading rather than restarting a service whose reload-on resource changed. A test in
|
||||
the controller holds the networking module to declaring the runtime's file written into and the
|
||||
runtime reloaded. The adoption lab bed gives the machine a runtime file with a setting of its own
|
||||
|
||||
@@ -40,9 +40,14 @@ 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 **service** the machine runs — a unit running or enabled at boot — 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 unit file a package merely ships, which nothing runs, is not found: the private network's own
|
||||
tunnel unit is one;
|
||||
- an **archive** whose target is present with no record is not unpacked or re-owned; a
|
||||
**process** whose unit file is present with no record is not written over or restarted; a
|
||||
**user** that exists with no record keeps its shell and groups;
|
||||
- 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.
|
||||
@@ -51,23 +56,34 @@ What is held is reported as held and is never removed, as ADR 0100 decides for f
|
||||
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
|
||||
derived from each *taken* module: every machine port it publishes that the filter would admit
|
||||
from the private network only, and the ports its manifest names under `guards` — which is how
|
||||
the store's port and the broker's management port are included. Only TCP is guarded. 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.
|
||||
in their action, log setting or comment as one rule, so adding the mesh's would take over the
|
||||
operator's. Where the found rule is such a twin but is not a plain allow — a deny, a limit, a
|
||||
logged allow — the opening is refused, naming that rule, and nothing is added.
|
||||
|
||||
**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.
|
||||
|
||||
**What of ADR 0100 this replaces.** ADR 0100's guard refused two ports, the foundation's own,
|
||||
checked free at genesis, and could therefore close nothing the machine served. That guard is
|
||||
replaced by the one above: a guarded port of a taken module may be one the predecessor served
|
||||
more widely, and taking the module narrows it to the private network. Everything else in ADR 0100
|
||||
stands, and its rules for files and containers now cover the other kinds listed here.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Taking a module can narrow a port the predecessor served to anyone: the guard then refuses it
|
||||
from outside the private network, as the module declares. Nothing says so yet
|
||||
([issue 086](../04-ISSUES/086-taking-a-module-narrows-a-port-without-saying-so/00-report.md)).
|
||||
- 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
|
||||
@@ -78,14 +94,15 @@ the flag on an adopted machine is refused.
|
||||
|
||||
## 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'
|
||||
Unit tests hold the host to holding a found directory, a found service, a found archive, process
|
||||
and user, and a container that would mount found data; to not holding a unit nothing runs; to
|
||||
deferring an action run in a held container; to adding no opening a found rule answers, and to
|
||||
refusing one a conflicting found rule would absorb. 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.
|
||||
outside the private network even with the found firewall admitting it, and that an operator's
|
||||
own rule for a port the mesh opens survives the opening being removed.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
@@ -158,8 +158,11 @@ keeps its mode and owner, a unit present with no record keeps its state and boot
|
||||
container that would mount found data is not created, and an action run in a held container
|
||||
waits for the cutover. **A file the machine shares is written into, never over**
|
||||
([ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md)): the host sets the mesh's keys in the object already there,
|
||||
keeps every other key, records what each of its keys held before and gives them back when the
|
||||
file is undeclared. Such a file replaces nothing, so it is never held. A service that re-reads
|
||||
keeps every other key, adds its members to a list already there rather than replacing it,
|
||||
records what each of its keys held and which members it added, and gives them back when the
|
||||
file is undeclared. Such a file replaces nothing, so it is never held. And on any node, before
|
||||
the host writes over a file it has no record of making, it keeps the original once and names
|
||||
where; if it cannot keep it, it does not write. A service that re-reads
|
||||
its configuration is **reloaded** for what it names in `reload-on`, never restarted. *How it is
|
||||
checked:* unit tests hold the host to each of these, and the adoption bed asserts the runtime's
|
||||
own settings survive adoption and a container without a restart policy keeps running.
|
||||
|
||||
@@ -300,8 +300,12 @@ cutover. The firewall found there stays in force and the mesh opens what it need
|
||||
([08-connectivity](08-connectivity.md)). **Converging is one act per node, previewed**: it refuses
|
||||
while an assigned module still holds a found container; otherwise it lists what is reachable on the
|
||||
machine now — listening sockets and published ports — whether an assigned module declares each or
|
||||
it will close, and which modules it will take, then takes them, loads the mesh's own filter in place
|
||||
of its refusal-only table and disables the found firewall without flushing it. Returning a
|
||||
it will close, which modules it will take and every held thing each will replace, and ends with a
|
||||
short digest of all of that. **The flip is made only with that digest** — the operator confirms
|
||||
the preview they read, and a preview that has changed since, or whose account of the machine is
|
||||
more than fifteen minutes old, is refused. It then takes the modules, loads the mesh's own filter
|
||||
in place of its refusal-only table and disables the found firewall without flushing it, putting
|
||||
back the forwarding policy the found firewall had set. Returning a
|
||||
converged node to adopted unloads the derived filter, restores the refusal-only table, enables the
|
||||
found firewall again and converges the openings through it; what was taken stays taken. *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
|
||||
|
||||
@@ -9,6 +9,7 @@ updated: 2026-09-22
|
||||
decisions:
|
||||
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
|
||||
- 02-DECISIONS/0101-a-machines-own-resolver-does-not-make-it-in-use.md
|
||||
- 02-DECISIONS/0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md
|
||||
- 02-DECISIONS/0067-genesis-is-a-pivot.md
|
||||
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||
@@ -117,7 +118,10 @@ refuses ([08-connectivity](08-connectivity.md)). **A converged genesis refuses a
|
||||
a container running, or a port listening on an address other than loopback that is neither ssh's
|
||||
nor held by one of the operating system's own network daemons
|
||||
([ADR 0101](../../02-DECISIONS/0101-a-machines-own-resolver-does-not-make-it-in-use.md)) — and
|
||||
names every one it counted, 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
|
||||
names every one it counted, so a forgotten flag cannot close a working machine. **A machine
|
||||
raised adopted stays adopted when genesis is run again**
|
||||
([ADR 0103](../../02-DECISIONS/0103-what-an-adopted-node-holds-and-what-its-guard-refuses.md)): the installer reads the mode from what the machine records,
|
||||
refuses a run without the flag on an adopted machine, and refuses the flag on a converged one. *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.
|
||||
|
||||
+2
-12
@@ -1,9 +1,9 @@
|
||||
---
|
||||
status: located
|
||||
opened: 2026-09-22
|
||||
located-in: [mesh-control internal/overlay, mesh-host internal/apply]
|
||||
located-in: [mesh-controller internal/overlay, mesh-host internal/apply]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
amended-design: 03-DESIGN/01-to-be/05-the-node-host.md
|
||||
---
|
||||
|
||||
# 084 — Taking the networking module on an adopted node restarts every container on the machine
|
||||
@@ -56,13 +56,3 @@ and nothing checks for it today.
|
||||
- Should an adopted node that cannot trust the registry be refused a module that needs to pull?
|
||||
Or should the refusal come earlier, when the node joins?
|
||||
|
||||
## Diagnosis
|
||||
|
||||
*2026-09-22.* Worse than reported. The controller's "merge" of the runtime's file merges an
|
||||
operator's settings into the module's content. The host then writes the result **whole**, so a
|
||||
machine's own runtime settings, including its data directory, are replaced, not added to.
|
||||
Measured on a lab machine: the runtime takes a new trusted-registry list on a reload, and a
|
||||
running container without a restart policy survives it. Decided in
|
||||
[ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md): the
|
||||
runtime's file is written into and the runtime reloaded. The hosts file stays a whole file, held
|
||||
until networking is taken.
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# 084 — Diagnosis
|
||||
|
||||
*2026-09-22.* Worse than reported. The controller's "merge" of the runtime's file merges an
|
||||
operator's settings into the module's content. The host then writes the result **whole**, so a
|
||||
machine's own runtime settings, including its data directory, are replaced, not added to.
|
||||
Measured on a lab machine: the runtime takes a new trusted-registry list on a reload, and a
|
||||
running container without a restart policy survives it. Decided in
|
||||
[ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md): the
|
||||
runtime's file is written into and the runtime reloaded. The hosts file stays a whole file, held
|
||||
until networking is taken.
|
||||
Reference in New Issue
Block a user