Files
pnpm/pnpr/docs/discovery.md
Zoltan Kochan 430d892741 feat(pnpr)!: serve every ecosystem under its own prefix (#17087)
pnpr served npm at the server root when npm was the only configured
ecosystem and moved it under /npm/ as soon as a second ecosystem was
added. Adding an ecosystem therefore broke every .npmrc registry URL,
every tarball URL already written into a lockfile, named-registry URLs,
and OIDC workload publish paths, and /npm meant the package `npm` on one
server and the ecosystem prefix on another. This settles section A1 of
pnpm/tasks#131: every ecosystem is always prefixed.

- Ecosystem::base_path is now independent of the configuration. The
  per-configuration Registries::base_path and is_only_ecosystem are gone,
  and the routes, the resolver's tarball URLs, OIDC workload paths and
  pnpr's own-origin route classification all use it.
- The default image registry still answers at /v2/ on the host root; an
  image client cannot be pointed at a prefix. The registry directory keeps
  its `prefixed` and `namedPrefixed` flags for the web UI.
- The read-only token allowlist matched the npm audit endpoints at the
  root only, so read-only tokens were already refused for audits on
  multi-ecosystem servers. It now matches them under the npm prefix.
- /metrics no longer switches between Prometheus metrics and the npm
  package `metrics` on the Accept header; the package is /npm/metrics.
- The pnpm v11 and v12 test harnesses, the registry-mock crate and the
  integrated benchmark address the fixture registry at /npm/ and keep the
  server origin for the pnpr protocol endpoints. The benchmark asks each
  revision mock's registry directory whether npm is prefixed, so it can
  still compare against a pnpr revision from before this change.
2026-10-18 16:55:55 +02:00

3.4 KiB

id, title
id title
discovery Browsing and discovery

Added in: v0.1.0-alpha.11

Cross-origin browser access and upstream discovery are both off by default. Turn them on to run a registry UI on its own origin.

cors:
  allowedOrigins:
    - https://registry-ui.example.com

registries:
  local:
    type: hosted
    access: $all
    packages:
      '@example/*': {}
  npmjs:
    type: upstream
    url: https://registry.npmjs.org/
    public: true
    search: true
  main:
    type: router
    sources: [local, npmjs]

defaultRegistry: main

An allowed origin must contain only an http or https scheme, a host, and an optional port. Listing an exact origin is the whole grant: there is no wildcard. An allowed origin may send GET, HEAD, PUT, POST, and DELETE requests with an Authorization header. pnpr never sets cookies that authenticate requests, so a cross-origin write needs the caller's token.

The upstream search setting opts one registry into browser-facing search, and also enables /-/org/{scope}/package discovery for it. pnpr applies registry routing and access rules to the entries it returns, and uses only the upstream credentials from its own configuration, never a browser caller's authorization header. Discovery refuses redirects, and configured upstream headers are sent only over HTTPS or loopback HTTP. Search totals count visible, deduplicated results and are exact across every participating source.

Browsing without a search term

GET /-/v1/search?browse=true&size=20&from=0
GET /cargo/api/v1/crates?browse=true&per_page=20&page=1

browse=true pages through the npm packages and Cargo crates a registry hosts. Use the ecosystem prefix and named registry mount your server needs. Browse results follow registry routing and access rules, exclude upstreams and staged publications, and use the same response format and pagination limits as search. An empty search without browse=true still returns nothing.

To bound the work one browser request can cause, pnpr rejects an upstream search that would scan more than 2,000 upstream results or eight upstream pages, and rejects an offset that would need a larger scan. Refine the search term when a query reaches that limit.

The registry directory

GET /-/pnpr/v0/registries lists the named registries the caller can see. It returns registries, the visible defaultRegistries keyed by ecosystem, and the mounted ecosystems with their available and prefixed flags. prefixed is false only for container images, whose default registry answers at the host root.

Each entry carries its name, its kind (hosted, upstream, or router), and its ecosystem; the identity is the pair (ecosystem, name). Concrete registries report their namespace patterns and routers their ordered sources, and either field is null when the caller's access rules do not permit disclosing it. A hosted registry also reports teamsManagedBy and rulesManagedBy (config or api, see Managing teams through the API and Changing rules through the API). The top-level admin flag says whether the caller is one of the auth.admins. Upstream addresses, credentials, storage paths, and package access rules are never returned. Responses are private and must not be cached.