|
|
|
@@ -0,0 +1,110 @@
|
|
|
|
|
---
|
|
|
|
|
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 self-contained:** a node raising a first mesh may have no route to anything
|
|
|
|
|
([research 012](../../01-RESEARCH/012-the-minimum-viable-node/00-overview.md)). Images and
|
|
|
|
|
packages the bundle needs travel *with* it, fetched when the bundle was **built** rather than
|
|
|
|
|
when it is applied.
|
|
|
|
|
|
|
|
|
|
**What that makes it:** an artifact built on a machine with a network, for a machine that may
|
|
|
|
|
have none — which is the reframing research 012 records, arriving here as a requirement.
|
|
|
|
|
|
|
|
|
|
## 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 — and why the host's declaration
|
|
|
|
|
vocabulary has to reach further than files, directories and units.
|
|
|
|
|
|
|
|
|
|
## Open
|
|
|
|
|
|
|
|
|
|
- **Whether identity is the fifth.** Above; it follows from a decision not yet taken.
|
|
|
|
|
- **Whether the host can do step 2.** Creating a database inside a running store is not node
|
|
|
|
|
state, and [ADR 0043](../../02-DECISIONS/0043-a-declaration-is-an-ordered-list-of-owned-resources.md)
|
|
|
|
|
has the host applying *state on this machine*. At bootstrap the store is on that machine, so it
|
|
|
|
|
is at least local. It is the sharpest unresolved thing in the bootstrap path.
|
|
|
|
|
- **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.
|