mesh/merge-gate head 8d8dcd5ebb86 merged into trunk cd239283207a — pass: builds gitea, lab → novox, shanks; no bus step; every machine composes with the ch…
mesh/repo-check head 8d8dcd5ebb86 merged into trunk cd239283207a — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
The lab's credential came from the package team, which also writes the @novox packages, and the branches a run clones are not reviewed yet. The lab now contributes {"access": "read"} to its npm-package-registry requirement, and gitea's provisioner puts such a consumer on a team of its own, readers: code read on the org's repositories, package read, no write, and off the package team. The build agent, which publishes, stays on packages. A stop's SIGTERM to the run's process group killed the wrapper before it removed the credential file. The run's script now removes it as soon as its clones are done and traps every way it ends, and a run's start and lab_check remove what a crash or a reboot left.
55 lines
3.4 KiB
TypeScript
55 lines
3.4 KiB
TypeScript
// gitea's provisioner — the adapter that makes gitea a provider of the mesh
|
|
// `npm-package-registry` interface. The reconcile loop, the contributions file, and reading the
|
|
// mesh's minted password are the sdk harness's; this writes only the per-service half: how gitea
|
|
// creates and removes a consumer's npm credential (novox/hq ADR 0048/0076).
|
|
//
|
|
// **A package registry seat is one per ecosystem (novox/hq ADR 0109).** gitea holds the npm seat
|
|
// (ADR 0110). Adding cargo or PyPI is adding a provision — another `provides` entry, another
|
|
// `receives` path and another registration below — not widening this one. `git`, which gitea also
|
|
// provides, mints nothing and so registers nothing here: the mesh's own repositories are public,
|
|
// and a clone credential is not yet decided (ADR 0111).
|
|
//
|
|
// The `npm-package-registry` interface: a consumer authenticates to the npm registry at
|
|
// `/api/packages/novox/npm/` with basic auth, as `as` with the password the mesh minted, and can
|
|
// read packages under the `@novox` scope, and write them unless it asked for read only (teams.ts). The
|
|
// registry's npm owner is the gitea org `novox`; a consumer is a gitea *user* on one of that org's teams.
|
|
//
|
|
// **The user name and password are the mesh's, not the provisioner's (ADR 0048).** The mesh derives
|
|
// the login and hands it to both ends, and mints the password. gitea creates a user under exactly
|
|
// that login and sets exactly that password every run — so a rotation takes — and seals nothing: the
|
|
// consumer already has its copy through the mesh's own channel.
|
|
//
|
|
// The admin calls run through GiteaAdmin (basic auth as the mesh's gitea admin), which is the
|
|
// module's one boundary to the forge's admin API (see client.ts).
|
|
|
|
import { runProvisioner, type Provision } from "@novox/mesh-sdk/provisioner";
|
|
import { GiteaAdmin } from "../client.js";
|
|
import { grant, holds } from "./teams.js";
|
|
|
|
// Which team a consumer joins — `packages` to publish, `readers` to read only — is teams.ts's.
|
|
|
|
const gitea = GiteaAdmin.fromEnv();
|
|
|
|
// Where this registration's contributions land comes from $MESH_RECEIVES, never a path written
|
|
// here: the mesh writes the file where the manifest's `receives` says, and a second copy of that
|
|
// path in code would drift from it. One variable carries one path, so a second registration in this
|
|
// module needs the mesh to say where each provision's file is — not yet possible, and not faked.
|
|
runProvisioner("npm-package-registry", {
|
|
async create(p: Provision): Promise<void> {
|
|
// The org and its teams are ensured per create: idempotent, and no separate bootstrap step. The
|
|
// user carries the consumer's login and the mesh's minted password, set every run so a rotation
|
|
// takes; membership of its team is what grants it what it may do.
|
|
await grant(gitea, p);
|
|
},
|
|
|
|
async remove(p: { as: string }): Promise<void> {
|
|
// Login prohibited, never deleted (novox/hq issue 241: a withdrawal never destroys a consumer's data — on 2026-10-04 a misread grants file withdrew every consumer at once): deleting purges every repository the user owns.
|
|
await gitea.prohibitLogin(p.as);
|
|
},
|
|
// Asked every minute by the harness: whether the backend still holds this consumer exactly as
|
|
// the mesh gave it, so a login lost behind the provisioner's back is made again (novox/hq issue 120).
|
|
async holds(p: Provision): Promise<boolean> {
|
|
return holds(gitea, p);
|
|
},
|
|
});
|