To-be 27 and ADR 0113 (proposed): a module requires, the mesh resolves

The design pass. Everything a module needs is a requirement: a name, a contract, and one of four kinds
of provider — a module, the node's host, the mesh, the operator. Installing a module resolves every
requirement or refuses, naming everything missing at once. It retires six mechanisms that grew
separately: provisions through bindings, settings, assigned ports, machine facts, minted secrets and
literals in the definition.

ADR 0113, proposed: a provider makes what it provides, and the mesh carries it back sealed to the
consumer's node. It is the return path ADR 0048 left "to a separate decision", now needed three ways:
data provisions with nothing to answer with, contracts needing a value the controller cannot make, and
a vault that generates nothing. Who a consumer is stays the mesh's (ADR 0049). Genesis is the one
exception. On acceptance it supersedes 0048 and amends 0085.

ADR 0112 is revised from three sources to that single concept.

ADR 0110 is amended for two points raised in review. The vault gets the mesh-vault seat (issue 106).
A seat's holder outranks co-location for a provision it delivers. Writing that down exposed an
inconsistency: mesh-store delivering postgres-database would have sent every database consumer to the
control-node, against to-be 23's node-local stores. So a seat delivers a provision only where the mesh
has one answer for everyone — artifact store, npm registry, git, vault — and mesh-store and mesh-broker
deliver nothing. 23 and 26 follow.

'Control plane' becomes 'controller' in the records written today.
This commit is contained in:
jochen
2026-09-25 22:34:46 +02:00
parent 7668190154
commit aad92ea8fe
9 changed files with 524 additions and 123 deletions
+20 -12
View File
@@ -47,8 +47,9 @@ argued for is an entry nobody can explain.
| seat | scope | delivers | typically held by |
|---|---|---|---|
| `mesh-controller` | mesh | — | the controller |
| `mesh-store` | mesh | `postgres-database` | the store |
| `mesh-broker` | mesh | `amqp` | the broker |
| `mesh-store` | mesh | — | the foundation's store |
| `mesh-broker` | mesh | — | the foundation's broker |
| `mesh-vault` | mesh | `secret` | 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 |
@@ -61,7 +62,7 @@ argued for is an entry nobody can explain.
| `the-resolver-configuration` | node | — | whichever of the alternative resolver configurations is chosen |
| `the-showcase` | node | — | the showcase module |
The control plane holds this set in code, and a test asserts both its size and that every entry names
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 document follows the code, not the reverse. If the two disagree,
the test has been changed without this table, and the table is what is wrong.
@@ -70,16 +71,23 @@ the test has been changed without this table, and the table is what is wrong.
A seat that delivers a provision may only be held by a module that provides it, at the seat's scope.
A mesh seat delivers a mesh-scoped provision.
**Its holder answers for that provision.** When a requirement for it has more than one provider in the
mesh, the control plane takes, in order:
**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. The store and the broker are
not: nodes run their own stores and a consumer uses the one on its machine
([23 — Choosing a provider](23-choosing-a-provider.md)). So their seats guard that the foundation's
own server is singular, and route nobody.
**Its holder answers for that provision.** A requirement for it resolves, in order, to:
1. the provider the consumer's node was pinned to, because a consumer coupled to one provider's
contents has said so ([23 — Choosing a provider](23-choosing-a-provider.md));
2. the holder of the seat;
3. the only provider, when there is one;
4. otherwise nothing, and the requirement is refused with the candidates named.
2. the holder of the seat, **even when another provider runs on the consumer's own machine**;
3. otherwise nothing, and the requirement is refused, naming the unheld seat.
So a second provider can run beside the holder and harm nothing. The forge holds
Co-location, which answers first for every other provision, does not apply here: a seat says which
one is the mesh's, and co-location answering first would let any second provider on a consumer's
machine take over for that consumer, silently. So a second provider can run beside the holder and
harm nothing. The forge holds
`npm-package-registry`. An npm proxy may provide the same provision on another machine, and a module
requiring an npm registry is still served by the forge, without anybody pinning it. Moving the role
to the proxy is moving the seat: unassign the claim from one, assign it to the other, and every
@@ -87,7 +95,7 @@ consumer follows.
**What a consumer receives is a grant**, the same as for any provision: where the provider answers,
what it serves, and a credential where one is minted. A consumer never reads the seat directly. The
one exception is the control plane itself, which reaches the store and the broker through a narrow
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.
## A seat that delivers nothing
@@ -98,7 +106,7 @@ their job, and it is a real one: it is the mesh saying what a machine is, in wor
## The overview
The control plane lists every seat in the set with its scope, what it delivers, and each holder as a
The controller lists every seat in the set with its scope, what it delivers, and each 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.
@@ -115,7 +123,7 @@ mesh records which:
| 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 control plane composes the clone URL at the moment of building,
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.