The builder's package-registry grant — the first this provider ever received — retried for a day saying only that an edit 404'd. Two faults under it: the API refuses an email without a dotted domain, so `@localhost` failed validation at create (the CLI that made mesh-admin accepts it, which is why the admin exists and no consumer did); and ensureUser read that 422 as 'already exists' and went on to edit a user that was never made, burying the create's own message. The address is now gitea's own hidden-address shape, and the edit path is taken only for a user that is actually there.
50 lines
2.6 KiB
TypeScript
50 lines
2.6 KiB
TypeScript
// gitea's provisioner — the adapter that makes gitea a provider of the mesh `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).
|
|
//
|
|
// The `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 and write packages under the `@novox` scope. The registry's npm owner is the gitea org
|
|
// `novox`; a consumer is a gitea *user* placed on that org's package team.
|
|
//
|
|
// **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";
|
|
|
|
// The npm registry owner: a gitea org named `novox`, whose package team every consumer joins so it
|
|
// can read and write packages under the `@novox` scope (ADR 0076).
|
|
const ORG = "novox";
|
|
const PACKAGE_TEAM = "packages";
|
|
|
|
const gitea = GiteaAdmin.fromEnv();
|
|
|
|
runProvisioner("package-registry", {
|
|
async create(p: Provision): Promise<void> {
|
|
// The org and its package team are the same for every consumer; ensuring them per-create is
|
|
// idempotent and needs no separate bootstrap step.
|
|
await gitea.ensureOrg(ORG);
|
|
const teamId = await gitea.ensureTeam(ORG, PACKAGE_TEAM, true);
|
|
// The user carries the consumer's login and the mesh's minted password, set every run so a
|
|
// rotation takes. Membership of the package team is what grants read+write on packages.
|
|
//
|
|
// The address is gitea's own convention for one that is not real: its email validation
|
|
// requires a dotted domain, so `@localhost` was refused at create — the fault that had this
|
|
// grant retrying for a day — while `@noreply.localhost` is the shape gitea itself gives
|
|
// hidden addresses.
|
|
await gitea.ensureUser(p.as, p.password, `${p.as}@noreply.localhost`);
|
|
await gitea.addUserToTeam(teamId, p.as);
|
|
},
|
|
|
|
async remove(p: { as: string }): Promise<void> {
|
|
await gitea.deleteUser(p.as);
|
|
},
|
|
});
|