From 2d48b85c0e45d8f3178a55bc6f0d9a28b750d8d7 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 00:09:56 +0100 Subject: [PATCH] Design 51: answer the first review of the milestone amendment (builds counted from the records, the write side's window and outage, the revert's open points, holds and the old name's events, live proof) Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- .../51-the-registries-keep-what-is-named.md | 84 ++++++++++++++++--- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md index 243393a6..d60c9c78 100644 --- a/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md +++ b/03-DESIGN/01-to-be/51-the-registries-keep-what-is-named.md @@ -62,7 +62,12 @@ asks the status view's `node_haves` for every node, away ones included, and stop whose declaration names an artifact has applied a build whose node-engine states the capability to read the new names (ADR 0614 §4), judged by that stated capability and never by a fingerprint. Its tool `registry_old_name` lists the nodes still holding it open, and drops a retired node from the window by hand, recorded as a hand act; a node holding -it open for seven days is a finding naming the node, cleared when the window closes. `cargo-package-registry` stays +it open for seven days is a finding naming the node, cleared when the window closes. Closing the old name emits +`old-name-closed` on both image seats' `.events`. A node away when the last `delivered` was said must close the +window when it returns and applies the new build, with no other delivery: the event that says such a node's return, +and the verb that answers each node's stated capabilities, are not yet named by any record or design (the stated +capability comes with the release that adds it, design 47); until they are, the window waits on the next `delivered`, +tracked on issue 1966. `cargo-package-registry` stays named, without a holder, until a build module publishes cargo packages. **One contract, a seat definition** (design 26, *A seat's definition*). Verbs: `publish`, `publish-part` (ADR 0611), `versions`, `remove` (a @@ -131,13 +136,23 @@ the verb. writes from any process on the mesh network, so the rule protects nothing until its write side is closed. So `distribution`'s own program serves the store's port in front of the registry container, which listens on its node's loopback only: reads (the OCI distribution protocol's GET and HEAD) stay open to every node's container - runtime, and a write reaches the store only from `distribution`'s own process, after a `publish` it accepted. Each + runtime, and a write reaches the store only from `distribution`'s own process, after a `publish` it accepted. + **Until every build module publishes through the seats**, the build modules' builds still running push straight to + the store, as they do today: so `distribution`'s program also accepts a write from a build module's own account + until every node's have of every build module, read through `node_haves` at its start and on each `delivered`, runs + a build that publishes through the seats; then it refuses them, naming `publish`. **While the program is down**, + the store answers no read and no write: a node pulling an image fails and retries, and what already runs is not + touched; the holder's health check, which probes the read side every minute, names it, and the program serves + again at once when it restarts, holding nothing but its transfers in `incoming`. Each holder keeps its write side as an own secret beside a read-only tool token (the forge module: `write:package`, its tool token `read:package`). - **The package seats' read sides need no credential** for the mesh's own packages: the forge serves the mesh owner's packages to anonymous reads, so the provisions `npm-package-registry` and `go-package-registry` carry no credential (form none), and mesh-build-node, which held a pair for both writing and installing, requires none (the - operator's answer of 2026-11-03, that the mesh's packages may be read without a credential). + operator's answer of 2026-11-03, that the mesh's packages may be read without a credential). The scope is the + mesh network only: the store's GET and HEAD and the forge's package reads answer anonymously there, and through no + public route. Whether the forge has a public route that would also serve its package reads anonymously is not + known to this design, to confirm with the operator before the merge. - **The forge module serves both package seats' verbs from its Go bundle**, the one that already serves its package retention, renamed for both packages; its tools bundle in TypeScript is unchanged, so no holder needs generated TypeScript types in this milestone. @@ -161,7 +176,9 @@ the verb. naming the merge it waits on. A fingerprint already numbered keeps its number. - **A package is numbered per source revision**, not per fingerprint: a second trunk build of a revision already numbered for that package (a build module's change reaching it by a second build, ADR 0603 §3, or the contracts - move's one rebuild) keeps the package's version and publishes nothing, since a package's content is its source. + move's one rebuild) keeps the package's version and publishes nothing, since a package's content is its source, + but only when `versions` holds that revision `published`; a revision numbered but never stored is published by its + next build under the same version. The build log says the version was kept, and why. So a publish that triggers a rebuild of the package's own build module (the shared contracts, the forge module that holds their registry and the Go build module that builds them, ADR 0614 §6) never comes round to a new version, and the triggers end. An image, bundle or archive is numbered per fingerprint, as above. @@ -178,9 +195,11 @@ the verb. - **An unknown floor does not stall the build of an image, bundle or archive.** The floor of `distribution`'s own names is `distribution`'s `versions`, which only its new build serves, and the same holds for the forge module and the builder: waiting would never end. So a trunk build of an image, bundle or archive whose floor is not yet known - is built unnumbered and published by its digest (as a bundle or archive is from every done key, ADR 0605 §3); once - the floor answers, the builder numbers it, and the build module calls `publish` with the version and the held - digest, which stores nothing. A package's trunk build waits for its floor, since a package is published only under + is built unnumbered and published by its digest (as a bundle or archive is from every done key, ADR 0605 §3). That + publish is what wakes the floor: the builder hears the holder's `published` on its durable consumer of + `.events` and asks the floor again then, never on a timer; once all three answer it numbers the build and emits + `numbered`, which the build module consumes to call `publish` with the version and the held digest, which stores + nothing. A package's trunk build waits for its floor, since a package is published only under its version. - **Events of the builder**: `numbered` (name, version, tag, fingerprint, commit, head when it answers an ask), on its own family, kept seven days; a waiter on a build's end does not wake on it. **Tools of the builder**: @@ -210,7 +229,10 @@ the verb. branch's end; retention may remove an old unreferenced one (below). - **The forge bridge says a branch's end**: beside its `merged`, `refused`, `open` and `pull` kinds on its stream `forge`, it says a branch-deleted kind, from the forge module's `forge.delete` event, which it consumes on a durable - consumer as it consumes the pull events. Its tool `bridge_tags` answers a package's git tags, for the floor. + consumer as it consumes the pull events. Only the forge module's account may publish `forge.delete`, the subject + being in its own namespace (design 32 § 2), so no other module can end a branch's tagged versions. Every existing + consumer of `forge` ignores a kind it does not know. Its tool `bridge_tags` answers a package's git tags, for the + floor. ### Retention @@ -220,6 +242,10 @@ With the store's write side closed, the artifact sweep (seat `mesh-artifact-swee longer deletes or holds by writing to the store: it calls `remove` on the seat that holds the thing, with the reason retention, and `hold` to keep a kept archive from removal, both granted to its holder alone by `distribution`'s caller list; its rules of what is kept (*The artifact store* below) stand, and it reads both reference forms. +`distribution` keeps what is held in its own state `holds` (bus state, template state), emits `held` and `released` +on the seat's `.events`, and its tool `registry_holds` lists what is held, since when and for whom, and +releases one by hand. A `remove` or `hold` the holder refuses to the sweep writes the sweep's finding, naming the +thing and the reason, cleared by its next run with no refusal. The Go seat keeps what the npm seat keeps: every referenced version and the newest five of each package series. A Go version is referenced when a `go.mod` on a repository's default branch requires it; the forge module reads every `go.mod` as it reads lockfiles, and a `go.mod` it could not read stops a real run before anything is deleted, as an @@ -251,6 +277,22 @@ its own rule; nothing stores the order (ADR 0612 §2, ADR 0614 §1). The edges t | The holders' `incoming` and `.events` exist before the holders start | bus state: each entry is a requirement on the provision `bus-state` of the bus module (ADR 0614 §5) | the assigner, as for any provision | | The shared contracts, the forge module that holds their registry and the build module that builds them | package and build module edges are triggers, never waits; the cycle they make holds nothing and is not refused (ADR 0614 §6); inside the group every member takes `mesh-contracts` and mesh-sdk by the group's head (the checker's group pin) | the indexer's graph | +**How many builds** (ADR 0603 §3). The three build modules change in this milestone, so every module they build is +built twice: at once with the recipe that runs, and again once a majority of build nodes runs the new build module. +The contracts move rides that first build and adds none (ADR 0613 §1). Whether the rebuild that a first numbered +publish of mesh-sdk or `mesh-contracts` triggers in their users (ADR 0614 §6) joins the second build or adds a third +is not decided by ADR 0603 or ADR 0614, so this design gives no count beyond two per module; it is put to the +operator. + +**The node-engine's own build keeps the old reference form** until every node-engine states the capability to read +the new one: a node-engine away across the milestone must be able to read the build it wakes to, or it could never +update itself (ADR 0603 §7, §9). The builder writes the node-engine's done key in the old form until the stated +capabilities, which the status view reads, show it on every node. + +**When the group is pushed.** Only once the release that adds the node-engine's stated capability (design 47) runs on +every node, read from the status view's haves before the push, and once the progressive insights of ADR 0595 and +ADR 0597 this design is written to are accepted. + **The milestone's own check** runs the changed build modules (ADR 0603 §10) against the running holders, which serve no `publish`: a changed build module whose manifest invokes a verb no running build serves is one the check cannot run, and the modules it builds are composed from their manifests, the verdict naming them and the verb (ADR 0614 §7). @@ -267,7 +309,10 @@ guard go in the same change (ADR 0606 §6, read with ADR 0612 §1). **Rolled back by one revert** (ADR 0612 §4): reverting the milestone's merge brings back the build modules' manifests that require the write credentials, so the vault mints them again as own secrets on the revert's delivery, and the builds that publish through the seats go with it. The old seat name still retires on the node-engines' -reports (ADR 0610 §4). **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images +reports (ADR 0610 §4). Two things of the revert are not decided by any record and are tracked on issue 1966: which +edge delivers `distribution`'s reverted build, which takes direct writes again, before the build modules' reverted +builds that push straight to the store; and whether a done key written in the new form is built again after the +revert or must stay readable by the reverted readers, which the old mesh-sdk refuses. **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images with a one-off credential of the bootstrap, discarded once the holders serve `publish`. ### Acceptance tests @@ -301,17 +346,30 @@ answers (a holder down whole is its module's own health check's); `registry_inco - **Holder, added for the milestone.** A write to the store's port from any account but `distribution`'s process is refused, and a read from a node's container runtime is answered; the artifact sweep's `remove` with the reason retention removes an untagged bundle and emits `removed`, and the same call from another caller is refused; `hold` - keeps an archive from removal; the forge bridge says a branch-deleted kind for the forge module's `forge.delete`. + keeps an archive from removal, emits `held` and is listed by `registry_holds`, which releases it with `released`; a + refused sweep `remove` writes the sweep's finding and its next clean run clears it; the forge bridge says a + branch-deleted kind for the forge module's `forge.delete`, a `forge.delete` from another account is refused by the + bus, and an existing consumer of `forge` ignores the new kind; with the program stopped a read fails and the + holder's finding is written, and after its restart the read is answered and the finding cleared; a build module's + old build pushing straight to the store is accepted until every node runs a build module publishing through the + seats, and refused after. - **Builder, added for the milestone.** With the seat's holder down at a first claim, a bundle's trunk build is built - unnumbered and published by its digest, and is numbered once the floor answers; a package's trunk build waits; - a second trunk build of a numbered package's revision keeps its version and publishes nothing. + unnumbered and published by its digest, the holder's `published` makes the builder ask the floor again and number + it, and the build module, hearing `numbered`, publishes the version with the held digest; a package's trunk build + waits; a second trunk build of a package's revision held `published` keeps its version and publishes nothing, + while one numbered but refused is published by its second build under the same version; with `bridge_tags` + silenced (a floor source other than the publish target) the floor finding is written and cleared once it answers. - **Checker.** A changed build module invoking a verb no running holder serves is not run, and the verdict names the modules composed from their manifests instead, and the verb. - **Vault.** Reverting the milestone's merge mints the build modules' credentials again; the bootstrap's one-off credential is gone once the holders serve `publish`. - **Live.** The status view shows each build module's new build waiting on its holder's build serving `publish` until that runs healthy (ADR 0612 *How it is checked*); no build module's provision key holds a write credential; - `registry_old_name` names the nodes holding the old name open, an away node among them. + `registry_old_name` names the nodes holding the old name open, an away node among them. After the delivery: + mesh-sdk's first numbered version is `published` on the Go seat and an image is read back by its digest, both + shown by `builder_versions`; a node runs a module from a reference in the new form; a node-engine away across the + milestone wakes and applies the new node-engine. Go's `direct`: its removal is named on the milestone's issue, and + the change that removes it adds to the Go build module's recipe test a case that refuses `direct` in the fetch. ## Reviewed by architectural domain