diff --git a/02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md b/02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md new file mode 100644 index 0000000..ac6a913 --- /dev/null +++ b/02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md @@ -0,0 +1,63 @@ +--- +topic: building it +status: accepted +date: 2026-09-21 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0006-the-substrate-and-the-control-plane.md +--- + +# 96. An upstream image is copied between registries, never through a machine's image store + +## Context + +A module may declare that an artifact is an image published elsewhere, to be copied into the +mesh's own registry so machines fetch it by a digest this mesh assigned rather than by a name +somebody else controls. The builder pulled it into the build machine's image store and pushed it +under the mesh's name, and the push was refused: a published image is an index over several +architectures, the runtime's store keeps the index, and pushing one platform out of it fails +however the platform is asked for +([issue 046](../04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md)). +Every variant of pull-then-push was tried and failed the same way. + +## Considered Options + +1. **Resolve the index to one platform and push that.** Tried, reverted: it did not make the + push work, and a workaround for a store's behaviour is a thing nobody removes later. +2. **Tooling that copies between registries**, installed on the build machine. Rejected: one + more thing the builder's image carries, for a protocol the builder already speaks for blobs. +3. **Copy over the registry API**, in the builder. Adopted. + +## Decision + +The builder copies an upstream image between registries and never through a machine's image +store: it reads the index and every manifest it names, moves each blob by digest into the mesh's +registry — skipping what is already there, since blobs are content-named — puts the manifests +and then the index under the module's repository, and pins the index's digest. Public images are +read with the anonymous bearer token the registry hands out on challenge, which is how the +public hub and the others the catalogue names serve them. The mesh mirrors the whole index, so +what a machine fetches is the image for its own architecture; that every machine on one mesh is +the same architecture is an assumption this mesh makes and had not written down until now. + +Genesis has no registry to copy into and keeps the pull: the image stays in the first machine's +store, named by its own id, as every artifact does before there is anywhere to publish. + +## Consequences + +An upstream artifact builds. What got harder: the builder now holds a registry client of its +own, some two hundred lines, where a runtime command used to do; and a private upstream that +demands a credential is refused, since the copy is anonymous by design. + +## How it is checked + +A test raises a fake upstream registry serving an index over two platforms behind a bearer +challenge, and a fake mesh registry that records what arrives: every blob of both platforms +arrives once, two manifests and the index are put under their digests, the reference returned +pins the index under the module's repository, and a second copy uploads nothing. A reference +test reads names the way a runtime does. + +## References + +- [issue 046](../04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md) +- [ADR 0006](0006-the-substrate-and-the-control-plane.md) +- [`03-DESIGN/01-to-be/18-building-a-module.md`](../03-DESIGN/01-to-be/18-building-a-module.md) diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 71b8182..c508e29 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -161,6 +161,7 @@ python3 00-META/checks/index.py fail if stale - **0076** — [The SDK is a published package, and the toolchain resolves it by version](0076-the-sdk-is-a-published-package.md) - **0082** — [The registry is reached by name, and the overlay is its security](0082-the-registry-is-reached-by-name-and-trusted-by-the-overlay.md) - **0086** — [A secret reaches a process as a file, and an exception is declared](0086-a-secret-reaches-a-process-as-a-file.md) +- **0096** — [An upstream image is copied between registries, never through a machine's image store](0096-an-upstream-image-is-copied-between-registries.md) ### How it is checked diff --git a/03-DESIGN/01-to-be/18-building-a-module.md b/03-DESIGN/01-to-be/18-building-a-module.md index 96e218a..37ec58e 100644 --- a/03-DESIGN/01-to-be/18-building-a-module.md +++ b/03-DESIGN/01-to-be/18-building-a-module.md @@ -7,6 +7,7 @@ code: - mesh-catalog modules/builder updated: 2026-09-21 decisions: + - 02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md - 02-DECISIONS/0091-a-mount-is-declared-three-ways.md - 02-DECISIONS/0087-a-seeded-file-is-created-once.md - 02-DECISIONS/0040-what-a-module-is.md @@ -202,6 +203,18 @@ remedies, and a test parses every manifest in the catalogue beside the checkout. | `image` | built from a Dockerfile — for software that needs a particular base | | `upstream` | somebody else's image, mirrored and pinned by a digest this mesh assigned | +**An upstream image is copied between registries, never through a machine's image store** +([ADR 0096](../../02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md)). A +published image is an index over several architectures, and a runtime's store refuses to push +one platform out of an index it pulled. The builder reads the index and every manifest it names +over the registry API, moves each blob by digest into the mesh's registry, puts the manifests and +then the index under the module's repository, and pins the index — the whole image, so what a +machine fetches is the one for its own architecture. Public images are read with the anonymous +token a registry hands out on challenge; a private upstream is refused. *How it is checked:* a +test copies an index over two platforms from a fake registry behind a bearer challenge into a fake +mesh registry and asserts every blob arrived once, the manifests and index under their digests, +and nothing uploaded on a second copy. + ### What it puts on a machine | resource | is | a module may | diff --git a/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md b/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md index b1ed533..bf02f6a 100644 --- a/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md +++ b/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/00-report.md @@ -1,9 +1,9 @@ --- -status: located +status: resolved opened: 2026-09-14 located-in: [mesh-controller internal/builder (upstream artifacts)] -fixed-by: -amended-design: +fixed-by: ADR 0096; mesh-controller multiple-fixes (the builder copies the index and its manifests between registries over the registry API); proven by a test against a fake upstream serving an index over two platforms +amended-design: 03-DESIGN/01-to-be/18-building-a-module.md --- # 046 — An upstream image cannot be mirrored into the mesh's own registry diff --git a/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/01-diagnosis.md b/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/01-diagnosis.md index 0d4fdfd..5417d19 100644 --- a/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/01-diagnosis.md +++ b/04-ISSUES/046-an-upstream-image-cannot-be-mirrored-into-the-mesh/01-diagnosis.md @@ -12,6 +12,8 @@ than a limitation; today every machine on one mesh is the same architecture, and that assumption is now written down here rather than nowhere. -**Located in:** the builder's upstream-artifact step. Not fixed here: a registry-to-registry copy -is a few hundred lines against the registry API and is proven only against a real registry -serving a real index, which is a lab run of its own. +**Located in:** the builder's upstream-artifact step. Fixed as +[ADR 0096](../../02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md): a copy over +the registry API, proven against a fake upstream serving an index over two platforms behind a +bearer challenge. A run against the public hub from a mesh's builder is the remaining proof, and +the first build of a module with an upstream artifact will be it.