The substrate is a store and a broker
Third correction to one table today, found the same way as the other two: by asking whether both halves of the test were answered, or only the easy one. 0006 admits the registry because "it cannot grant itself a repository" — true, and the second half. Nothing established that the control plane needs one in order to run. Counted rather than argued: the bundle raises twelve resources and no registry is among them. The registry arrives afterwards as an ordinary module, which is exactly what the lab asserts. 0006 half-said this already, calling it "substrate by role and ordinary by delivery, provisioned once there is a control plane to do it". A member provisioned by the thing it supposedly precedes is not a member; that 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, but it genuinely uses the registry. 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 means. The substrate is now exactly what the bundle raises, which is the strongest form the list can take: checkable by counting rather than by reading an argument, and the two cannot drift. The finding is not about substrates. A test with two conditions is a test only when both are asked.
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user