From a1a10e9ed2a49cb3b4b56995f71e640393ce69f1 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 31 Aug 2026 21:23:55 +0200 Subject: [PATCH] Bootstrap ends at a usable mesh, and the first credential comes from a person MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bootstrap stopped when the control plane started — a mesh that runs and cannot be used by anybody not standing at the machine, since the networked surfaces need an identity provider and no module has been assigned yet. It now runs through the provider and the first login. The obstacle was not incidental. The mesh has never held a readable secret: Make generates and seals, keeping no readable copy. An initial administrator's credential is the first value a person must read. Generating it and printing it once was the convenient option and is refused. It would give the control plane a plaintext secret for the first time — briefly, and to one terminal, but the capability would then exist, and an exception made for one case does not stay one. The next awkward credential gets printed too, and "a copy of the database is a copy of nothing" stops being checkable by reading the code. So the operator supplies it, on standard input, not echoed — the path that already exists for a model-access key. What is created is an account in the identity provider, not a user of the mesh; there is still no user model. Unattended bootstrap remains possible and the value still comes from outside: automation supplying it is the operator supplying it. What is refused is the mesh inventing one, so an unattended bootstrap with nothing provided yields a mesh with no administrator — correct rather than broken. --- .../0036-bootstrap-ends-at-a-usable-mesh.md | 99 +++++++++++++++++++ 02-DECISIONS/README.md | 1 + 2 files changed, 100 insertions(+) create mode 100644 02-DECISIONS/0036-bootstrap-ends-at-a-usable-mesh.md 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