Files
hq/04-ISSUES/096-a-setting-that-cannot-work-is-stored-and-stops-the-node/00-report.md
T
jschoubben c4151e6bc4 ADR 0163 built: the take digest, the minted-secret refusal, the networks setting, settings judged where stored, genesis raising the forge as declared; issues 096, 097, 126 resolved
The record gets its built note; designs 05 and 09 the revisions; 086, 098,
099, 100 and 101 stay located because every machine is converged and the
record's live row — a take read on an adopted machine — has not been run;
090 is built in part, its network difference left for the take to say.
2026-10-01 23:45:57 +02:00

75 lines
4.3 KiB
Markdown

---
status: resolved
opened: 2026-09-23
located-in: [mesh-controller cmd/mesh-controller/adoption.go (take previews nothing), mesh-host internal/apply (the comparison and the record)]
fixed-by: mesh-controller (the pull request after 201: JudgeSettings, LeftOut), mesh-host 64 (left_out kept)
amended-design:
---
# 096 — A setting that cannot work is accepted where it is set, and stops the node where it is read
## What was observed
On the control-node during the first module's migration, 2026-09-23, and reproduced since against
the controller's own tests.
A per-node port setting was accepted and stored. Composing that node's declaration then failed on
it, and because a node is told everything or nothing, **every push to that machine was refused**
until somebody found the setting and removed it. The message named a port number. It did not name
the setting, the layer it was stored in, or the node it had stopped; nothing said that a stored
statement was the reason the machine had gone quiet.
The specific reason that setting could not work is [issue
094](../094-a-port-published-as-the-machine-side-cannot-be-moved/00-report.md), and it is fixed.
This issue is the shape that surrounded it, which is not:
- **The place that stores a setting has no manifest in view.** It checks what it can without one —
that a value is a port, that ssh keeps its own, that two modules on the machine do not claim the
same machine port — and leaves anything that needs the module's own declaration to composition.
So a key naming a port the module does not publish is stored today, and a typo is stored today,
and both are found later, from the far end.
- **Composition fails the node, not the module.** One unusable statement about one module refuses
the whole declaration, so the other modules on that machine stop being told anything either —
including modules that were fine before the setting existed.
## Why it matters beyond this instance
The two together turn a typo into an outage of the control link for a machine, at a distance from
the thing that caused it. The delay is the damage: a refusal at the moment of setting is a
correction, and the same refusal a push later is a machine nobody can talk to, found by whoever
next notices it is not being updated.
It generalises past ports. Any setting whose validity depends on the module's declaration has this
shape — a value that is well-formed on its own and impossible against the manifest. Ports are
merely where the mesh found it first, because migrating a service is when settings get written.
It also touches a rule the mesh states elsewhere: what refuses, refuses early and by name. A
refusal that names a number rather than the statement that produced it cannot be acted on without
knowing the code.
## Open questions
- Should storing a setting compose it against the module's manifest first — and if so, against
which version, given the catalogue moves and a manifest that was right when the setting was
written may not be later?
- Or should the guard be at the far end: composition refuses that **module** and sends the rest of
the node, so an impossible statement costs one service and not the machine?
- Either way, what does a refusal have to name — the node, the module, the layer and the key — for
an operator to undo it without reading the source?
- Is there anything a node must never be pushed without, such that sending a partial declaration is
worse than sending none?
## Decided, 2026-10-01
[ADR 0163](../../02-DECISIONS/0163-taking-a-module-over-is-a-comparison.md), rule 6: judged where stored; an impossible statement costs a module. Building follows,
host first, then the controller's `take`.
## Resolved, 2026-10-02
One judgement, in the catalogue, run where a setting is stored and where a machine is composed. Stored,
a setting that cannot compose with the module's current definition is refused naming the node, the
module, the layer and the key; a key that reaches nothing is refused there too. Composed, a definition
that moved under a stored setting leaves that module out of the machine's declaration — the envelope
names it, the host keeps what it holds and wrote for it, `plan` and `push` say it — and the machine is
told everything else. A stray setting no longer refuses the whole machine where it is read.