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:
+3
-2
@@ -47,8 +47,9 @@ another — and a mesh you cannot name precisely is a mesh two people describe d
|
|||||||
- **seat** — a named position at a scope (node / site / mesh) with a **capacity**. A capacity-1 seat
|
- **seat** — a named position at a scope (node / site / mesh) with a **capacity**. A capacity-1 seat
|
||||||
is exclusive (one holder); a higher-capacity seat is a **bench** (several holders coexist).
|
is exclusive (one holder); a higher-capacity seat is a **bench** (several holders coexist).
|
||||||
- **claim** — a module taking a spot on a seat. `claims: [{name, scope}]` in a manifest. A
|
- **claim** — a module taking a spot on a seat. `claims: [{name, scope}]` in a manifest. A
|
||||||
mesh-scoped exclusive claim is how the mesh says "there is one of me" — e.g. `mesh-controller`
|
mesh-scoped exclusive claim is how the mesh says "there is one of me". A foundation seat is
|
||||||
claims `the-controller`.
|
named after the server it guards: the `mesh-controller`, `postgres` and `lavinmq` modules claim
|
||||||
|
the `mesh-controller`, `mesh-store` and `mesh-broker` seats ([ADR 0079](../02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md)).
|
||||||
- **provision** — a service one module `provides` and others `require`; the mesh resolves a provider
|
- **provision** — a service one module `provides` and others `require`; the mesh resolves a provider
|
||||||
and wires the two with an endpoint and a credential. This is separate from seats: a provision is
|
and wires the two with an endpoint and a credential. This is separate from seats: a provision is
|
||||||
a service you offer, a seat is a slot you occupy.
|
a service you offer, a seat is a slot you occupy.
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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)
|
- **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)
|
- **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)
|
- **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
|
### What runs on them, and how it gets there
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ code:
|
|||||||
- mesh-catalog modules/postgres
|
- mesh-catalog modules/postgres
|
||||||
- mesh-catalog modules/lavinmq
|
- mesh-catalog modules/lavinmq
|
||||||
- mesh-lab test/integration/mesh.test.ts (a bare machine becomes a mesh)
|
- mesh-lab test/integration/mesh.test.ts (a bare machine becomes a mesh)
|
||||||
updated: 2026-09-16
|
updated: 2026-09-17
|
||||||
decisions:
|
decisions:
|
||||||
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
- 02-DECISIONS/0004-a-node-and-how-it-joins.md
|
||||||
- 02-DECISIONS/0078-the-store-and-broker-are-modules.md
|
- 02-DECISIONS/0078-the-store-and-broker-are-modules.md
|
||||||
|
- 02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md
|
||||||
- 02-DECISIONS/0077-the-controller-and-the-foundation.md
|
- 02-DECISIONS/0077-the-controller-and-the-foundation.md
|
||||||
- 02-DECISIONS/0005-the-node-host.md
|
- 02-DECISIONS/0005-the-node-host.md
|
||||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ two.
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| 1 | `postgres` | `postgres-database` | **the controller's own records and every module's.** One server, not two |
|
| 1 | `postgres` | `postgres-database` | **the controller's own records and every module's.** One server, not two |
|
||||||
| 2 | `lavinmq` | `amqp` | the broker every node dials, and what modules are granted vhosts on |
|
| 2 | `lavinmq` | `amqp` | the broker every node dials, and what modules are granted vhosts on |
|
||||||
| 3 | `mesh-controller` | *claims* `the-controller` | decides what runs where |
|
| 3 | `mesh-controller` | *claims* `mesh-controller` | decides what runs where |
|
||||||
| 4 | `distribution` | `artifact-store` | what the mesh built, pinned by digest — the module is the software (Distribution), the provision is the job |
|
| 4 | `distribution` | `artifact-store` | what the mesh built, pinned by digest — the module is the software (Distribution), the provision is the job |
|
||||||
| 5 | `builder` | — | turns source into artifacts |
|
| 5 | `builder` | — | turns source into artifacts |
|
||||||
| 6 | `mesh-tools` | build inputs | the base everything with code compiles against. **Runs nowhere** |
|
| 6 | `mesh-tools` | build inputs | the base everything with code compiles against. **Runs nowhere** |
|
||||||
|
|||||||
+17
-3
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: open
|
status: resolved
|
||||||
opened: 2026-09-17
|
opened: 2026-09-17
|
||||||
located-in: [mesh-controller, mesh-host]
|
located-in: [mesh-controller, mesh-host]
|
||||||
fixed-by:
|
fixed-by: mesh-catalog + mesh-controller (the foundation modules claim mesh-scoped seats)
|
||||||
amended-design:
|
amended-design: 02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 056 — An adopted module assigned to a second node raises a second server
|
# 056 — An adopted module assigned to a second node raises a second server
|
||||||
@@ -40,3 +40,17 @@ for.
|
|||||||
controller refuses to place it on a node whose foundation did not raise that container?
|
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
|
- 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?
|
in a bed that tries the second assignment and asserts the refusal?
|
||||||
|
|
||||||
|
## Resolution (2026-09-17)
|
||||||
|
|
||||||
|
The foundation modules now claim a mesh-scoped **seat** named after the server each guards —
|
||||||
|
`postgres` claims `mesh-store`, `lavinmq` claims `mesh-broker`, and the controller's seat is
|
||||||
|
renamed `the-controller` → `mesh-controller` so all three follow one convention
|
||||||
|
([ADR 0079](../../02-DECISIONS/0079-the-foundation-seats-are-named-after-their-servers.md)). The
|
||||||
|
resolver's `checkClaims` refuses a second holder mesh-wide — the same mechanism that keeps one
|
||||||
|
hub and one controller — so a second `assign` is a refusal at resolution (*one per mesh*), not a
|
||||||
|
silent second server.
|
||||||
|
|
||||||
|
Checked by `TestAFoundationModuleCannotBeRaisedOnASecondNode` (each foundation module's second
|
||||||
|
assignment is refused) and by the manifests declaring the seat; the two-node adopted-broker bed
|
||||||
|
stays green with the seats in place, so the claim does not break single-node adoption.
|
||||||
|
|||||||
Reference in New Issue
Block a user