diff --git a/02-DECISIONS/0033-the-substrate-is-a-store-and-a-broker.md b/02-DECISIONS/0033-the-substrate-is-a-store-and-a-broker.md new file mode 100644 index 0000000..5de0e07 --- /dev/null +++ b/02-DECISIONS/0033-the-substrate-is-a-store-and-a-broker.md @@ -0,0 +1,91 @@ +--- +topic: the tiers +status: accepted +date: 2026-08-31 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0028-the-substrate-supplies-the-control-plane-and-nothing-else.md +--- + +# 33. The substrate is a store and a broker + +## Context + +Third correction to one table in one day, all found the same way: by asking whether **both** halves +of the substrate test were actually answered for a given member, or only the second. + +The test ([ADR 0006](0006-the-substrate-and-the-control-plane.md)) is *what the control plane needs +in order to run, and cannot ask itself for, because it is not running yet.* ADR 0006 admits the +image registry on this line: + +| role | product | | +|---|---|---| +| image registry | **an OCI registry** | it cannot grant itself a repository | + +**That is the second half again.** It is true that a control plane cannot grant itself a +repository. Nothing establishes that it needs one *in order to run*. + +**Counted rather than argued.** `substrate-first-node.lock` — the only bundle there is, and what a +first node actually becomes — raises twelve resources, and no registry is among them: + +``` +container runtime · the store · one database per context · the schemas +· the broker's certificate · the broker · the control plane +``` + +The registry arrives afterwards, as an ordinary module the mesh assigns. That is what the lab +asserts, in those words: *the mesh runs its own artifact store.* + +**ADR 0006 half-said this already**, calling the registry *substrate by role and ordinary by +delivery, provisioned once there is a control plane to do it.* A member that is provisioned by the +thing it supposedly precedes is not a member; the phrase was carrying a contradiction rather than +resolving one. + +**The registry is a closer call than the object store, and the difference is worth keeping.** The +control plane never touches an object store at all — no client, no bucket, ever +([ADR 0028](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md)). It genuinely +*uses* the registry: the builder pushes to it, hosts pull from it, and nothing reaches a machine +without it. **So the registry is a real dependency of the mesh operating, and not of the control +plane starting** — and it is the second that the word substrate means. + +## Decision + +**The substrate is two things: a relational store and a message bus.** Both are in the bundle, +both must exist before the control plane's first instruction, and neither can be asked for. + +**The registry is an ordinary module.** The mesh cannot deliver anything without one, and it +installs one the way it installs everything else. The first node's chicken-and-egg is already +solved and needs nothing from this list: it fetches upstream images directly, then runs a registry +of the mesh's own. + +**The test is applied to both columns, every time.** *Cannot grant itself one* is true of almost +any service and settles nothing on its own. It is what admitted the object store, and then the +registry, and both were removed by asking the other question. + +## Consequences + +**The substrate is now exactly what the bundle raises**, which is the strongest form this list can +take: it can be checked by counting rather than by reading an argument. A member that is not in +the bundle is not substrate, and the two statements cannot drift apart. + +**A mesh that builds nothing still needs a registry** — to receive anything at all — but it needs +it as a module, on its own schedule, replaceable. That was already true and was obscured by the +list. + +**The word may now be doing too little work.** "Substrate" for *a database and a broker* is a term +of art for two things everybody can name. Renaming is not taken here and is worth considering +separately; what this record fixes is the membership, not the vocabulary. + +**Three removals from one table in one day is itself the finding.** Each member was admitted on the +half of the test that is easy to answer, and the design read plausibly throughout. The rule that +comes out of it is not about substrates: **a test with two conditions is a test only when both are +asked.** + +## References + +- [ADR 0006](0006-the-substrate-and-the-control-plane.md) — the definition, and the table this + corrects a second row of +- [ADR 0028](0028-the-substrate-supplies-the-control-plane-and-nothing-else.md) — the object + store, removed for the same reason +- [ADR 0031](0031-the-control-plane-authenticates-nobody.md) — identity, which was conditional and + is now a module diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index e2c5a47..15d55fe 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -96,6 +96,7 @@ python3 00-META/checks/index.py fail if stale - **0029** — [A network is a shape, because an action cannot be undone](0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md) - **0030** — [Data outlives the mesh that declared it](0030-data-outlives-the-mesh-that-declared-it.md) - **0031** — [The control plane authenticates nobody, so identity is a module](0031-the-control-plane-authenticates-nobody.md) +- **0033** — [The substrate is a store and a broker](0033-the-substrate-is-a-store-and-a-broker.md) ### What runs on them, and how it gets there diff --git a/03-DESIGN/01-to-be/07-the-substrate.md b/03-DESIGN/01-to-be/07-the-substrate.md index d1ff74d..6be2209 100644 --- a/03-DESIGN/01-to-be/07-the-substrate.md +++ b/03-DESIGN/01-to-be/07-the-substrate.md @@ -37,7 +37,7 @@ The test, applied: | a relational store — **PostgreSQL** | its own state lives there | no — provisioning needs the store | **substrate** | | a message bus — **LavinMQ** | it reaches nodes over it ([ADR 0002](../../02-DECISIONS/0002-nodes-communicate-over-a-broker.md)) | no — it cannot grant itself a virtual host | **substrate** | | ~~an object store~~ | ~~artifacts and blobs it delivers~~ | — | **not substrate** — [ADR 0028](../../02-DECISIONS/0028-the-substrate-supplies-the-control-plane-and-nothing-else.md) | -| an image registry — **the OCI registry** | images it delivers to nodes | no — it needs a repository | **substrate** | +| ~~an image registry~~ | ~~images it delivers to nodes~~ | — | **not substrate** — needed to operate, not to start ([ADR 0033](../../02-DECISIONS/0033-the-substrate-is-a-store-and-a-broker.md)) | | ~~an identity provider~~ | ~~only if it delegates authentication~~ | — | **not substrate** — it delegates to nothing ([ADR 0031](../../02-DECISIONS/0031-the-control-plane-authenticates-nobody.md)) | | ingress — **Traefik** | not to start; only to be reached by name | — it grants itself one afterwards | **not substrate** ([ADR 0007](../../02-DECISIONS/0007-connectivity.md)) | | anything else the mesh hosts | no | — | not substrate |