Both halves are on their main branches, so the seats stop being an intention. Writes the as-is document from the controller's code and the catalogue's manifests: the closed set of fourteen, the three refusals a claim meets, the holder being an assignment and nothing else, and the one place a seat changes resolution — which of several providers answers, never whether a requirement may go unanswered. Two things the as-is layer exists for are stated rather than smoothed over: a seat cannot answer before it is held, which is the standing condition issue 121 records; and capacity is not implemented at all, so the design's bench has no counterpart in the code.
171 lines
10 KiB
Markdown
171 lines
10 KiB
Markdown
---
|
|
layer: to-be
|
|
status: implemented
|
|
code:
|
|
- mesh-controller internal/catalogue/seats.go
|
|
- mesh-controller internal/catalogue/resolve.go
|
|
- mesh-controller cmd/mesh-controller/seats.go
|
|
- mesh-controller cmd/mesh-controller/source.go
|
|
- mesh-controller internal/inventory/migrations/0032-a-source-may-live-on-a-seat.sql
|
|
- mesh-catalog modules/gitea/module.json
|
|
updated: 2026-09-26
|
|
decisions:
|
|
- 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
|
|
- 02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md
|
|
- 02-DECISIONS/0109-a-package-registry-seat-is-one-per-ecosystem.md
|
|
---
|
|
|
|
# 26 — The seats
|
|
|
|
**What a mesh can have one of, and who fills each.** A seat is a named role at a scope, held by one
|
|
module assignment. The mesh defines which seats exist. Holding one may deliver a provision, and the
|
|
list of seats with their holders is the quickest answer to "what is in this mesh".
|
|
|
|
## What a seat is
|
|
|
|
A seat has four properties, fixed by the mesh rather than by any module:
|
|
|
|
| property | is |
|
|
|---|---|
|
|
| name | what a definition names and an assignment holds, and what a person reads in the list |
|
|
| scope | node, site or mesh: where its capacity applies. Every seat in the set has a capacity of one, so one holder per scope. A bench, a seat with several holders, is a word the glossary keeps and no seat uses yet |
|
|
| delivers | the provision its holder answers for, or nothing |
|
|
| decision | the record that made it a seat |
|
|
|
|
**A definition says which seats a module can hold. An assignment says which it does hold.** The store
|
|
module can hold `mesh-store`, and it may run on every node whose capabilities match. Exactly one of
|
|
those assignments holds the seat, because that assignment says so, and a second assignment saying so
|
|
is refused. A seat makes a role singular, never a module.
|
|
|
|
**The seat points at the assignment.** Everything the mesh knows about the holder is what it knows
|
|
about that assignment: the node, the node's settings for the module, and what the module serves.
|
|
|
|
**The set is closed.** A seat the mesh does not define is refused wherever it is named, and so is one
|
|
named at the wrong scope. Adding a seat is a decision, recorded, for the reason every addition to the
|
|
host's vocabulary is one: the set is what a person reads to learn what a mesh can have, and an entry
|
|
nobody argued for is an entry nobody can explain.
|
|
|
|
## The set
|
|
|
|
| seat | scope | delivers | typically held by |
|
|
|---|---|---|---|
|
|
| `mesh-controller` | mesh | — | the controller |
|
|
| `mesh-store` | mesh | — | the store the mesh's own records live in |
|
|
| `mesh-broker` | mesh | — | the broker carrying the mesh's own bus |
|
|
| `mesh-vault` | mesh | `secret`, reserved | the vault |
|
|
| `the-artifact-store` | mesh | `artifact-store` | the artifact registry |
|
|
| `the-catalogue` | mesh | — | the catalogue |
|
|
| `npm-package-registry` | mesh | `npm-package-registry` | the forge |
|
|
| `git` | mesh | `git` | the forge |
|
|
| `the-build-machine` | node | — | a builder |
|
|
| `the-dns-port` | node | — | the local resolver |
|
|
| `the-intrusion-prevention` | node | — | an intrusion-prevention service |
|
|
| `the-packet-filter` | node | — | the packet filter |
|
|
| `the-private-network` | node | — | the private network the mesh runs over |
|
|
| `the-resolver-configuration` | node | — | whichever of the alternative resolver configurations is chosen |
|
|
| `the-showcase` | node | — | the showcase module |
|
|
|
|
The controller holds this set in code, and a test asserts both its size and that every entry names
|
|
the record that made it a seat. **This table and [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)
|
|
govern, and code that disagrees is what is wrong.** The implementation in progress predates several
|
|
things here: seats held by assignments rather than claimed by definitions, the `mesh-vault` seat and
|
|
its reservation, and the foundation's seats delivering nothing. It is brought to this table before it
|
|
merges.
|
|
|
|
## The foundation's seats
|
|
|
|
`mesh-controller`, `mesh-store` and `mesh-broker` name which assignment the mesh *itself* uses: the
|
|
controller, the store holding its records, the broker carrying its bus. **They route no consumer.** The
|
|
store and broker modules may run on other nodes too. A database or `amqp` consumer is served by
|
|
co-location, from whichever runs on its own node, the seat's holder included
|
|
([23 — Choosing a provider](23-choosing-a-provider.md)). A requirement cannot name one of them,
|
|
because they deliver nothing.
|
|
|
|
## A seat that delivers a provision
|
|
|
|
**A seat delivers a provision only where the mesh has one answer for everyone.** The artifact store,
|
|
the npm registry, git and the vault are each one per mesh by decision. A seat that delivers a
|
|
provision may only be held by an assignment of a module that provides it, at the seat's scope.
|
|
|
|
**A requirement may name the seat, and then its holder answers.** Naming the seat asks for *the
|
|
mesh's* one, so the holder answers **even when another provider runs on the consumer's own machine**,
|
|
and nobody is asked anything. With the seat unheld, the requirement is refused, naming the seat. A
|
|
second provider can run beside the holder and harm nothing. A forge assignment holds
|
|
`npm-package-registry`, and an npm proxy may provide the same provision on another machine. A builder
|
|
that names the seat is still served by the forge, without anybody pinning it.
|
|
|
|
**A requirement that names no seat resolves as any other**: a pin, the provider on the consumer's own
|
|
machine, the only provider. If several remain and none is local, a person chooses when the module is
|
|
assigned. The candidates are listed with the seat's holder suggested first, and the answer is recorded
|
|
as the assignment's pin ([27](27-a-module-requires-the-mesh-resolves.md)). Nothing is guessed, and
|
|
nothing changes silently because a second provider happened to appear nearby.
|
|
|
|
**Moving the role is changing which assignment holds the seat.** No definition changes and nothing is
|
|
unassigned: the forge keeps running, and keeps holding `git`, when its npm role moves. A module can
|
|
take the role only if its definition says it can hold the seat.
|
|
|
|
**The vault's provision is reserved.** Only an assignment holding `mesh-vault` may provide `secret` at
|
|
all: a definition providing it that cannot hold the seat is refused, an assignment providing it without
|
|
holding the seat is refused, and a `secret` requirement always names the seat, because there is no
|
|
other provider. A second provider of secrets would be a second place secrets live, which is what the
|
|
vault being one per mesh exists to prevent.
|
|
|
|
**What a consumer receives is what it required**, the same as for any provision: where the provider
|
|
answers, what it serves, and a credential. A consumer never reads the seat directly. The one exception
|
|
is the controller itself, which reaches the store and the broker through a narrow seat placeholder,
|
|
because it made them before any module existed and cannot be their consumer. One foundation module
|
|
also reads it today, to find its own server's port. [27](27-a-module-requires-the-mesh-resolves.md)
|
|
moves that to a host port requirement.
|
|
|
|
## A seat that delivers nothing
|
|
|
|
Most node seats deliver nothing. They say which module is this machine's packet filter, or which of
|
|
two alternative resolver configurations it runs, and a second holder is refused. That is the whole of
|
|
their job, and it is a real one: it is the mesh saying what a machine is, in words a person can read.
|
|
|
|
## The overview
|
|
|
|
The controller lists every seat in the set with its scope, what it delivers, and its holder as a node
|
|
and a module. A seat nobody holds is listed as unheld. That is an answer, "this mesh has no forge",
|
|
and not a fault.
|
|
|
|
Holdings are derived from assignments whenever they are asked for, never stored. The list is always
|
|
what the mesh is running, because it is computed from the same thing that decides what the mesh runs.
|
|
|
|
## The git seat, and where a build comes from
|
|
|
|
A module is built from a repository, a path and a ref. The repository is one of two things, and the
|
|
mesh records which:
|
|
|
|
| form | means | recorded as |
|
|
|---|---|---|
|
|
| on the `git` seat | a repository on the forge that holds the seat | its path on the forge, and the seat |
|
|
| external | a repository anywhere else, a public forge for instance | its URL, exactly as given |
|
|
|
|
For a repository on the seat, the controller composes the clone URL at the moment of building, from
|
|
where the holder runs and the scheme and port it serves for `git`. The recorded source never contains
|
|
an address, so moving the forge changes nothing that was recorded. The build machine is not told the
|
|
difference: it receives a URL either way.
|
|
|
|
With the seat unheld, a build from the seat is refused and says why. External builds carry on.
|
|
|
|
**Not yet designed:** a credential for cloning a private repository. The mesh's own repositories are
|
|
public. The natural place for a clone credential is a `secret` from the vault, and that is a decision
|
|
still to take.
|
|
|
|
## How it is checked
|
|
|
|
The rules here are [ADR 0110](../../02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md)'s
|
|
and [ADR 0111](../../02-DECISIONS/0111-a-build-source-is-on-the-git-seat-or-external.md)'s, and each is
|
|
checked as their tables say:
|
|
|
|
| Rule | Checked by |
|
|
|---|---|
|
|
| The set is closed, and every entry names its decision | 0110: a unit test on the set's size and decisions; manifest tests refusing an unknown seat or the wrong scope. |
|
|
| A seat is held by one assignment, and only by one whose module can hold it | 0110: resolution tests for a second holder and for a seat the definition does not name. |
|
|
| A requirement naming a seat is answered by its holder; a foundation seat cannot be named | 0110: resolution tests with a second provider on the consumer's node, with the seat unheld, and naming `mesh-store`. |
|
|
| Several providers and none local is a person's choice | 0110: an assignment test listing candidates with the seat's holder first and recording the pin. |
|
|
| `secret` is reserved | 0110: the parser and resolution refusals for another provider and a pin. |
|
|
| Holdings are derived, and the overview lists every seat | 0110: the `seats` command test, including an unheld seat. |
|
|
| A build source on the seat records no address; an unheld seat refuses only self-hosted builds | 0111's tests. |
|