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.
Separate hosted storage namespaces by ecosystem (`default/<ecosystem>/` and
`orgs/<org>/<ecosystem>/`) and map package names to path segments per
ecosystem:
- Hosted registries declared under an `<ecosystem>:` group without an
explicit `org:` now default their `org` to their own registry `name`
instead of `<ecosystem>~<name>`.
- Hosted registries in different ecosystems may share an `org:` without
colliding; the collision check in `pnpr-config` compares
`(org, ecosystem)`.
- `layout::package_segments` and `layout::decode_package_segments` take an
`Ecosystem`: npm maps scoped `@<scope>/<name>` to `[@<scope>, <name>]`
and unscoped to `[@, <name>]`; Cargo and PyPI map to `[@, <name>]`; OCI
maps multi-component `c_1/.../c_k` to `[@<c_1>, ..., @<c_{k-1}>, <c_k>]`
and single-component names to `[@, <name>]`.
- `pnpr upgrade-storage` converts legacy hosted stores into the
per-ecosystem layout and maps nested OCI repositories into multi-segment
`@<c_i>` directories.
Closes https://github.com/pnpm/tasks/issues/213
Part of https://github.com/pnpm/tasks/issues/191.
Wrong claims:
- cli: drop the nonexistent `add-user --password` flag and document the
terminal/stdin password read; `oci-gc --min-age` and `cache-gc --max-age`
are the flags (the `-secs` forms are aliases); `--from` defaults to the
`storage` key, not an internal field; `info` and `http` log the same
access records; only global and server options have PNPR_* variables.
- configuration: `packumentTtl` defaults to 5m; the minimal config now
sets `auth.htpasswd.file`, and the auth section says accounts live in
memory without it or a backend; grouped hosted registries default their
org to `<ecosystem>~<name>`; the handshake is served for any surface;
the pipeline surface also satisfies "something must be served"; add
`package.update_maintainers` to the audit list.
- tls-and-reverse-proxy: remove the invalid `publicUrl:` YAML key and the
nonexistent `rate_limiting` setting.
- backup-and-restore: `pnpr verify` hashes by default (`--check-hashes`
does not exist, `--skip-hashes` skips only the hash
comparison); `migrate-storage
--verify` compares bytes.
- webhooks: audit events do not cover publishes or token creation; the
example uses a real action and the `actor` field.
- cli: the `RUST_LOG` example keeps `pnpr::audit=info`.
- install-acceleration: the `--public-url` host is exempt from the
private-address refusal.
- shared-side-effects-cache: `pnpm pipeline` publishes workspace-task
artifacts.
Missing facts: `routes.allowedPrivateNetworks`, the upstream `search` key
and canonical `maxAge`, built-in `npm.jsr.io` route, relative path
resolution, `log` defaults and `type`, `${VAR:-default}` substitution and
the full secret-field list, `oci` defaults, `artifacts.quota` bounds,
`migrate-storage` destination rules, `--osv false`, `--packument-ttl`
default, S3 `forcePathStyle`/`allowHttp` defaults, grouped org layout,
OIDC key table, container image section, `backend:` ignoring the htpasswd
and token files.
Duplicates and links: health probe bodies, reload procedure, and the
resolver allowlist now link to their one home; broken `#secret` anchor
fixed; links into the pnpm docs use absolute pnpm.io URLs.
Stale history: drop "pnpr v1" and the per-paragraph "Since alpha.N" /
"Since pnpm 12.x" prefixes, and the contributor-only extensibility text.
Also corrects the `as_filter_directive` doc comment, which claimed `http`
quiets the rest of the crate.
* docs: lint prose with Vale
Add Vale with an in-repo style, .vale/styles/pnpm, that turns the
writing rules in AGENTS.md and the Google developer documentation style
guide into lint rules. The stock Vale packages (Google, Microsoft,
RedHat, write-good) were too noisy to block on, for example 1,300
passive-voice hits in the v12 docs alone, so the rules are written for
this repo, following the GitLab model. Each one links the guideline it
enforces.
Levels:
- error, fails CI: dashes, plain-word substitutions, condescending
words, "please", "note that", hedges, puffery, optional plurals,
exclamation marks, ellipses, curly quotes, generic link text, product
spellings, sentence-case headings, repeated words, double spaces,
implementation history in comments; in changesets, bold or italic
emphasis, internal names and release-line names.
- warning: future tense, sentences over 30 words, "instead of" and
"so that" tails in changesets.
- suggestion: passive voice.
.vale.ini scopes the rules. Docs get every rule. Contributor Markdown
and code comments keep dashes and headings as warnings until their
roughly 8,100 em dashes are rewritten. Released changesets stay in
.changeset/ and are not rewritten, so lint-prose.mjs lints only the
changesets added since PROSE_BASE (the PR base in CI).
The prose workflow installs a pinned, checksum-verified Vale release and
runs `pnpm lint:prose`. The rest of this change rewrites the existing
docs, READMEs, contributor Markdown, and code comments to pass the
error-level rules. Only prose and comment text changed. Renamed
headings keep their anchors.
Spelling stays with cspell, which checks identifiers that Vale does not
see.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs: keep prose lint from flattening meaning
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Complete production-readiness requirements for pnpr Section A.
- Persisted-document format versions: introduce formatVersion to packuments,
OCI images, crate documents, and state journals, rejecting documents written
by newer format versions.
- CLI surface grammar: support `pnpr serve` subcommand, and add parse_cli_interval
for duration units (s/m/h/d) on --packument-ttl, --min-age, and --max-age.
- Strict config validation: reject unknown fields, disallow removed configuration
blocks, and normalize field naming across config and CLI flags.
- Client protocol versioning: add capability handshake at /-/pnpr, v0 resolve
and publish endpoints, policy echo in done frames, and structured error codes.
- Distribution improvements: add Node.js fallback wrapper, fix engines range,
and package tini in pnpr Docker container.
Relates to pnpm/tasks#131.
The hosted store shared one key space between packages, orgs and blobs:
a registry without an org stored packages at the store root, an org's
registry stored them in `<org>/`, a package's blobs sat beside its
document, and a nested image repository sat inside its parent's
directory. So an org collided with an unscoped package of the same name
(an unpublish of that package removed the org's whole namespace), the
implicit `<ecosystem>~<name>` org of a grouped registry was a valid npm
name, and OCI GC and verify needed ad-hoc exclusion lists. On S3,
object_store percent-encodes `~` in keys while the upload sweep compared
raw prefixes, so grouped OCI registries were never swept. This is part
of section A4 of pnpm/tasks#131.
The new layout (pnpr_storage::layout), the same on disk and in a bucket:
layout.json {"layout": 1}
default/packages/<name>/document.json
default/packages/<name>/blobs/<file>
orgs/<org>/packages/...
Every name, org and file name is one segment: `[a-z0-9._-]` as is,
every other byte as `!` and two hex digits. That keeps nested names
siblings, separates names that differ only in case on case-insensitive
filesystems, and leaves nothing for object_store to escape, so a key is
byte for byte the local path. Per-namespace records stay inside their
namespace; records every namespace shares stay at the root. Listing is
one directory read, so the `.package-index` workaround is gone, and the
blob inventory reports package and blob names instead of paths.
Startup refuses a store marked with another layout or an unmarked store
holding data, and marks an empty one. The proxy cache uses the same
package layout and is emptied once when its marker is missing.
`pnpr upgrade-storage` converts an earlier store on disk or in a bucket,
telling org directories from packages by the configured orgs; it moves
blobs before documents so an interrupted run can resume, and refuses a
directory that is both an org and a package. export, verify and
migrate-storage check the layout, and migrate-storage marks its target.
The registry fixtures the pnpm test suites serve are built in the new
layout, and the cached build is keyed by the layout version.
Close gaps left in reserved-username registration, from the review of
pnpm/pnpm#17046:
- A login that saw a reserved account, and then created it because an
admin removed the account in between, now takes back the account and
its token and answers 403. Previously the caller's password stayed
stored under the admin's name. The check runs before a token is issued.
The account is deleted only while it still accepts the login's
password, so an admin's concurrent rewrite stays, and the name's tokens
are revoked afterwards.
- `pnpr add-user` revokes tokens left under the username before it
creates the account, as the admin API already did. The create-or-update
step and token revocation move to `AuthState` so both share them.
- Document `pnpr add-user`, including that it must run while pnpr is
stopped when users live in an htpasswd file, and stop telling admins to
self-register the first admin account.
* docs(pnpr): document reverse-proxy and TLS setup, webhooks, lifecycle, and pnprServer
Document production deployment and operational architecture for pnpr and
pnpm (Section C13 and Section F of https://github.com/pnpm/tasks/issues/131):
- Add Reverse proxy and TLS deployment guide (pnpr/docs/tls-and-reverse-proxy.md)
with architecture rationale and configuration recipes for Nginx, Caddy,
Traefik, and AWS ALB.
- Add Webhooks and event notifications guide (pnpr/docs/webhooks.md)
documenting the decoupled event architecture, structured audit logs
(pnpr::audit), and object storage bucket notifications.
- Document signals, readiness probe state transitions, process lifecycle, and
standard exit codes in pnpr/docs/cli.md.
- Document S3 commit journal lifecycle, multi-replica considerations, and
disaster recovery caveats in pnpr/docs/storage.md.
- Document the pnprServer workspace setting reference in
pnpm/docs/settings/dependency-resolution.md and pnpm11/docs/settings/dependency-resolution.md.
- Update sidebars and quick start navigation.
Related to https://github.com/pnpm/tasks/issues/131
* docs(pnpr): clarify pnprServer offloads resolution and streams package metadata
Clarify that pnprServer offloads dependency resolution rather than file
fetching: the server streams back resolved package metadata frames as it walks
the graph so the client can prefetch tarballs from registries in parallel.
* docs(pnpr): address review feedback on Vector remap and pnprServer tarball fetch boundary
- Add remap transform in Vector example in pnpr/docs/webhooks.md to parse
raw JSON from Docker container logs before filtering audit events.
- Clarify direct public tarball fetching and server-side private upstream
credential boundaries in pnpm/docs/settings/dependency-resolution.md and
pnpm11/docs/settings/dependency-resolution.md.
* docs(pnpr): correct probe endpoints, shutdown lifecycle, and log shippers
* docs(pnpr): correct S3 commit journal staging paths and write-ahead sequence
* docs(pnpr): clarify proxy hop coverage and orphan pruning scope
* docs(pnpr): distinguish X-Real-IP from hop traversal and document ALB target port restriction
Add maintenance subcommands and migration tooling for pnpr (Section C12 of
https://github.com/pnpm/tasks/issues/131):
- Implement `pnpr config check` (`pnpr config-check`) to validate configuration
and report diagnostic details on routes, upstreams, auth, storage, and surfaces.
- Implement `pnpr verify` to audit hosted package documents against storage
blobs, checking for missing files, corrupted checksums, and unreferenced orphans.
- Implement `pnpr export --out <dir>` to dump packages, documents, and blobs,
with optional `--include-auth` support.
- Implement `pnpr migrate-storage` to migrate hosted packages and blobs from local
filesystem storage to an S3-compatible object store.
- Support Apache/Verdaccio `{SHA}` format in `pnpr-auth`, automatically upgrading
authenticated users to bcrypt upon first login, and provide `pnpr import-htpasswd`.
- Add backup and disaster recovery documentation in `pnpr/docs/backup-and-restore.md`
and update CLI documentation.
Related to https://github.com/pnpm/tasks/issues/131
* feat(pnpr): implement cache eviction, lru size cap, and orphan namespace pruning
Add configurable cache budget and TTL retention policies for the
upstream proxy cache, automated orphan namespace pruning on startup,
periodic runtime cache eviction, and a new `pnpr cache-gc` CLI command.
Closespnpm/tasks#131 (Section C5).
* test(registry-mock): poll for the live registry child during pending startup
Poll for the child process until it appears with its command line or the
timeout elapses, avoiding a race when sysinfo samples the process table
before the spawned child has completed execve.
* test(registry-mock): record spawned child PID via oneshot channel
* test(pnpr): set accessed time alongside modified time in cache eviction tests
* fix(pnpr): address PR review findings on cache eviction
- Reject unknown fields on cache configuration table
- Count only successfully removed files in eviction reports and size accounting
- Log and continue orphan namespace pruning if individual entry removal fails
- Prevent symlink traversal during orphan namespace pruning
- Skip pruning ~upstreams/ when secret is not explicitly configured to protect sibling caches
* fix(pnpr): preserve in-flight tmp files and check mtime before LRU eviction
- Ignore active temporary files (.tmp*) unless older than 1 hour threshold
- Prevent in-flight tmp files from inclusion in LRU candidates or totals
- Check file effective time before LRU deletion to avoid removing replaced files
- Add test verifying in-flight tmp files are preserved during eviction
Listen for Unix SIGTERM signals alongside SIGINT (Ctrl-C) to perform
graceful server shutdown in container environments such as Docker and
Kubernetes.
Implement readiness checking on storage backends (local filesystem and
S3), cache directories, and auth backends (htpasswd, SQLite, Libsql,
PostgreSQL, MySQL). Expose dedicated `/-/ready` and `/-/live` probes
mounted globally and under registry prefixes, and make `/-/ping` return
503 Service Unavailable when storage or authentication is unhealthy.
Refs: https://github.com/pnpm/tasks/issues/131
* docs(pnpr): remove stale duplicated docs and fix drifted claims
The crate README and OIDC.md under pnpr/crates/pnpr duplicated the website
docs and had drifted: a compilerCaches key that no longer exists, hardcoded
quota numbers, the old registry/ path. The pnpr.client README used the
pre-rename remoteSideEffectsCache spellings and claimed the artifact owner
must equal the username. Point both at pnpr/docs instead.
Also fix claims the code contradicts: the handshake is served when any
surface is enabled and advertises ecosystems, publish, and pipeline; the
resolve request field is registries, not namedRegistries; pnpm does not
call the handshake before an npm resolve; there is no config reload; the
packument TTL has no config key; and the --config help still described a
verdaccio-shaped file.
Install instructions now name the next dist-tag, because latest still
points at a July 2026 pre-alpha snapshot.
* docs(pnpr): address review on handshake, verify order, and root-mounted endpoints
* docs(pnpr): document the /npm/ registry bases on multi-ecosystem servers