--- topic: the mesh status: accepted date: 2026-10-01 deciders: jochen reconstructed: false extends: 02-DECISIONS/0126-a-module-declares-its-own-seats.md --- # 161. What deserves a seat: a role of a module is a seat, a singular fact about machines is a placement with a capacity of one, and a holder's software is the machine's ## Context Three issues asked the same question from three sides. The vault provides `secret` to the whole mesh and claims no seat, so nothing refuses a second vault by name ([issue 106](../04-ISSUES/106-the-vault-claims-no-seat/00-report.md)). The hub of the private network is a placement, `overlay place --hub`, and the issue asked whether "there is exactly one hub" is a seat's shape ([issue 105](../04-ISSUES/105-the-hub-of-the-private-network-is-not-a-seat/00-report.md)). Three modules claim the one uplink seat, one per network manager a machine might run, and nothing checks that the holder names the manager the machine actually runs ([issue 138](../04-ISSUES/138-two-modules-claim-one-seat-and-are-not-interchangeable/00-report.md)). Read against the code on the day of deciding: - The mesh's own seats are five by [design 26](../03-DESIGN/01-to-be/26-the-seats.md)'s table and four in the controller's seed: `mesh-vault` is in the table and not in the seed, and the vault's definition claims nothing. The design also says `secret` is reserved; no parser or resolution rule reserves it. A second provider of `secret` would be a second candidate, settled by a pin. - The store already keeps one hub: a unique index since the overlay's first migration, and the placing command refuses a second hub naming the first. What 105 observed as silent is not. [ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md) decided that the private network becomes a mesh-scoped seat held by a server module, with client modules — the overlay is the host's own today, so that seat has nothing to be held by yet. - A machine's capabilities are its profile, detected by the host at enrolment and never since, and resolution refuses a module on a machine lacking one it declares, naming the capability. The uplink holders declare `package-manager` and `service-manager`, which every machine has. [ADR 0126](0126-a-module-declares-its-own-seats.md) gave the reason the mesh's own seats exist: **the mesh's own code looks them up by name.** `mesh-store` is an identifier the controller dereferences, not a convention. That reason decides the first question; the other two are decided by what a seat is — a role held by a module assignment — and by what the mesh can check. ## Decision **1. A provision the mesh itself dereferences is delivered by a mesh seat its provider claims.** The vault's `secret` is one: the controller seals every minted credential with it. `mesh-vault` is the fifth seat of the mesh's own, mesh-scoped, delivering `secret`, under [ADR 0079](0079-the-foundation-seats-are-named-after-their-servers.md)'s convention; the vault's definition claims it; a second provider of `secret` is a second claimant and refused by name. The word *reserved* leaves design 26: the effect it described is the seat's. Every other mesh-scoped provision — `smtp`, `oidc-client`, `s3-bucket`, `route`, `acme-ca` and the rest — may have several providers, and a consumer with several and none local is a person's choice, as the glossary says. The test for "deserves a seat" is the question 0126 asked: does the mesh's own code find it by name? **2. A singular fact about machines is a placement with a capacity of one; a singular role of a module is a seat.** A seat is held by a module assignment and points at it; the hub is a machine, and the private network is the host's own until 0121's server and client modules exist. So the hub stays a placement, and what a seat would have given — refusal of a second by name, and the one named when asked — a placement of capacity one gives: the store keeps one (the unique index), the placing command refuses a second naming the one that stands, and the overlay listing names it. 0121's seat for the private network stands, deferred with the split it needs. The rule generalises: a fact of the shape *exactly one machine is X* is a placement checked by the store and said by name, never a seat with no module to hold it. **3. A holder of a seat whose role is "speak to what this machine runs" must be the dialect the machine runs, and the machine says which.** The host's profile gains one capability per network manager found active — `uplink-networkmanager`, `uplink-systemd-networkd`, `uplink-dhcpcd`, each `systemctl is-active` of the manager's unit — and each uplink holder declares its own. Assignment then refuses the wrong holder with the refusal that already exists, naming the capability; nothing new is judged. The profile is detected again by every apply and travels in the report, and the controller keeps the latest, so a machine that switches managers is, at its next push, a machine whose holder lacks a capability: the plan refuses and names it, which is the one thing the machine is the only one to know. `node-uplink` stays one seat: its three holders are three dialects of one role, and the capability picks the dialect. One module speaking all three is allowed by this and built by nobody. ## Consequences - The controller's seed gains `mesh-vault`; the seat table takes it additively at the next start, as every seed row does. The vault's definition claims it, one release after the controller. - The uplink definitions declare their capability one release after the host reports it, or they are refused on every machine in between; the order is controller (the report carries a profile), host, then catalogue. - Design 26 loses the word *reserved* for `secret` and states rules 2 and 3; the uplink row of the seat table names the capability its holders declare. - Issue 106 is resolved by rule 1, 105 by rule 2 with nothing to build, 138 by rule 3. ## How this is checked | Rule | Checked by | |---|---| | `mesh-vault` is in the mesh's own set, mesh-scoped, delivering `secret`, and the vault claims it | a catalogue test on the default seats; registration refuses a second claimant by name (`CanHold`'s existing test, with the vault's seat) | | A second hub is refused naming the first, and the listing names the hub | the overlay command's test; the store's unique index | | A machine's profile names the network manager it runs, and is renewed by every report | a host detector test per manager; a controller test that a report carrying a profile replaces the stored one | | An uplink holder on a machine running another manager is refused, naming the capability | the existing capability refusal, exercised by a resolution test with a networkmanager machine and the systemd-networkd holder | | Live | `mesh-controller.seats` lists `mesh-vault` held by the vault on the control node; `plan` of a machine refuses the wrong uplink holder naming `uplink-` | ## References - [ADR 0079](0079-the-foundation-seats-are-named-after-their-servers.md), [ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md), [ADR 0117](0117-a-machines-uplink-is-a-seat.md), [ADR 0121](0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md), [ADR 0126](0126-a-module-declares-its-own-seats.md) - [Design 26 — The seats](../03-DESIGN/01-to-be/26-the-seats.md) - Issues [105](../04-ISSUES/105-the-hub-of-the-private-network-is-not-a-seat/00-report.md), [106](../04-ISSUES/106-the-vault-claims-no-seat/00-report.md), [138](../04-ISSUES/138-two-modules-claim-one-seat-and-are-not-interchangeable/00-report.md)