CAT-BN3: mesh-build-node publishes through the registry seats and holds no registry credential

Bundles go through oci-artifact-registry and answer the seat's reference;
a trunk package is packed under the builder's version and published through
npm-package-registry, so the holder can check it with the builder. Reads are
anonymous, so the write pair, the sealed secret and the store's address go.
The one-part sender moves into buildstep for both build modules, so the move
to the registry clients is made in one place.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 15:30:06 +01:00
parent 39a8d8d7ee
commit af280050d6
21 changed files with 410 additions and 380 deletions
+13 -11
View File
@@ -24,9 +24,9 @@ the module runs `npm ci --ignore-scripts` there once (a directory without its `.
**How the registry is reached** (the choice of issue 1488): the lock holds @novox/mesh-sdk at the forge's public
address, and npm 12 installs a locked tarball only from the registry it is configured with. The module therefore
requires `npm-package-registry` (read access, provided by gitea) and, like the lab module, takes its binding and sealed
secret as files in its home. The install gets a private npm config file with the `@novox` scope at the binding's
`public-root` (else scheme, address, port and npm path) and the bound account's credential, and removes it afterwards;
requires `npm-package-registry` (provided by gitea, form none: reads are anonymous) and, like the lab module, takes its
binding as a file in its home. The install gets a private npm config file with the `@novox` scope at the binding's
`public-root` (else scheme, address, port and npm path), with no credential, and removes it afterwards;
every other package resolves from the public registry. This is the way today's build agent names it.
The runtime tree for bundles with externals (the pruned sdk and nats today in `/app/runtime`) is not installed
@@ -83,14 +83,16 @@ toolchain install: a long step answers at once with its id and ends on `<node>.<
toolchain's packages (not typescript or esbuild) copied to `stage-<artifact>/node_modules`: the build agent's depth
(`/app/modules/module`, `/app/node_modules`), so esbuild's `../../node_modules/...` path comments, and the bundle's
digest, equal its. Copies, not links, because the mesh makes no symlink. A bundle that keeps an external gets them in its own `node_modules`.
Once the build ended, this process pushes the packed archive to the artifact store (novox/hq ADR 0498 §1 §2, mesh-sdk's
`buildstep.PushBlob`, the address in `${dir:state}/registry`) under `<module>/<artifact>` by its digest, a candidate's as a
trunk build's, and answers `reference: artifact-store://<module>/<artifact>/blobs/<digest>`; a push that fails fails the
step `not stored: <artifact>: <the store's words>`. The packed bytes never stay on the node. tsc and esbuild, like the
Once the build ended, this process publishes the packed archive through the seat `oci-artifact-registry` (design 51
*Publishing*; buildstep's one sender, `buildstep.PublishOne`, the manifest `invokes` publish and publish-part) under
`<module>/<artifact>` by its digest, a candidate's as a trunk build's, and answers the seat's reference
`oci-artifact-registry/<module>/<artifact>@<digest>`; a publish the holder refuses, or a seat nothing holds, fails the
step `not stored: <artifact>: <the holder's words>`. The packed bytes never stay on the node. tsc and esbuild, like the
type-check and the test script, run shut off in bubblewrap (ADR 0448): no network, none of the account, the toolchain
read-only, the npm cache beneath a throwaway layer unless the step is told `publish` (a trunk build).
A `package` artifact (ADR 0498 §5) is built by its own `build` script and packed with `npm pack --ignore-scripts`, both
shut off; its digest is the packed file's. Only with `publish` is it published, by this process with the registry's
write credential (never in a workspace or a step's mounts): a version the registry lacks is published, the same
packed file already there is done without a publish, another file under that version fails `version taken:
<name>@<version>`; the reference `<name>@<version>` is answered only then. A candidate answers its digest alone.
shut off; its digest is the packed file's. A trunk build the builder gave a version packs it under that version (its
package.json's `version` set for the pack, then put back) and publishes it through `npm-package-registry`; the holder
asks the builder, answers held for the same file and refuses another under that version. The reference
`<name>@<version>` is answered only then. A candidate, or a trunk build not numbered, answers its digest alone.
The module holds no credential for the registry: reads are anonymous (ADR 0346) and writes go through the seat.