0118: give a unit back the state it was found in — never-stop broke the converge rollback; process removal found and fixed

This commit is contained in:
jochen
2026-09-27 00:58:17 +02:00
parent ebd19c4c6c
commit 13208f0f48
3 changed files with 56 additions and 25 deletions
@@ -7,7 +7,7 @@ reconstructed: false
extends: 0102-the-mesh-writes-into-a-shared-file-never-over-it.md extends: 0102-the-mesh-writes-into-a-shared-file-never-over-it.md
--- ---
# 118. Undeclaring removes what the mesh made, gives back what it changed, and leaves the machine's units as they are # 118. Undeclaring removes what the mesh made, and gives a unit back the state it was found in
## Context ## Context
@@ -57,34 +57,53 @@ keeps the dangerous behaviour as the default for exactly the units that matter m
runtime, the ssh daemon, the network — and each new module is one forgotten field away from a runtime, the ssh daemon, the network — and each new module is one forgotten field away from a
machine that goes dark when it is unassigned. machine that goes dark when it is unassigned.
**3. The line is ownership.** Chosen. **3. Never stop a unit the mesh did not create.** Rejected, found while implementing it. The
mesh's packet filter is a unit the distribution installed and the mesh started at converge;
returning a node to adopted ([ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md))
unloads it by undeclaring it. Never stopping it would leave the mesh's filter loaded beside the
predecessor's firewall re-enabled — the one rollback a converge promises, broken. Who wrote the
unit file is not the line; what the mesh *did* to the unit is.
**4. Give the unit back the state it was found in.** Chosen.
## Decision ## Decision
**Undeclaring removes what the mesh made, gives back what it changed, and leaves what was the **Undeclaring removes what the mesh made and gives back what it changed.** For a unit the mesh
machine's as it is.** For a unit, that means: **the host never stops, starts, disables or did not create, what it changed is the unit's state, so that is what is given back: **the host
enables a unit it did not create when that unit stops being declared.** An undeclared `service` records the state it first found the unit in, and undeclaring returns the unit to it.**
is forgotten — reported as such — and the unit keeps whatever state it is in.
- A unit the mesh *did* create — a `process` resource's unit, which the host writes — is still - **Recorded once**, the first time the host applies the service — whether it was running, and,
stopped and removed with it. That is the mesh's own code. where the declaration sets it, whether it was enabled at boot — and carried in the host's
record from then on. Later applies never overwrite it: by then the unit's state is the mesh's
doing.
- **A unit found running is left running.** The container runtime, the ssh daemon, a network
manager: running before the mesh arrived, running after it leaves.
- **A unit the mesh started is stopped again**, and one it enabled is disabled again — the packet
filter a converge loaded, which returning to adopted unloads.
- **Never started on the way out.** A unit the mesh stopped is not started again when its
declaration goes; starting something is a decision, and the operator makes it.
- **Unknown is left alone.** A record written before the host kept what it found says nothing
about the unit before the mesh; the unit is left exactly as it is. A unit left running can be
stopped by the operator; one stopped by mistake may be the link the operator needed to do it.
- A unit the mesh *did* create — a `process` resource's unit and bundle — is stopped and removed
with its declaration. That is the mesh's own code. (Before this record there was no way to
remove one at all: an undeclared process failed every apply on its node.)
- The service's settings the mesh wrote are given back by their own resources (a kept original - The service's settings the mesh wrote are given back by their own resources (a kept original
restored, a region or keys removed). A running service keeps running on what it read until it restored, a region or keys removed). A running service keeps running on what it read until it
next reads its configuration; the mesh does not restart it to make it notice. next reads its configuration; the mesh does not restart it to make it notice.
- A service declared with no `state` (ADR 0117) remains the way to say the mesh must not - A service declared with no `state` (ADR 0117) remains the way to say the mesh must not
**start** a unit either; this record is about what happens when a declaration goes away, and **start** a unit either; undeclared, it is forgotten.
covers every service.
- An operator who wants a unit stopped when its module goes says so first: declare it
`stopped`, push, then unassign. Stopping a machine's unit is a decision, made visibly — never
a side effect of removing a module.
## Consequences ## Consequences
- Unassigning the private network no longer stops the container runtime; unassigning sshd no - Unassigning the private network no longer stops the container runtime; unassigning sshd no
longer stops ssh; no uplink module can take a machine's network down on its way out. longer stops ssh; no uplink module can take a machine's network down on its way out.
- The host's removal report says "forgotten; the unit is the machine's" where it used to say - The host's removal report says what it gave back — "restored: stopped again, as the host
"stopped". A module that relied on its daemon stopping when unassigned — none in the catalogue found it" — or "forgotten: it was running before the mesh; left as it is" where it used to say
today does on purpose — needs the explicit step above. "stopped". Its plan names each unit an undeclare will stop, before it does.
- On a fresh machine where the mesh installed and started a service, unassigning its module
stops it again — the mesh gave, the mesh takes back. An operator who wants it kept declares it
in a module of their own, or starts it themselves after.
- A daemon can keep running after its module is gone, on configuration that was taken back from - A daemon can keep running after its module is gone, on configuration that was taken back from
under it. That is a visible, running process the operator can see and stop; the alternative under it. That is a visible, running process the operator can see and stop; the alternative
was an invisible outage. was an invisible outage.
+1 -1
View File
@@ -200,7 +200,7 @@ python3 00-META/checks/index.py fail if stale
- **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)* - **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)*
- **0115** — [One assignment of a module per node: the module's name is the assignment's identity](0115-one-assignment-of-a-module-per-node.md) *(proposed)* - **0115** — [One assignment of a module per node: the module's name is the assignment's identity](0115-one-assignment-of-a-module-per-node.md) *(proposed)*
- **0117** — [A machine's uplink is a seat: the mesh configures the manager, never the link](0117-a-machines-uplink-is-a-seat.md) - **0117** — [A machine's uplink is a seat: the mesh configures the manager, never the link](0117-a-machines-uplink-is-a-seat.md)
- **0118** — [Undeclaring removes what the mesh made, gives back what it changed, and leaves the machine's units as they are](0118-undeclaring-leaves-the-machines-units-running.md) - **0118** — [Undeclaring removes what the mesh made, and gives a unit back the state it was found in](0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md)
### How it is built ### How it is built
@@ -2,7 +2,7 @@
status: located status: located
opened: 2026-09-27 opened: 2026-09-27
located-in: [mesh-host internal/apply/apply.go (remove)] located-in: [mesh-host internal/apply/apply.go (remove)]
amended-design: 02-DECISIONS/0118-undeclaring-leaves-the-machines-units-running.md amended-design: 02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md
--- ---
# 130 — undeclaring a service stops it, even one the mesh only reloads or only keeps running # 130 — undeclaring a service stops it, even one the mesh only reloads or only keeps running
@@ -45,10 +45,22 @@ reaches it by.
## Resolution ## Resolution
[ADR 0118](../../02-DECISIONS/0118-undeclaring-leaves-the-machines-units-running.md): undeclaring [ADR 0118](../../02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md):
removes what the mesh made, gives back what it changed, and leaves the machine's units as they undeclaring removes what the mesh made and gives back what it changed. The host records the state
are. An undeclared `service` is forgotten, never stopped — the host did not create the unit. it first found a unit in, and undeclaring returns the unit to it — a unit found running (the
That covers the runtime, sshd and the uplink modules at once, without each module opting out; container runtime, sshd, a network manager) is left running; one the mesh started (the packet
the private network and the sshd module need no change. A `process`'s unit, which the host does filter a converge loaded) is stopped again; nothing is started on the way out; a record from
write, is still stopped and removed. The unassign preview asked for above is left open for the before the host kept what it found leaves the unit alone. That covers the runtime, sshd and the
controller. uplink modules at once, without each module opting out; the private network and the sshd module
need no change.
A first draft — never stop a unit the mesh did not create — was rejected while implementing it:
returning a converged node to adopted unloads the mesh's filter by exactly this path.
Found on the way: an undeclared `process` failed every apply on its node (`remove` had no case
for it). Now removed with its unit, timer and bundle — the mesh's own code. `user` and `archive`
have the same gap and are left for their own decisions: removing a login or unpacked files is not
something to settle in passing.
The unassign preview is partly answered — the host's plan names each unit it will stop — and the
controller's side is left open.