The bootstrap's sharpest open question, and the framing was wrong. "State on this machine" was being read as the filesystem and the service manager. A service running on this machine IS part of this machine — writing a file and creating a database in a local store differ in mechanism, not in scope. The real question was underneath: must the host learn what a database is? It must not. Giving it a `database` resource type means tier 0 knows Postgres, then a bucket, then a virtual host — the host acquiring the substrate's vocabulary one service at a time, which is what ADR 0037 exists to stop. So the bundle declares an ACTION and the host runs it and verifies it. What a database means stays with the module that provides one; the host knows only how to run a declared action against something local and check the result. Its vocabulary grows by one shape rather than by one resource type per service. Actions are permitted in the bundle and forbidden over the link, and the asymmetry is deliberate. A bundle arrives WITH the binary: anyone able to put a hostile action in it could equally have put it in the host itself, so refusing actions there buys nothing and costs the bootstrap. The link is a separate party, reachable separately, and an action there is the unbounded blast radius ADR 0039 refuses. That decision stands unchanged. And ongoing provisioning is not the host's at all — the control plane does it once a mesh exists — so the asymmetry costs nothing. Which dissolves the earlier worry about one mechanism with a tier boundary inside it: there are two mechanisms, with different actors, scopes and trust models, and that is the answer rather than a compromise. Named rather than hidden: this is the escape hatch research 011 warned about, arbitrary code in the place hardest to remove later. It is bounded by being bundle-only and by every action having to declare how it verifies itself, and that boundary is the whole defence.
5.7 KiB
layer, status, code, updated, decisions
| layer | status | code | updated | decisions | |||
|---|---|---|---|---|---|---|---|
| to-be | designed | 2026-08-26 |
|
The substrate
Tier 1. Defined the same way the control plane 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).
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) | 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 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). 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:
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) — 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. 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, 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.