Files
hq/03-DESIGN/01-to-be/07-the-substrate.md
T
jschoubben c631cbd07c The bootstrap starts a step earlier than recorded
Asked whether postgres has to be installed, and the answer exposed a missing
step. The store is a container, so something must run containers before anything
else happens — and a container runtime is a PACKAGE, not a container.

Step 0 is where several threads meet. It is what the host's capability detection
already reports, and the first use of that report by something other than a
person. It is adopted rather than installed when the machine already has a
runtime with configuration somebody chose. And it is a package, needing the
machine's own package manager and a network, both of which ADR 0046 permits.

So the host's bootstrap vocabulary is six shapes: package, container, file,
directory, service, action. Stage 2 built three of them.

The node host design now names which three remain and why the lab cannot yet
exercise them — a sealed scenario fetches nothing and its machines carry no
container runtime, which is lab-installation work rather than a constraint on
the design, because production machines have a network.
2026-08-26 23:58:19 +02:00

130 lines
6.7 KiB
Markdown

---
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):
```
0 a container runtime exists detected, or installed as a package
1 the store runs pulled by digest, from the bundle
2 a database is created in it an action, run locally
3 the control plane's schema applied an action, against that database
4 the control plane starts and only now is there a mesh
5 everything else is provisioned the ordinary path
```
**Step 0 is easy to leave out and it is where several things meet.** A substrate service is a
container, so something must run containers before anything else happens — and a container
runtime is a *package*, not a container. It is:
- what the host's capability detection already reports, and the first use of that report by
something other than a person;
- **adopted rather than installed** when the machine already has one with configuration somebody
chose ([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md));
- a package, which needs the machine's own package manager and a network — both permitted by
[ADR 0046](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md).
So the host's bootstrap vocabulary is five shapes: **package**, **container**, **file**,
**directory**, **service**, and **action**. Files, directories and services exist; the rest do
not yet.
**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.