--- topic: the tiers status: accepted date: 2026-09-17 deciders: jochen reconstructed: false extends: 0078-the-store-and-broker-are-modules.md --- # 79. The foundation seats are named after their servers ## Context [ADR 0078](0078-the-store-and-broker-are-modules.md) 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](../04-ISSUES/056-an-adopted-module-assigned-to-a-second-node-raises-a-second-server/00-report.md) 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](0077-the-controller-and-the-foundation.md) 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](0077-the-controller-and-the-foundation.md) 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 `checkClaims` refuses two holders of a mesh-scoped seat (`mesh-controller/internal/catalogue/resolve.go`). - `TestAFoundationModuleCannotBeRaisedOnASecondNode` asserts each foundation module's second assignment is refused with *one per mesh*. - The `postgres`, `lavinmq` and `mesh-controller` manifests 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](../04-ISSUES/057-a-cross-node-consumer-is-provisioned-only-when-the-provider-is-pushed-again/00-report.md). ## References - [issue 056](../04-ISSUES/056-an-adopted-module-assigned-to-a-second-node-raises-a-second-server/00-report.md) — the gap this closes. - [ADR 0077](0077-the-controller-and-the-foundation.md) — named the controller's seat, here renamed. - [ADR 0078](0078-the-store-and-broker-are-modules.md) — asserted one postgres and one lavinmq, here enforced. - [`00-META/glossary.md`](../00-META/glossary.md) — seat and claim.