--- layer: to-be status: designed code: [] updated: 2026-08-26 decisions: - 02-DECISIONS/0030-the-repository-structure.md - 02-DECISIONS/0037-the-host-applies-it-does-not-decide.md - 02-DECISIONS/0038-a-node-joins-by-linking-first.md --- # The substrate Tier 1. Defined the same way [the control plane](06-the-control-plane.md) is, because the same gap applied: the word was load-bearing and unpinned. ## The definition > **The substrate is what the control plane consumes and cannot grant itself.** Every module that needs a database asks the control plane's provisioning for one. The control plane needs a database too — and it cannot ask itself, because it is not running yet. That circularity is not an awkwardness to work around; it *is* the definition. Anything on the wrong side of it must be raised some other way, and the other way is the bundle the host carries ([ADR 0038](../../02-DECISIONS/0038-a-node-joins-by-linking-first.md)). The test, applied: | | control plane needs it | can it grant itself one? | | |---|---|---|---| | a relational store | its own state lives there | no — provisioning needs the store | **substrate** | | a message bus | it reaches nodes over it ([ADR 0001](../../02-DECISIONS/0001-nodes-communicate-over-a-broker.md)) | no — it cannot grant itself a virtual host | **substrate** | | an object store | artifacts and blobs it delivers | no — it needs a bucket to hold them | **substrate** | | an image registry | images it delivers to nodes | no — it needs a repository | **substrate** | | an identity provider | only if it delegates authentication | — | **conditional, below** | | anything else the mesh hosts | no | — | not substrate | ## What that resolves **Four or five?** [Research 006](../../01-RESEARCH/006-mesh-from-scratch/00-overview.md) asks whether the identity provider is a substrate service, and the test answers it *conditionally* — which is the honest answer rather than a number. - If the control plane **delegates** authentication, it cannot serve anybody before the provider exists, and it cannot grant itself a client. **Substrate.** - If it **authenticates natively**, the provider is an ordinary hosted service like any other. **Not substrate.** So the count follows from a design decision that has not been taken, and the record should say that rather than assert four. **Why not "important infrastructure".** An identity provider, a mail server and an analytics service are all infrastructure by any ordinary reading, and none of them are substrate — the control plane starts and runs without them. *Important* is not the test; *the control plane cannot obtain it* is. ## What the substrate is not - **Not tier 0.** The host raises the substrate; it is not part of it. The host carries the declaration that brings the substrate up, and depends on nothing. - **Not the control plane.** These are services with no knowledge of the mesh. A store does not know what a node is. - **Not a place for logic.** The skeleton is explicit: tier 1 is *declarations only, no logic of its own.* A substrate service is an upstream image, pinned, with configuration. - **Not privileged.** The substrate is provisioned *from* by the control plane and grants nothing on its own initiative. ## The pinned bundle The substrate is what `substrate.lock` contains, and this is the only place in the mesh where versions are pinned by hand rather than resolved. **Why pinned:** the bundle is applied when no mesh exists, so nothing can resolve a version, ask a registry, or check a constraint. What the host carries must already be exact. **Why references and not payload:** the bundle names images by **digest** and the host fetches them ([ADR 0046](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md)). A first node is a real machine with a network; the sealed case is the lab, and the lab places images itself. Reproducibility comes from pinning the identity of a thing rather than carrying its bytes, which is what keeps the bundle small enough for a person to read and check. ## Raising it The order, from [research 011](../../01-RESEARCH/011-the-module-graph/worked-provider.md): ``` 1 the host applies the bundle the store runs; no mesh exists 2 a database is created in it a provisioning step, done locally 3 the control plane's schema applied a migration against that database 4 the control plane starts and only now is there a mesh 5 everything else is provisioned the ordinary path ``` **Steps 2 and 3 happen before there is a mesh to do them**, which is why provisioning is part of the bootstrap rather than a service consumers use later. They are **actions** the bundle declares and the host runs ([ADR 0047](../../02-DECISIONS/0047-the-bundle-may-carry-actions-the-link-may-not.md)) — so the host's vocabulary grows by one shape rather than by one resource type per substrate service. ## Open - **Whether identity is the fifth.** Above; it follows from a decision not yet taken. - ~~**Whether the host can do step 2.**~~ **Resolved** by [ADR 0047](../../02-DECISIONS/0047-the-bundle-may-carry-actions-the-link-may-not.md). A service running on this machine is part of this machine, so the scope was never in question — the real question was whether the host must learn what a database is, and it must not. The bundle declares an **action**; the host runs it and verifies it, and what a database means stays with the module that provides one. - **Whether one host can raise all four.** The claim under stage 2 of [the node host](05-the-node-host.md), never proved. If it is false, the tier boundary moves. - **How the substrate is updated once a mesh exists.** Pinned by hand at bootstrap; afterwards the control plane could deliver it like anything else, and nothing says whether it does.