A module is a repository and a path, and installing is described to its end #33
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
layer: to-be
|
||||||
|
status: in-progress
|
||||||
|
code:
|
||||||
|
- mesh-host cmd/mesh-bootstrap
|
||||||
|
- mesh-host internal/bootstrap
|
||||||
|
- mesh-lab test/integration/whole-mesh-full.test.ts
|
||||||
|
updated: 2026-09-12
|
||||||
|
decisions:
|
||||||
|
- 02-DECISIONS/0067-genesis-is-a-pivot.md
|
||||||
|
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||||
|
- 02-DECISIONS/0005-the-node-host.md
|
||||||
|
- 02-DECISIONS/0010-delivery.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Raising a mesh
|
||||||
|
|
||||||
|
How a mesh comes into existence on machines that have none, and how a machine joins one that
|
||||||
|
already exists. This is the procedure an operator runs. It is not a description of the lab, and
|
||||||
|
the lab does not have one of its own.
|
||||||
|
|
||||||
|
## Two moments, and only two
|
||||||
|
|
||||||
|
A mesh is raised once and joined many times, and the two are not variations of each other.
|
||||||
|
|
||||||
|
**Genesis** happens on one machine, when there is no mesh. Nothing can be asked, nothing can be
|
||||||
|
granted, and nothing has been published anywhere. It is the only moment at which the ordinary
|
||||||
|
rules cannot all hold at once, and it is resolved by a pivot
|
||||||
|
([ADR 0067](../../02-DECISIONS/0067-genesis-is-a-pivot.md)).
|
||||||
|
|
||||||
|
**Joining** happens on every machine after the first, and it is ordinary. A mesh exists, so it can
|
||||||
|
be asked for a token and told what the machine should be. Joining installs the host and nothing
|
||||||
|
else: no temporary anything, no substrate raised by hand, no registry.
|
||||||
|
|
||||||
|
Confusing the two is what produced a procedure that only ever worked in a fixture. A bed that
|
||||||
|
raises four machines the same way has not tested genesis at all — it has tested joining, four
|
||||||
|
times, with the first one hand-fed.
|
||||||
|
|
||||||
|
## Genesis
|
||||||
|
|
||||||
|
The installer is a single program carrying the control plane's image inside it. That is what makes
|
||||||
|
genesis possible without a network to fetch from and without a registry to name: the image is
|
||||||
|
present because the installer is present.
|
||||||
|
|
||||||
|
It proceeds in one direction, and every step is safe to run again.
|
||||||
|
|
||||||
|
**First it refuses to start if the machine is not ready.** A container runtime, the ability to
|
||||||
|
write where it must write, the host binary where it expects it. A machine that is not ready is told
|
||||||
|
what is missing rather than half-changed.
|
||||||
|
|
||||||
|
**Then it loads the carried image and describes what the machine will become.** The image is named
|
||||||
|
by the digest of its own configuration — content-addressed and unforgeable, and requiring nothing
|
||||||
|
to have served it. This is legal precisely where nothing could have served one, and nowhere else.
|
||||||
|
|
||||||
|
**Then it raises the substrate and a temporary control plane, and waits for that control plane to
|
||||||
|
answer.** At this point the machine is a mesh of one node with nothing joined to it.
|
||||||
|
|
||||||
|
**Then the machine joins the mesh it is itself running.** It enrols, and an agent runs on it. Being
|
||||||
|
heard from once is not the same as an agent running, and the installer checks both, because
|
||||||
|
enrolling is itself the thing that makes a mesh hear from a machine.
|
||||||
|
|
||||||
|
**Then it installs a registry**, so the mesh has somewhere to keep its own images.
|
||||||
|
|
||||||
|
**Then it publishes the control plane's image to that registry**, which is the moment the image
|
||||||
|
first receives a digest assigned by something other than itself. This is the carrying step, and it
|
||||||
|
is the same step for all three things the build loop cannot produce for itself — the control plane,
|
||||||
|
the registry, and the builder. The rule and its closed list are in
|
||||||
|
[`12-a-module-repository`](12-a-module-repository.md#the-three-the-loop-cannot-build-and-there-are-only-three).
|
||||||
|
|
||||||
|
**Then it installs the control plane again, as an ordinary module pinned to that digest, and drops
|
||||||
|
the temporary one.** The pivot is complete: what raised the mesh is gone, and what runs is a module
|
||||||
|
like any other. From here the mesh can build and roll out its own upgrades, including to the thing
|
||||||
|
that runs it.
|
||||||
|
|
||||||
|
## After the pivot, and still part of installing
|
||||||
|
|
||||||
|
Genesis ends with a mesh of one that runs, and that is not the same as a mesh that works. What it
|
||||||
|
has is a control plane, a store, a queue and a registry. What it cannot yet do is **produce
|
||||||
|
anything** — and almost every module in the catalogue is waiting to be produced, because a manifest
|
||||||
|
names what its artifacts are and nothing has made them.
|
||||||
|
|
||||||
|
So installing continues:
|
||||||
|
|
||||||
|
**The builder arrives.** It is a module like any other and is assigned to a machine like any other,
|
||||||
|
but it cannot be built by the thing it is — see
|
||||||
|
[`12-a-module-repository`](12-a-module-repository.md#the-three-the-loop-cannot-build-and-there-are-only-three).
|
||||||
|
**How it arrives is unsettled**, and it is the one gap that stops everything after this paragraph
|
||||||
|
from being possible on a machine nobody is sitting at.
|
||||||
|
|
||||||
|
**The core modules are built.** Each is named by a repository and a path
|
||||||
|
([ADR 0069](../../02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md)), and the builder is
|
||||||
|
asked for each in turn: it clones, reads the manifest at that path, produces what it declares,
|
||||||
|
publishes each artifact into the mesh's own registry, and hands back the module with its artifacts
|
||||||
|
pinned and the commit recorded. The mesh records that, and from then on the module is described by
|
||||||
|
something it made rather than by a placeholder.
|
||||||
|
|
||||||
|
**The control plane is built like the rest.** It was carried in and published once, which got the
|
||||||
|
mesh running; building it from its own repository and path is what makes it upgradeable. The first
|
||||||
|
time that happens is the moment the mesh stops depending on the installer for anything.
|
||||||
|
|
||||||
|
**And then the catalogue.** Every module with source of its own is built the same way. Until this
|
||||||
|
has happened a mesh can install only what is public or carried, which is the substrate and little
|
||||||
|
else.
|
||||||
|
|
||||||
|
Only after all of that is the ordinary loop available: change a module's source, the mesh notices
|
||||||
|
its own copy is older than the source, rebuilds it, and rolls it out. That loop is what makes
|
||||||
|
moving services across one at a time possible, so it is part of installing rather than something
|
||||||
|
that comes later.
|
||||||
|
|
||||||
|
What remains after *that* belongs to somebody else: adding machines, and deciding what they run.
|
||||||
|
|
||||||
|
## Joining
|
||||||
|
|
||||||
|
A machine joins with the host binary and a token. It does not raise a substrate, does not install a
|
||||||
|
registry, and is never enrolled twice. The mesh already knows how to tell a machine what to be;
|
||||||
|
joining is the point at which a machine starts listening.
|
||||||
|
|
||||||
|
## Where the line falls
|
||||||
|
|
||||||
|
The installer owns everything that is the mesh's own. The lab owns only what is the lab's: raising
|
||||||
|
virtual machines, giving them addresses that resolve nowhere, and injecting faults.
|
||||||
|
|
||||||
|
**The lab runs the installer. It does not describe installation.** This is the whole point. A
|
||||||
|
second description kept in step with the first is the arrangement that already failed — the fixture
|
||||||
|
invented a registry that exists in no production, and hid two separate faults for as long as it
|
||||||
|
existed. Anything the lab must do that the installer does not is either a lab concern or a hole in
|
||||||
|
the installer, and saying which is a decision, not a convenience.
|
||||||
|
|
||||||
|
## What is not yet true
|
||||||
|
|
||||||
|
Stated plainly, because a document that implies otherwise is worse than none.
|
||||||
|
|
||||||
|
**The installer is not packaged.** It builds from source. An operator raising a first machine still
|
||||||
|
needs a toolchain and a working tree, which is most of the burden the installer exists to remove.
|
||||||
|
|
||||||
|
**A mesh cannot say how it was raised.** Nothing afterwards can contradict a claim that a machine
|
||||||
|
was brought up the supported way, so the rule that it must be is, today, unenforced.
|
||||||
|
|
||||||
|
**The builder has no way to arrive.** It cannot be pulled from the public internet, because the
|
||||||
|
mesh builds it; and the installer carries one image only. So the paragraphs above describing the
|
||||||
|
core modules being built are, today, describing something that cannot start.
|
||||||
|
|
||||||
|
**A machine has no account for a registry that asks for one.** The mesh grants a consumer a
|
||||||
|
credential for a database; it does not yet do so for the store its own images live in. Genesis
|
||||||
|
avoids the question by carrying the image it needs, which makes this a joining problem and a
|
||||||
|
pulling problem, not a genesis one.
|
||||||
|
|
||||||
|
## How these rules are checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| Genesis works on a machine that is not the lab | The bed raises its first machine by running the installer, not around it. A bed that stops doing so fails its own acceptance check. |
|
||||||
|
| The other machines join, and are not re-raised | The bed gives them the host binary and a token only. A second enrolment of the first machine is a failure, not a no-op. |
|
||||||
|
| Every step may be run again | The installer is re-run against a raised machine and must change nothing and report why. |
|
||||||
|
| An image is named exactly | A machine refuses a bundle naming an image by tag. The refusal is exercised, not assumed. |
|
||||||
|
| The installer is what installed this | **Nothing.** See above. |
|
||||||
|
| The builder can arrive on a fresh mesh | **Nothing.** There is no route for it yet. |
|
||||||
|
| A core module is built rather than only carried | The control plane is rebuilt from its own repository and path, and the running mesh is upgraded to the result — the same path any module takes. |
|
||||||
|
| Installing produced a mesh that can produce | After installing, a module with source of its own is asked for and comes back pinned to a digest this mesh's registry assigned, not to a placeholder. |
|
||||||
@@ -26,6 +26,7 @@ document is written and this one's status becomes `implemented`.
|
|||||||
| [`14-model-access.md`](14-model-access.md) | Model access as a provision, and what a licence is bound to | [ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md), [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
| [`14-model-access.md`](14-model-access.md) | Model access as a provision, and what a licence is bound to | [ADR 0024](../../02-DECISIONS/0024-model-access-is-a-provision.md), [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md) |
|
||||||
| [`15-the-agent-session.md`](15-the-agent-session.md) | One mechanism started twice — a node's session and the mesh's | [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md), [ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) |
|
| [`15-the-agent-session.md`](15-the-agent-session.md) | One mechanism started twice — a node's session and the mesh's | [ADR 0004](../../02-DECISIONS/0004-a-node-and-how-it-joins.md), [ADR 0026](../../02-DECISIONS/0026-the-mesh-has-a-session-of-its-own.md) |
|
||||||
| [`16-module-coverage.md`](16-module-coverage.md) | What a module must be able to say, measured against 127 that exist | [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md), [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) |
|
| [`16-module-coverage.md`](16-module-coverage.md) | What a module must be able to say, measured against 127 that exist | [ADR 0009](../../02-DECISIONS/0009-modules-and-the-graph.md), [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) |
|
||||||
|
| [`17-raising-a-mesh.md`](17-raising-a-mesh.md) | How a mesh comes into existence, and how a machine joins one that exists | [ADR 0067](../../02-DECISIONS/0067-genesis-is-a-pivot.md), [ADR 0006](../../02-DECISIONS/0006-the-substrate-and-the-control-plane.md), [ADR 0005](../../02-DECISIONS/0005-the-node-host.md) |
|
||||||
|
|
||||||
## Not yet written
|
## Not yet written
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user