ADR 0079: foundation seats are named after their servers; issue 056 resolved

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
This commit is contained in:
2026-09-17 02:15:08 +02:00
parent f70973e222
commit ee7abe0a8e
6 changed files with 87 additions and 7 deletions
@@ -0,0 +1,63 @@
---
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.
+1
View File
@@ -108,6 +108,7 @@ python3 00-META/checks/index.py fail if stale
- **0074** — [The mesh defines a module protocol; an SDK is an implementation of it](0074-the-wire-is-specified-not-the-types.md)
- **0075** — [An artifact store is a provision; a package registry is a different one](0075-two-stores-and-which-provides-what.md)
- **0078** — [The store and the broker are ordinary modules](0078-the-store-and-broker-are-modules.md)
- **0079** — [The foundation seats are named after their servers](0079-the-foundation-seats-are-named-after-their-servers.md)
### What runs on them, and how it gets there