"One postgres, one lavinmq" (ADR 0078) holds only by genesis assigning the adopted modules to the control-node alone; nothing enforces it. A second assign raises a divergent second server, silently. Open questions: a mesh-scoped exclusive seat, or explicit adoption the controller refuses to place elsewhere. https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
2.1 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | ||
|---|---|---|---|---|---|---|
| open | 2026-09-17 |
|
056 — An adopted module assigned to a second node raises a second server
Symptom
The store and broker are adopted in place (ADR 0078,
issue 051): genesis raises mesh-store/mesh-broker on the control-node, and assigning the
postgres/lavinmq module there makes the module reconcile the already-running container instead
of raising a new one. Adoption works because the container is already running on that node.
Nothing stops the same module being assigned to a second node. On a node where no
mesh-store is running, the applier finds no container by that name and raises one — a second
postgres, a second broker. The whole point of Phase 3 — "one postgres, one lavinmq" — holds only
by the convention that genesis assigns these modules to the control-node alone; it is not enforced.
Why it matters
"There is one store and one broker" is stated as a property of the mesh, and a property enforced by
nothing is indistinguishable from a wrong one. A single extra assign — the ordinary verb an
operator types — silently produces a divergent second server holding none of the first's data, and
consumers resolved to it get an empty store. The failure is silent and far from its cause.
The exclusive foundation modules are the mesh's clearest case of "there is one of me," yet unlike a mesh-scoped seat, an adopted module carries no claim that the resolver would refuse a second holder for.
Open questions
- Should
postgres/lavinmqclaim a mesh-scoped exclusive seat (the waymesh-controllerclaimsthe-controller), so the resolver refuses a second assignment by the same mechanism that keeps one controller? - Or should adoption be explicit — a module that adopts a foundation container declares it, and the controller refuses to place it on a node whose foundation did not raise that container?
- How is "one postgres, one lavinmq" checked, rather than assumed — in the resolver, in
status, or in a bed that tries the second assignment and asserts the refusal?