Files
pnpm/pnpr/docs/quick-start.md
Zoltan Kochan 5d366c3f27 refactor(pnpr)!: apply PHILOSOPHY.md and consolidate pre-1.0 shims (#17360)
Extend PHILOSOPHY.md, pnpr/AGENTS.md, REVIEW_GUIDE.md, .coderabbit.yaml, and
.pr_agent.toml so the repository philosophy applies to pnpr and establishes
that pnpr is pre-1.0 on the alpha prerelease lane (experimental per
PHILOSOPHY.md), consolidating storage layouts, internal record formats,
configuration keys, and CLI flags in place before 1.0 without
backward-compatibility fallbacks, aliases, or migration commands between alpha
builds.

- Remove pnpr upgrade-storage and Command::UpgradeStorage.
- Remove legacy .pnpr-journal recovery path and journal manifest serde aliases
  (packument_file, tarballs).
- Remove StoredDerivedTar::Legacy and DerivedTar::scanned in .tar-integrity
  records.
- Remove the redundant /v1 segment from compiler_cache_path.
- Remove CLI subcommand and flag aliases (pnpr config-check,
  --packument-ttl-secs, --no-check-hashes, --min-age-secs, --max-age-secs) and
  the PNPR_PACKUMENT_TTL_SECS env fallback.
- Remove YAML configuration snake_case, *_secs, maxage, and backend.postgresql
  serde aliases, standardizing on camelCase.
2026-10-26 09:28:07 +01:00

3.2 KiB

id, title
id title
quick-start Quick start

Write a config

pnpr routes packages through declared registries: every origin (a locally-hosted registry, an upstream like npmjs) is declared explicitly and claims the package names it serves, and a router resolves each name to exactly one of them. Save this as pnpr.yaml:

storage: ./storage

auth:
  htpasswd:
    file: ./htpasswd
    # Allow one user to self-register, for the publish step below.
    maxUsers: 1

registries:
  # Packages published to this server. The `packages:` map is this
  # registry's namespace: only these names can live here.
  local:
    type: hosted
    packages:
      '@mycompany/*':
        publish: $authenticated

  # The public npm registry. No `packages:` map — it serves every name,
  # so it must be the last source in the router below.
  npmjs:
    type: upstream
    url: https://registry.npmjs.org/
    public: true

  # A package resolves to the first source that claims its name:
  # your scope goes to the local registry, everything else to npm.
  main:
    type: router
    sources: [local, npmjs]

# The bare server URL serves the router.
defaultRegistry: main

Replace @mycompany with your own scope. There is no fall-through between registries: a @mycompany/* package that isn't published locally is a definitive 404, never a request to npmjs. This is what closes dependency confusion.

Start the server

pnpr -c ./pnpr.yaml

It listens on 127.0.0.1:7677. Running plain pnpr with no config works too. The bundled config proxies npmjs and provides a local hosted registry at /npm/~local/, whose packages need a login. It allows no self-registration, so create accounts with pnpr add-user.

Point a client at it

Configure pnpm (or npm/yarn) to use the local server as its registry:

pnpm config set registry http://127.0.0.1:7677/npm/

Now pnpm install fetches packages through pnpr. Packages proxied from the upstream are cached, so subsequent installs are served locally.

Each registry is also directly addressable at http://127.0.0.1:7677/npm/~<name>/. For example, you can point only your scope at the hosted registry instead of using the router.

Publish a package

Create a user and publish:

pnpm login --registry http://127.0.0.1:7677/npm/
pnpm publish --registry http://127.0.0.1:7677/npm/

The publish routes through the router to the local hosted registry. Publishing a package that resolves to an upstream, or whose name no registry claims, is rejected.

Where to go next