diff --git a/02-DECISIONS/0071-where-genesis-gets-its-source.md b/02-DECISIONS/0071-where-genesis-gets-its-source.md new file mode 100644 index 0000000..6ebb201 --- /dev/null +++ b/02-DECISIONS/0071-where-genesis-gets-its-source.md @@ -0,0 +1,80 @@ +--- +topic: the tiers +status: accepted +date: 2026-09-12 +deciders: jochen +reconstructed: false +extends: 0070-the-catalogue-owns-the-module-graph.md +--- + +# 71. Genesis clones from a mesh, and checks what it got + +## Context + +**[ADR 0070](0070-the-catalogue-owns-the-module-graph.md) has the init builder clone the source, +and does not say from where.** [ADR 0067](0067-genesis-is-a-pivot.md) had already rejected building +at genesis partly for that reason: the forge holding the source runs *on* the mesh, so a total +rebuild would need the mesh it is rebuilding. + +That objection is real but narrower than it reads. It only binds when the mesh being raised and the +mesh holding the source are the same one, which is true exactly once. + +## Decision + +**Genesis clones from a mesh's forge, reached by name.** Any mesh that holds the source can serve +it. The first mesh is not structurally special — it is simply the only one that existed when there +was nothing else to clone from. + +**If the mesh serving the source is lost, the name moves to another mesh that holds a copy.** +Recovery is a name pointing somewhere else, not a backup being restored. This is what makes the +source's survival a property of there being more than one mesh, rather than a property of somebody +having remembered to take a copy. A mesh that has installed from that name holds the source +afterwards, so every installation adds a place the name could point. + +**Genesis names a commit and checks what it got.** It does not clone whatever a branch happens to +point at. The forge a mesh installs from is the trust anchor for everything that mesh will ever +run, and a branch is a moving target that somebody else controls. + +*This is not hypothetical.* On 2026-09-11 the forge that would serve this role was running a +cryptominer, and its git operations were being tampered with in flight — output injected into the +protocol stream by a hook that fired on every fetch. Nothing was altered: the repositories were +verified against local copies and found byte-identical. But a mesh installing from that name during +those hours had no way to establish that for itself, and would have had none. + +## Consequences + +The init builder needs a name it can resolve and a commit it can verify, and nothing else. It does +not need to know which mesh answers. + +Whoever operates the mesh that name points at carries a responsibility to everyone installing from +it, and should know that. It is not merely a convenience host. + +A mesh that cannot reach any forge cannot be raised. That is a real limit and it is accepted: the +alternative is carrying the whole source in the installer, which makes the installer a release +artifact that goes stale rather than a program that fetches what it was told to. + +## Open — what relationship a mesh keeps afterwards + +**Not decided, and named here so it is not decided by accident** by whoever writes the init +builder. Two shapes, and they are meaningfully different: + +**A snapshot, and then independence.** A mesh installs once, mirrors the source into its own forge, +and has no upstream afterwards. It is fully self-hosted, in the sense that nothing it needs lives +anywhere else. Updates are then something an operator does deliberately, by pulling changes in — +tooling for which is possible and is not a priority. + +**A continuing upstream for core modules**, the way a distribution serves packages and a separate +collection serves everything else. A mesh keeps looking at the origin for the modules that make a +mesh a mesh, and holds its own for the rest. + +The first is more obviously aligned with the rest of this design, which is arranged so nothing a +mesh needs depends on somebody else continuing to host it. The second is more convenient and makes +a security problem in one forge everybody's problem. Neither is chosen here. + +## How this is checked + +| Rule | Checked by | +|---|---| +| Genesis needs only a name and a commit | A mesh is raised with the name pointed at a different mesh than the last time, and the result is identical. | +| What was cloned is what was asked for | Genesis is pointed at a commit and refuses a forge serving different content under it, rather than building what it received. | +| Losing the serving mesh is survivable | The name is repointed at a mesh that installed from it earlier, and a raise succeeds. | diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index fc67ee1..499fddd 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -103,6 +103,7 @@ python3 00-META/checks/index.py fail if stale - **0068** — [The lab takes requests, one at a time, and runs each from its own copy](0068-the-lab-takes-requests.md) *(proposed)* - **0069** — [A module is a repository and a path within it](0069-a-module-is-a-repository-and-a-path.md) - **0070** — [The catalogue owns the module graph, and genesis builds rather than carries](0070-the-catalogue-owns-the-module-graph.md) +- **0071** — [Genesis clones from a mesh, and checks what it got](0071-where-genesis-gets-its-source.md) ### What runs on them, and how it gets there