diff --git a/01-RESEARCH/012-the-minimum-viable-node/00-overview.md b/01-RESEARCH/012-the-minimum-viable-node/00-overview.md index 6b446d0..89e0456 100644 --- a/01-RESEARCH/012-the-minimum-viable-node/00-overview.md +++ b/01-RESEARCH/012-the-minimum-viable-node/00-overview.md @@ -34,6 +34,11 @@ carry everything in the bundle, download at apply time, or have something push t first. Downloading fails on the first node, which cannot fetch the image registry from the image registry it is trying to start. +> **Qualified by [ADR 0046](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md).** The +> reframing below still holds for what a *tailored installer* contains — the missing pieces for a +> given machine. It does **not** have to hold for container images: the installer fetches those +> by digest, because a real machine has a network and the sealed case is the lab. + **The reframing:** the machine is not offline. What matters is *when* the fetching happens. Move it from apply time to **build time** — build the installer on a machine that has a network, tailored to the target, and apply it on a target that then needs nothing. That is the same move diff --git a/02-DECISIONS/0046-the-installer-fetches-what-it-pins.md b/02-DECISIONS/0046-the-installer-fetches-what-it-pins.md new file mode 100644 index 0000000..038917b --- /dev/null +++ b/02-DECISIONS/0046-the-installer-fetches-what-it-pins.md @@ -0,0 +1,77 @@ +--- +status: accepted +date: 2026-08-26 +deciders: jochen +reconstructed: false +extends: 0038-a-node-joins-by-linking-first.md +--- + +# 46. The installer fetches what it pins + +## Context + +Stage 2 of [the node host](../03-DESIGN/01-to-be/05-the-node-host.md) needs to raise the +substrate, and a substrate service is a container, and a container needs an image. Where the +image comes from had been blocking implementation. + +The blocking version of the question assumed the machine might have no network, which produced a +bad trilemma: carry every image inside the artifact, fetch at apply time, or have something else +place them first. Carrying them makes a three-megabyte binary into a multi-hundred-megabyte one +and strains [ADR 0041](0041-the-host-depends-on-nothing.md). + +**The assumption was wrong, and it came from the lab.** A scenario is a closed address space by +design — that is what lets two scenarios hold the same addresses without meeting. Production is +not: a machine being adopted has a network, and one that does not is a machine where very little +works anyway. + +## Decision + +**The installer fetches what the bundle pins.** + +`substrate.lock` carries **references, not payload** — an image name and a digest. At apply time +the host fetches them. + +| Situation | Fetched from | +|---|---| +| a first node, no mesh yet | upstream, wherever the image ordinarily lives | +| every node after that | the mesh's own registry | +| **the lab** | **nowhere — the lab places them first** | + +**Pinned by digest, not by tag.** A tag moves; a digest does not. Reproducibility comes from +pinning the identity of the thing, not from carrying its bytes — which is what makes fetching +acceptable rather than a compromise. + +**The lab is the exception, and it is the lab's problem.** A sealed scenario cannot reach a +registry, so the lab places images into a machine the same way it already places the host +binary. That is a property of a test environment, and letting it dictate the production design +would be the tail wagging the dog. + +## Consequences + +- **[ADR 0041](0041-the-host-depends-on-nothing.md) survives untouched.** *Copy it onto a + machine and run it* remains literally true: one binary, a few megabytes, which then fetches + what it was told to fetch. The alternative would have quietly redefined the property that + decision rests on. +- **The bundle stays small and reviewable.** A list of pinned references is something a person + can read and check. A bundle containing images is not. +- **An apply can fail because something is unreachable**, which a self-contained artifact could + not. That is the cost, it is accepted, and it must fail *legibly* — naming what it could not + fetch and from where, not "install failed". +- **The lab needs a way to place images**, and the machine it places them into needs a container + runtime, which a sealed scenario cannot install either. Both are lab-installation concerns and + neither is solved here. +- **The build-time-versus-apply-time reframing in + [research 012](../01-RESEARCH/012-the-minimum-viable-node/00-overview.md) narrows.** It still + holds for what a *tailored installer* contains — the missing pieces for a given machine — but + it does not have to hold for images, because fetching them is available and cheap. Recorded + because the reframing was general and is now qualified. +- **Nothing here says what happens when a fetch is impossible on a real node.** An air-gapped + machine is not a case the mesh has, and if one appears this decision is what it revisits. + +## References + +- [ADR 0041](0041-the-host-depends-on-nothing.md) — the property this preserves. +- [ADR 0038](0038-a-node-joins-by-linking-first.md) — the bundle this fills in. +- [`07-the-substrate.md`](../03-DESIGN/01-to-be/07-the-substrate.md) — what the bundle pins. +- [Research 012](../01-RESEARCH/012-the-minimum-viable-node/00-overview.md) — the reframing this + qualifies. diff --git a/03-DESIGN/01-to-be/07-the-substrate.md b/03-DESIGN/01-to-be/07-the-substrate.md index 433a8d5..fdd7a9e 100644 --- a/03-DESIGN/01-to-be/07-the-substrate.md +++ b/03-DESIGN/01-to-be/07-the-substrate.md @@ -73,13 +73,12 @@ 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. +**Why references and not payload:** the bundle names images by **digest** and the host fetches +them ([ADR 0046](../../02-DECISIONS/0046-the-installer-fetches-what-it-pins.md)). A first node is +a real machine with a network; the sealed case is the lab, and the lab places images itself. -**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. +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