diff --git a/02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md b/02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md new file mode 100644 index 0000000..3763486 --- /dev/null +++ b/02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md @@ -0,0 +1,59 @@ +--- +topic: building it +status: accepted +date: 2026-09-21 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md +--- + +# 97. A vendor image is a declared build input, and a recipe fetches nothing undeclared + +## Context + +Three modules could not be built by the mesh's builder because their recipes reached for what +no manifest named: a public package, or a binary copied out of a public image +([issue 064](../04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/00-report.md)). +A module already names the bases it stands on — another module's artifact, by name — and the +builder answers with what the mesh holds; a vendor's image had no such declaration, so a recipe +named it directly and the build worked when the public registry answered, which is sometimes. + +## Considered Options + +1. **Let the build environment reach public registries.** Rejected: a build that fetches from + somebody else's registry on its own is one the mesh cannot rebuild the same way twice. +2. **A vendor image is a base like any other**, declared under `build.on` with the argument the + recipe reads it from, pinned by digest, copied into the mesh's registry before the build + ([ADR 0096](0096-an-upstream-image-is-copied-between-registries.md)). Adopted. + +## Decision + +A build's `on` entry is either a module's artifact or an image published elsewhere, pinned by +digest, read from one build argument. Before the build the image is copied into the mesh's +registry under the module's repository and the recipe is handed the copy; genesis, with no +registry, pulls it into the first machine's store. A recipe whose `FROM` or `COPY --from` names a +registry image the manifest did not declare is refused before the build, naming the image and +the remedy; its own stages, declared arguments and `scratch` are not fetches. An unpinned vendor +image is refused: a tag is what somebody else can move. + +The package half of the issue is not decided here: the mesh's package registry already proxies +the public one, and the failure the report saw has to be run again to be placed. + +## Consequences + +A module's build inputs are all in its manifest, and every one of them is something the mesh +holds a copy of. What got harder: a recipe that used to name a base image on its first line now +names an argument, and the manifest names the image. + +## How it is checked + +A builder test declares a pinned vendor image, asserts it is copied under the module's +repository and handed to the recipe as the argument, and asserts an unpinned one is refused. A +recipe test asserts an undeclared `FROM`, an undeclared `COPY --from` and an undeclared argument +are named, and that stages, declared arguments and `scratch` are not. + +## References + +- [issue 064](../04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/00-report.md) +- [ADR 0096](0096-an-upstream-image-is-copied-between-registries.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 c508e29..3b098db 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -162,6 +162,7 @@ python3 00-META/checks/index.py fail if stale - **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) +- **0097** — [A vendor image is a declared build input, and a recipe fetches nothing undeclared](0097-a-vendor-image-is-a-declared-build-input.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 37ec58e..d28567d 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/0097-a-vendor-image-is-a-declared-build-input.md - 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 @@ -215,6 +216,14 @@ test copies an index over two platforms from a fake registry behind a bearer cha mesh registry and asserts every blob arrived once, the manifests and index under their digests, and nothing uploaded on a second copy. +**A vendor image is a declared build input** +([ADR 0097](../../02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md)). A build's `on` +entry is a module's artifact or an image published elsewhere, pinned by digest, read from one +build argument; the image is copied into the mesh's registry before the build and the recipe is +handed the copy. A recipe whose `FROM` or `COPY --from` names a registry image the manifest did not +declare is refused before the build, naming it and the remedy. *How it is checked:* builder tests +on a declared and an unpinned vendor image, and a recipe test on what counts as a fetch. + ### What it puts on a machine | resource | is | a module may | diff --git a/04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/01-diagnosis.md b/04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/01-diagnosis.md index 9f97344..8acce39 100644 --- a/04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/01-diagnosis.md +++ b/04-ISSUES/064-a-mesh-build-cannot-fetch-a-modules-external-dependencies/01-diagnosis.md @@ -16,5 +16,8 @@ repositories succeed in the same Dockerfiles. **Located in:** the builder (what it tells npm and the runtime) and the catalogue (three modules -whose Dockerfiles fetch what no manifest names). Still open: a build must be re-run to place the -404, and a decision is needed on whether a vendor image is declared as a build input. +whose Dockerfiles fetch what no manifest names). The image half is decided and built: +[ADR 0097](../../02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md) — a vendor image +is declared under `build.on`, copied in, and a recipe fetching what is undeclared is refused. Still +open: the package half — a build must be re-run against the mesh's proxying registry to place the +404 — and the three recipes themselves, which now declare their images or are refused.