Extends ADR 0075. Surfaced fixing builder's hand-faked package-registry binding tonight: gitea's manifest declares the provision once with a single npm-path, conflating what should be independently assignable per ecosystem (npm/cargo/docker/...) the same way artifact-store and package-registry were themselves split. Cited in 22-the-work-ahead.md's Phase 2, where the target state this decision points at was already described a week ago. Numbered 109, not 108: route-proxy's policy feature (mesh-controller PR still-unwritten decision record — reserved but never committed. Renumbered around it rather than colliding.
6.2 KiB
topic, status, date, deciders, reconstructed, extends
| topic | status | date | deciders | reconstructed | extends |
|---|---|---|---|---|---|
| the tiers | accepted | 2026-09-25 | jochen | false | 0075-two-stores-and-which-provides-what.md |
109. A package registry seat is one per ecosystem, not one for all of them
Context
Fixing builder's consumption of package-registry tonight surfaced the shape ADR 0075 actually
left implicit. 0075 split artifact-store from package-registry and said the second is "an
ecosystem's own registry — npm, cargo, PyPI, Go" — but it defined one provision for all four,
not one each.
What that produces, read from the manifests as they stand:
gitea'smodule.jsondeclaresprovides: package-registryonce, and itsservesblock carries exactly one path:npm-path. Nothing names a cargo or PyPI endpoint, though gitea's own package API serves both.builderhad, until tonight, a hand-written JSON fragment standing in for a real grant —{"provision": "package-registry", "from": "gitea", "at": "127.0.0.1", ...}— because nothing in the interface gave it a real one to ask for. The fragment namednpm-pathspecifically; there was nowhere to put a second ecosystem's endpoint even if one had been wired.- The fix applied tonight declares
requires: ["artifact-store", "package-registry"]and lets the mesh mint the grant properly — correct for what exists today, but it is one seat standing in for what should be several, the same conflation 0075 itself named and did not resolve for this provision specifically.
The version-skew problem 0075 wrote down for the artifact-store/package-registry split repeats one level down, inside "package registry" itself: npm resolves by name and range from one namespace, cargo from another, and a single grant conflates them exactly the way one store for both digests and ranges would have.
Considered Options
1. One package-registry provision, gitea answers every ecosystem it can. What exists today.
Simplest to grant — one credential, one binding, done once per consumer. Rejected: a consumer
that only ever needs npm still receives a grant shaped to cover cargo and PyPI, and there is no way
to hand off only npm to a different provider (verdaccio, say) without renegotiating the whole
provision for every consumer of any ecosystem.
2. One provision, parameterised by ecosystem. requires: package-registry plus a declared
ecosystem: npm alongside it, still one interface. Rejected: the provides/requires refusal
mechanism this mesh already uses (two providers of one provision is a naming conflict until
resolved) would need to become conditional on a parameter it does not otherwise carry anywhere in
the mesh's resolution — a special case for exactly one provision, rather than the mesh's existing
mechanism applied again.
3. One provision per ecosystem — npm-package-registry, cargo-package-registry,
docker-package-registry, and so on, each independently provides/requires. Chosen.
Decision
A package registry seat is one per ecosystem. npm-package-registry, cargo-package-registry,
docker-package-registry — each its own provision, resolved, granted, and refused exactly the way
artifact-store and today's single package-registry already are. Adding an ecosystem is adding a
provision, not widening one.
Gitea may hold several seats at once. Nothing here says gitea answers only one; ADR 0075
already established that a provider may answer more than one named thing on one machine ("a mesh
running gitea for git and packages alongside a registry serving artifacts is an ordinary
arrangement"). Gitea fulfilling npm-package-registry and cargo-package-registry both is the
expected shape, not an exception.
Each seat's grant is independent. A consumer that only needs npm holds only the
npm-package-registry grant. Moving that one ecosystem to a different provider — verdaccio,
named directly as the motivating case — means assigning npm-package-registry to verdaccio and
leaving every other seat exactly where it was. No consumer of cargo-package-registry observes
the change; no manifest naming package-registry broadly needs to be found and re-read.
builder's fix tonight is the interim shape, not the target. It correctly consumes the one
seat that exists today (package-registry, npm in practice). Splitting it becomes, later,
replacing that one line with the ecosystems builder actually uses — a manifest change, not a
redesign of how builder asks for anything.
Consequences
gitea'smodule.jsongains aprovidesentry per ecosystem it actually serves, each with its ownservesblock (npm-path, a cargo path, a PyPI path) in place of the onepackage-registryentry with a singlenpm-pathinside it.gitea's provisioner (modules/gitea/provisioner/index.ts) currently runs onerunProvisionerregistration forpackage-registry; each seat needs its own registration, or one provisioner keyed by which seat'screate/removefired — the mesh'sProvisiontype does not yet carry which named provision a call is for when a module answers more than one, and that is worth checking before assuming the harness already supports it.- Every consumer's
requiresmoves from the one name to however many ecosystems it actually uses.builderis the only known consumer today; widening later is one manifest line per module, not a migration. verdaccio's role sharpens: not "package-registry, an alternative for npm alone" (0075's phrasing) but a namednpm-package-registryprovider, a straight swap against gitea's answer to the same seat.- Not solved here: whether
cargo-package-registryandpypi-package-registryare needed at all before something actually consumes them. This record names the shape; building unused seats is its own decision.
References
- ADR 0075 — the record this extends; defined
package-registryas the second provision without splitting it per ecosystem. mesh-catalog modules/builder/module.json— tonight's fix, the interim single-seat shape.mesh-catalog modules/gitea/module.json,modules/gitea/provisioner/index.ts— today's single-provision, npm-only implementation.