The store and broker were "one per mesh" by convention only. Each foundation module now claims a mesh-scoped seat named after the server it guards — postgres/mesh-store, lavinmq/mesh-broker — and the controller's seat is renamed the-controller -> mesh-controller so all three follow one rule. The resolver refuses a second holder, closing 056. Glossary, the foundation and installation docs, and the decisions index follow the new name. https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
3.4 KiB
topic, status, date, deciders, reconstructed, extends
| topic | status | date | deciders | reconstructed | extends |
|---|---|---|---|---|---|
| the tiers | accepted | 2026-09-17 | jochen | false | 0078-the-store-and-broker-are-modules.md |
79. The foundation seats are named after their servers
Context
ADR 0078 settled that the store and broker are the
ordinary postgres and lavinmq modules, adopted in place on the control-node, and that a mesh
runs one postgres and one lavinmq. But that singularity held only by convention: genesis
assigns them to the control-node alone. Issue 056
recorded the gap — a second assign to another node finds no container of that name there and
raises a second server, holding none of the first's data, and nothing refuses it.
The mesh already has the mechanism for "there is one of me": a mesh-scoped exclusive seat.
ADR 0077 gave the controller one, which it named
the-controller. Nothing gave the store and broker theirs.
Decision
Each foundation module claims a mesh-scoped seat named after the server it guards:
mesh-controller, mesh-store, mesh-broker. So the postgres module claims mesh-store and
the lavinmq module claims mesh-broker; the resolver refuses a second holder mesh-wide, the same
way it keeps one hub and one controller. A second assign is now a refusal at resolution — one
per mesh — not a silent second server.
The controller's seat, which ADR 0077 named
the-controller, is renamed mesh-controller under this same rule, so all three foundation seats
follow one convention: the seat is the server. (The module mesh-controller and its seat now share
a name, which is the point — there is one of that server, and the seat says so.)
This does not tie a foundation module to the control-node — a mesh-scoped seat forbids a second holder, not a wrong single one. Adoption still requires the container to already be running where the module lands, which genesis arranges; the seat closes the "two servers" gap, and the control-node convention remains what puts the one holder in the right place.
How this is checked
- The resolver's
checkClaimsrefuses two holders of a mesh-scoped seat (mesh-controller/internal/catalogue/resolve.go). TestAFoundationModuleCannotBeRaisedOnASecondNodeasserts each foundation module's second assignment is refused with one per mesh.- The
postgres,lavinmqandmesh-controllermanifests declare the seat.
Consequences
"One store, one broker, one controller" is now a property the mesh enforces rather than a convention it hopes for. The silent operational edge that remains — a cross-node consumer provisioned only when the provider is pushed again — is separate, and tracked as issue 057.
References
- issue 056 — the gap this closes.
- ADR 0077 — named the controller's seat, here renamed.
- ADR 0078 — asserted one postgres and one lavinmq, here enforced.
00-META/glossary.md— seat and claim.