ADR 0046 — the installer fetches what it pins
The blocking question was where a container image comes from, and the version that blocked assumed the machine might have no network. That assumption came from the LAB: a scenario is a closed address space by design, which is what lets two scenarios hold the same addresses without meeting. Production is not sealed — a machine being adopted has a network, and one that does not is a machine where very little works anyway. So substrate.lock carries references, not payload: an image name and a digest, fetched at apply time. A first node pulls from upstream because no mesh registry exists yet; every node after that pulls from the mesh's own. The lab is the exception and places images itself, the way it already places the host binary — a property of a test environment, and letting it dictate the production design would be the tail wagging the dog. Pinned by DIGEST rather than tag. Reproducibility comes from pinning the identity of a thing, not from carrying its bytes, which is what makes fetching acceptable rather than a compromise. ADR 0041 survives untouched, which was the point. "Copy it onto a machine and run it" stays literally true — one binary, a few megabytes, which then fetches what it was told to. Carrying images would have quietly redefined the property that decision rests on. Costs accepted and named: an apply can now fail because something is unreachable, which a self-contained artifact could not, so it must fail legibly — naming what it could not fetch and from where. And the lab needs a way to place images into a machine that also has no container runtime, both of which are lab-installation concerns and neither solved here. Research 012's build-time-versus-apply-time reframing narrows accordingly: it still holds for what a tailored installer contains, and no longer has to hold for images.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user