Repository navigation
npm registries as a built-in format plugin: hosted, proxy and group repositories with npm login - #463
Merged
Merged
Conversation
npm, pnpm, Yarn and Bun against /npm/<repository>/: hosted, proxy and
group repositories of the new format plugin crates/formats/npm, served by the
{mount}/{repo} dispatcher like NuGet.
- Packuments, abbreviated and full (with publish times and the README of
latest), version documents, tarballs, search, ping.
- npm publish: the base64 tarball streams out of the JSON document into
the store (Upload::Document, unpack_upload), checked against the
integrity and version it claims; the deployment policy decides on
republishing.
- Dist-tags, deprecations and unpublishing as host changes (package_tag,
component.deprecation); retention never deletes a tagged version.
- Logins: npm's browser login approved on /cli-login/<secret>, a password
login (yarn npm login), logout and whoami, minting API tokens; Bearer
API tokens are accepted on every plugin repository.
- Proxies: Bearer or Basic credentials, the upstream's dist-tags, tarballs
verified against the published digests, publish times for the full
packument (Query::DatedPackages), 502 for an upstream that refuses the
proxy or serves a package that does not hold up.
- Groups: dist-tags from the first member that has them, reserved scopes,
a writable member for publishes and changes, audits forwarded upstream
without reserved names.
- Contract 0.4.0: request bodies on demand, document uploads, changes,
logins, forwards, tags, host errors in the format's shape.
- Web UI: a package's dist-tags, deprecations and README, the npm connect
snippets, the browser login approval page, and the deployment policy and
writable member for every package format.
- Docs (docs/npm.md, format-plugins.md), the npm-e2e CI job with the real
clients, changelog fragments.
Fixes #41
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BaGpNGgkQV54XvgL5D9ay2
npm 11 (CI's Node 26) checks the registry's versions before publishing and refuses a known one itself, so the republish never reached the server and its message did not match. With --force the client skips its check and the server's 409 is what refuses the republish, with npm 10 and 11 alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BaGpNGgkQV54XvgL5D9ay2
Resolve the conflicts with #458 (formats describe themselves in Rust, plugin.toml is gone) and #460 (access levels): - npm describes itself with a DESCRIPTOR like NuGet and raw; plugin.toml's upstream_token key becomes the Descriptor field `upstream_token` (UpstreamToken::Bearer for npm, None for NuGet and raw). - The dispatcher's per-action authorization uses authz::Level. - The package_tag / cli_login migration becomes 0047: main's access levels took 0046. - The cli-login web page's path is a reserved mount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BaGpNGgkQV54XvgL5D9ay2
schmitch
enabled auto-merge
October 24, 2026 14:58
schmitch
pushed a commit
that referenced
this pull request
Oct 24, 2026
main now holds #463, this branch's former base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BaGpNGgkQV54XvgL5D9ay2
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #41
npm registries for npm, pnpm, Yarn and Bun at
/npm/<repository>/: hosted, proxy and group repositories of a new built-in format plugin,crates/formats/npm, which implementsFormatPluginand is served by the{mount}/{repo}dispatcher like NuGet and raw. No npm code lives in the server: everything npm needed from the host went into the plugin contract as a general feature.The crate was asked for at
plugins/npm; #456 has since moved every format tocrates/formats/<id>/(AGENTS.md), so it sits next tocrates/formats/nugetandcrates/formats/raw, and like them it describes itself with a RustDESCRIPTOR(#458).What npm clients get
application/vnd.npm.install-v1+json) and full (publish times intime, the README oflatest), version documents, tarballs,npm search,npm ping.npm publish: the base64 tarball streams out of the JSON document into the store; the tarball must match the integrity,shasum, name and version the document claims. The deployment policy decides on republishing (409 by default).npm deprecate,npm unpublish(a version or the package; the delete permission).latestcannot be removed and moves to the highest version left.npm loginis approved in the browser on a new web UI page (/cli-login/<secret>, password or OIDC sign-in),yarn npm loginchecks a password through the realm chain; both mint an API token (90 days,[cli_login]) thatnpm logoutrevokes;npm whoami. API tokens are accepted asAuthorization: Beareron every plugin repository.registry.npmjs.orgor any registry: Basic credentials or a Bearer token (to the remote's origin only), the upstream's dist-tags and deprecations, tarballs verified against the published digests, the publication delay dated from the upstream's publish times, stale serving while the upstream is down.@acme) kept away from proxy members, a writable member that takes publishes and changes through the group,npm auditforwarded to the proxy member without the reserved names.{"error": "..."}, which they print.Contract (
forge:format@0.4.0, unreleased) and hostAdditions to the 0.4.0 contract #456 introduced, all general:
Action::ReadBody(bodies on demand, bounded, with the permission their use needs),Action::Change(unlist,relist,delete,deprecate,tag,untag; every permission a set needs is checked before any change),Action::Auth(logins performed by the host, rendered byauth_response),Action::Forward(a POST to the upstream with the proxy's credentials).Upload::Documentwithunpack_upload/parse_upload/published: a package inside a document, unpacked while it streams.Query::Tags,Query::DatedPackages(publish times even where a proxy pays for the full document),UpstreamOp::Tags,UpstreamAnswer::Located(a package URL with its digests),Fetch::accept,error_response,readme,reserved_prefix, andDescriptor::upstream_token(Bearerfor npm: a proxy's password without a username is sent as a Bearer token).package_tagandcli_login,component.deprecation(migration 0047).Admin API and web UI
allow_redeployfollows the policy. The repository form offers both for package formats, and the edit and create bodies send them..npmrc/.yarnrc.ymllines and install commands;/cli-login/<secret>approves a CLI login.Tests
crates/formats/npm: 37 unit tests and 15 protocol tests (testing::serve).crates/server/tests/npm.rs: 12 integration tests (publish, packuments, the deployment policy, dist-tags, deprecation, unpublishing, permissions, logins, proxies with a mock upstream, groups and the dependency-confusion guard, retention).npm-e2e(.github/scripts/npm-e2e.sh): npm's browser login, Yarn's password login, logout, publishes with npm and pnpm (also through the group), installs with npm and pnpm (and Bun when installed) from hosted, proxy and group repositories, dist-tags, deprecation, unpublishing, the guard andnpm audit. Branch protection needsnpm-e2eadded to the required checks by hand (it is inREQUIRED_JOBSand DEVELOPMENT.md).Run locally after merging
main(#458, #460):cargo fmt --all --check;cargo clippy --workspace --all-targets --all-features -- -D warnings;cargo sqlx prepare --workspace --check;cargo xtask changelog --checkcargo testof every workspace crate (.github/scripts/test-parallel.sh --workspace, 108 binaries) against one shared Postgres: 103 passed in the parallel run, and the 5 that collided over the shared database (retention_worker, stopped midway byadmin_retention's instance-wide switch;auth_realm,dao,encryption,workers) passed run alone. Not run: the gatedforge-e2esuite..github/scripts/npm-e2e.shwith npm 11.21 and 10.9, pnpm 11.5 and 10.28, Yarn 4.18 and Bun: passed (npm 11 refuses a known version itself, so the republish check uses--forceto reach the server).github/scripts/raw-e2e.sh(FORGE_E2E_SKIP_GITHUB=1), since raw shares the dispatcher this changes: passedtsc,vitest(417)Docs and changelog
docs/npm.md(new),docs/format-plugins.md(the 0.4.0 additions),crates/formats/README.md,README.md,DEVELOPMENT.md,forge.toml.example([npm],[cli_login]); fragments41.feature.md,41-package-options.feature.md,41.fixed.md,41.limitation.mdunderdocs/changes/2.0.0/.🤖 Generated with Claude Code
https://claude.ai/code/session_01BaGpNGgkQV54XvgL5D9ay2