diff --git a/02-DECISIONS/0036-bootstrap-ends-at-a-usable-mesh.md b/02-DECISIONS/0036-bootstrap-ends-at-a-usable-mesh.md new file mode 100644 index 0000000..87d330d --- /dev/null +++ b/02-DECISIONS/0036-bootstrap-ends-at-a-usable-mesh.md @@ -0,0 +1,99 @@ +--- +topic: the tiers +status: accepted +date: 2026-08-31 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0035-one-implementation-several-surfaces.md +--- + +# 36. Bootstrap ends at a usable mesh, and the first credential comes from a person + +## Context + +Bootstrap currently ends when the control plane starts +([`07-the-substrate.md`](../03-DESIGN/01-to-be/07-the-substrate.md)). That is a mesh that runs and +cannot yet be used by anybody who is not standing at the machine: the networked surfaces need an +OAuth2 identity provider ([ADR 0035](0035-one-implementation-several-surfaces.md)), the provider is +a module, and no module has been assigned. + +**So bootstrap should go further** — through the identity provider and the first login — and stop +at a mesh somebody can actually use. + +**One thing in the way, and it is not incidental.** The mesh has never held a readable secret. The +sealing code says what it does and why: + +> Make generates a secret and seals it to both ends, **keeping no readable copy.** + +An initial administrator's credential is the first value a **person must read**. Everything else +the mesh generates is something no human ever sees, and everything a human provides is something +the mesh immediately stops being able to read. + +## Considered Options + +1. **The mesh generates it and prints it once**, to the terminal of whoever ran the bootstrap. + Convenient, and needs no prompt. **Rejected.** It would give the control plane a plaintext + secret for the first time — briefly, and only to one terminal, but the capability would then + exist. *An exception made for one case does not stay one*: the next credential that is awkward + to supply gets printed too, and the property that a copy of the mesh's database is a copy of + nothing stops being checkable by reading the code. + +2. **No password: a one-time link that lets the operator set their own.** The nicest to use. + **Rejected for now** — it needs a mechanism that does not exist, and the thing it improves is + one prompt, once, on a new mesh. + +3. **The operator supplies it.** **Adopted.** + +## Decision + +**Bootstrap runs to a usable mesh**: the substrate, the control plane, the identity provider as an +ordinary module, its realm and client provisioned, an administrator able to log in, and the +networked surfaces available. + +**The administrator's credential is supplied by the person doing the bootstrap**, on standard +input and not echoed — the path that already exists for a model-access key. The mesh seals it and +cannot read it afterwards. + +**What is created is an account in the identity provider, not a user of the mesh.** The mesh still +has no user model and gains none here ([ADR 0034](0034-the-local-account-owns-the-mesh.md)). What +this produces is the first login for the applications that have one. + +**The provisioning is ordinary.** A realm, a client and a first account are what an identity +module's provisioner makes from what the mesh granted it — the same shape as a database and a +bucket, which are built and proven. + +**The surfaces arrive when their dependency does.** The command API is not started with the +control plane and then broken until identity exists; it becomes available once it can authenticate, +the way anything else waits for a provider. + +## Consequences + +**The mesh still never holds a readable secret**, and that sentence needs no exception clause. +That is the whole reason for the prompt. + +**An unattended bootstrap is still possible, and the value still comes from outside.** Automation +supplying the credential is the operator supplying it. What is refused is the *mesh inventing* +one — so an unattended bootstrap with no credential provided produces a mesh with no +administrator, which is correct rather than broken. + +**Bootstrap gains an interactive step**, and it is the only one. Worth stating because a bootstrap +that cannot run without a person is a real constraint on how a node is stood up, and this is +deliberate rather than an oversight. + +**The identity provider is still not substrate.** It is assigned by the control plane, so it comes +after it, and a thing that comes after cannot be a thing that must exist before +([ADR 0033](0033-the-substrate-is-a-store-and-a-broker.md)). Bootstrap running through it does not +move it: bootstrap is a sequence, the substrate is a dependency. + +**And the recovery path is unchanged.** When the identity provider is broken later — which is the +failure that matters, not the one at first start — the command line still works, because it +authenticates through nothing (ADR 0035). + +## References + +- [ADR 0035](0035-one-implementation-several-surfaces.md) — the surfaces, and why the command line + must keep working +- [ADR 0034](0034-the-local-account-owns-the-mesh.md) — the local account owns the mesh; this adds + no user model +- [ADR 0033](0033-the-substrate-is-a-store-and-a-broker.md) — what must exist before the control + plane, which this does not change diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index ad98579..64a4a6f 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -97,6 +97,7 @@ python3 00-META/checks/index.py fail if stale - **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) +- **0036** — [Bootstrap ends at a usable mesh, and the first credential comes from a person](0036-bootstrap-ends-at-a-usable-mesh.md) ### What runs on them, and how it gets there