Found reading the second module's cutover rather than running it: the catalogue's config drops a rule the machine's has, and no step puts the two side by side.
66 lines
3.5 KiB
Markdown
66 lines
3.5 KiB
Markdown
---
|
|
status: open
|
|
opened: 2026-09-23
|
|
located-in: []
|
|
fixed-by:
|
|
amended-design:
|
|
---
|
|
|
|
# 098 — Taking a module replaces a configuration nobody compared, and the part that was the installation's is gone
|
|
|
|
## What was observed
|
|
|
|
Preparing the second module's cutover on the control-node, 2026-09-23. Found by reading, before
|
|
anything was taken — which is the only reason it is a report and not an incident.
|
|
|
|
The module declares the service's whole configuration file, at the same path the machine already
|
|
has one. On an adopted node that file is *found*, so it is held until the module is taken, and the
|
|
host records the original first — all exactly as
|
|
[ADR 0100](../../02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md) says.
|
|
|
|
The two files are not the same. The machine's says, in its own words, that packages under one
|
|
private scope are **stored locally and never proxied upstream**; every other package is proxied.
|
|
The catalogue's file has the second rule and not the first. Taking the module would therefore
|
|
replace a policy statement with its absence: the private packages would start resolving through a
|
|
public upstream that has never heard of them.
|
|
|
|
Nothing in the cutover says so. `take` replaces the held file and reports that it did. There is no
|
|
step between "held" and "replaced" at which the two contents are put side by side, and the
|
|
operator is not asked. The whole-node preview names *which* held files a flip will replace; it does
|
|
not say **how they differ**, and the per-module cutover — the step that actually does it — previews
|
|
nothing at all.
|
|
|
|
The original is kept, so this is recoverable. It is not detectable: the service starts, answers,
|
|
and serves the wrong thing.
|
|
|
|
## Why it matters beyond this instance
|
|
|
|
A configuration file is where an installation keeps what is true about *it* — which packages are
|
|
private, which realm is trusted, which paths are exceptions. A catalogue module is, correctly, the
|
|
same for every mesh. Declaring the file whole makes the second overwrite the first, and the
|
|
migration is precisely when every such file changes hands.
|
|
|
|
There is no mechanism to carry the difference. A module's file content is a fixed string: no
|
|
setting is substituted into it, and the mesh's way of writing *into* a file it does not own whole
|
|
([ADR 0102](../../02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md)) reads and
|
|
writes JSON, so a service configured in any other language cannot use it. The two remaining
|
|
options are both wrong: put one installation's private scope into a catalogue every mesh shares,
|
|
or accept the loss.
|
|
|
|
The silence is the worse half. The mesh's rule is that each step says what it changes before it
|
|
changes it; here a step changes the meaning of a running service and says only that a file was
|
|
written.
|
|
|
|
## Open questions
|
|
|
|
- Should `take` refuse, or ask, when the held file it would replace differs from what it declares —
|
|
and show the difference? What makes a difference acceptable enough to pass silently?
|
|
- Should a module be able to declare a configuration **partially**, in the service's own language,
|
|
and if so which languages must the host speak — or should such a file never be declared whole,
|
|
and always merged?
|
|
- Where does an installation's own policy live, if not in the catalogue and not in the file the
|
|
catalogue overwrites? A settings layer substituted into content would answer it; nothing
|
|
substitutes settings into content today.
|
|
- Is the kept original enough of an answer, given nothing restores it and nothing points at it
|
|
when the service starts behaving differently?
|