Files
mesh-catalog/modules/gitea
jochen a9960d7a1b
mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
gitea: read npm lockfiles of every version and measure every version's size
Retention read no package-lock.json on the forge: in lockfile v2 and v3 a
packages entry's dependencies is a map of name to range, which the reader
decoded as nested entries, so all 32 files failed whole and the versions
they pin were kept by nothing. Entries are now read one by one, tolerating
unknown fields and skipping workspace links; an entry of an unknown shape
is said, not the whole file.

A dry run measured only the versions it would delete, so every kept
version answered 0 bytes. Every version is now measured, the registry's
total is answered, and a size that cannot be read is said (hq ADR 0251 §4).
2026-10-08 13:48:12 +02:00
..

gitea

The forge, and the mesh's npm package registry: it claims the git and npm-package-registry seats. Its tools, its events and the provisioner of registry accounts are the TypeScript bundle (index.ts, tools/, provisioner/). This README covers the Go bundle beside it.

The package registry's retention (novox/hq ADR 0251 §4, to-be 51)

Every publish adds a version to the registry, and until this nothing removed one. The Go bundle cmd/npm-registry says which versions each package holds, what keeps each one, and — asked with a why — deletes the versions nothing keeps.

tool what
npm_packages r every package of the registry's owner, each version newest first with its publish time, its size and why it is kept
npm_retention a the versions retention would delete, and the bytes. A dry run unless dry_run is false; a real run needs why

A version is kept when:

  • a lockfile on the default branch of any repository on the forge names it — package-lock.json (every lockfile version), npm-shrinkwrap.json, yarn.lock (classic and berry) and pnpm-lock.yaml (by each resolved name@version or name/version), whatever the version's age;
  • it is the highest published version satisfying a range that a package.json there names in its dependencies, development, peer or optional dependencies (an npm: alias counts for the package it stands for; a path, a URL, git or a workspace is not a range on this registry). npm itself installs the latest dist-tag when it satisfies the range; that version is kept by its dist-tag;
  • a dist-tag names it — and a range that is a dist-tag's name keeps that tag's version;
  • it is among the newest keep of its package, five by default (the artifact store's five builds, ADR 0189 §3), by semantic-version order. Pre-releases count among the newest.

A version that is not a semantic version is never ordered, so it is kept. A range that cannot be read, or a package whose dist-tags cannot be read, keeps every version of its package, and the answer says why under keeps_all.

Nothing is deleted on partial knowledge. A repository whose files could not be listed, or a manifest or lockfile that could not be fetched, is named under repositories_unread in every answer, and a real run then deletes nothing at all. A file that was fetched but is not readable (broken JSON) is named under files_not_read; what it would have named is not known, and the operator reads that list before a real run. Under node_modules nothing is read: that is what a lockfile already says. An empty repository, or one without its default branch, holds nothing to read and is not a failure.

A real run deletes each version through the forge's own interface (DELETE /api/v1/packages/{owner}/npm/{name}/{version}), says what it deleted and what the forge refused, and a version already gone counts as deleted.

How it reaches the forge

Over the forge's HTTP interface on the machine (MESH_GITEA_URL), as the admin account the vault delivered (MESH_GITEA_ADMIN_USER, the password in MESH_GITEA_ADMIN_PASSWORD_FILE — the same file the TypeScript bundle mints its token with), by basic authentication: this bundle mints no token and keeps nothing. The password is read per call and never logged, answered or put in an error. The registry's owner is MESH_NPM_OWNER, the owner in the seat's npm-path.

Calls to the forge run eight at a time, and one tool call reads for at most 50 seconds, inside a module's 60-second ask.

Why the manifest lists no tools

The TypeScript bundle's tools are served without a tools list, and a claim without serves offers the module's tools as the seat's verbs — so a list here would make these two tools verbs of both the git and the npm-package-registry seats. The two names are kept in ToolNames, and a test holds them to this README and to what the bundle serves.

Tests

go test ./...

Against a fake forge (httptest): what keeps a version (a lockfile whatever its age, the highest version satisfying a range, a dist-tag, the newest keep); a dry run deleting nothing; a real run without why refused; a real run deleting only what nothing keeps; an unread repository stopping a real run before anything is deleted; an unreadable range keeping its whole package; a wrong password failing without saying the password. And the range matcher against npm's rules — exact, ^, ~, x and *, comparisons with partial versions, hyphen ranges, ||, and the pre-release rule — and each lockfile format.