@volter/twin-npm-registry
v2.0.14
Published
Local npm-compatible package registry and advisory database twin built on @volter/world-core.
Readme
@volter/twin-npm-registry
A local, event-sourced npm-compatible package registry and advisory database. It serves package
metadata and tarballs to unmodified npm and Bun clients, accepts real npm publish documents,
answers npm's bulk-advisory endpoint, and can enforce the token/scoped-package behavior used by
npm.pkg.github.com.
world-npm-registry serve --port 4873
npm publish --registry=http://127.0.0.1:4873 --//127.0.0.1:4873/:_authToken=twin-token
npm install --registry=http://127.0.0.1:4873 your-packageWith no flavor flag, the original Host selects independent npmjs or GitHub Packages state; this
is the mode used by NPM_REGISTRY_TWIN_URL and the world injector. Use --github only to force a
single GitHub flavor. GitHub publication requires a scoped name, while repository metadata is
optional and is treated as linkage metadata when supplied. This is fake local auth: any non-empty
Bearer token is accepted, so no real npm or GitHub credential enters the world.
Publication storage
New local publishes store tarball bytes once, by SHA-256, through the kernel's blob store under
the service's resources/blobs/sha256/ directory. The action retains metadata and blob references;
the connector reconstructs the original npm attachment document when pushing upstream. Blobs are
written before the atomic package/version action becomes visible. Failed or conflicting publishes
can leave unreferenced blobs until the World is discarded; they cannot change a published version.
Downloads verify the referenced bytes and fail if the blob is missing or corrupt. Connector
publication hydrates local bytes before recording a vendor-write intent, so a repaired local
read failure can retry safely without being mistaken for an uncertain provider outcome.
Existing inline-base64 actions remain readable and pushable without migration. Connector pulls and confirmed observations keep their existing inline representation, preserving their public return shape and the kernel's event-based fork behavior. Consequently large pulled histories can still be expensive; this optimization targets local publish/seed histories. Unusual noncanonical base64 attachment strings are retained inline to preserve their exact upstream representation.
Back up or relocate the complete service directory, including resources, rather than only its
JSONL files. A newly seeded World uses compact actions; existing inline histories are not rewritten
automatically. The shared projection still replays metadata and retains its existing locking,
duplicate-action, revert, and confirmation semantics.
Validation on the original 300-publication incident dataset (345 MB of archive bytes) produced a 1.40 MB action log, versus 920.6 MB inline, and about 347 MB total allocated storage. All 300 archives were downloaded and checked against their original SHA-256 values. The registry peaked at about 404 MB physical footprint in this run, versus the incident's recorded 21.7 GB peak; publication took 6.3 seconds and publication plus verification took 14 seconds. This is a measured local-publish workload, not a bound on arbitrarily large uploads or connector pulls.
Coverage
Done today:
- full packuments, version/tag manifests, binary tarball downloads and exact HEAD lengths;
- immutable-version publishing through the real npm CLI document/attachment protocol, serialized across processes as one decision-and-append transaction;
- crash-safe publication as one durable action projecting package metadata and version-addressed tarball bytes, so reused upload filenames cannot alias two immutable versions;
- distribution-tag list/add/remove, search, ping, and token-backed
whoami; - bulk advisory lookup, including deterministic twin-only advisory seeding;
- independent
registry.npmjs.organdnpm.pkg.github.comnamespaces, with GitHub token/scoped-name behavior; - zero-edit unmodified npm 11 publish/install through the world injector and official registry URLs;
- connector pull of named package metadata plus every advertised tarball by default, with explicit version subsets (including an explicit empty list) projected as honest partial packuments;
- connector push of pending local package-publish actions through an injected, guarded client.
Connector push treats a publication as one compound unit. A lost success response or possible
immutable-conflict 403 is confirmed only after the exact name/version, every caller-owned immutable
version-manifest field, and tarball bytes reconcile from the remote. Remote integrity/shasum values
are checked against those bytes even when the caller omitted them; only the connector's explicit
registry-owned/version-normalization allowlists and normalized tarball URLs may differ. A 403 that
does not reconcile remains a failed attempt, so corrected authentication or policy can retry it.
The connector then fetches every tarball advertised by the complete observed packument before
confirming, preserving concurrent versions, maintainers, tags, and other mutable root fields without
creating dead local routes. One confirmation covers the package, all fetched tarballs, and
tombstones for previously observed tarballs absent from that complete replacement packument.
Replacement pulls apply the same retirement rule. Explicit
subsets remove omitted versions, their tags, attachments, and version-indexed time entries, so
metadata never advertises bytes the connector did not fetch.
Ordering blocks only later publications or pulls of that package, so an unrelated package can continue after a failure. A durable per-package workflow lease spans provider write/readback, tarball acquisition, and confirmation across processes; a per-package durable observation clock makes that order causal rather than dependent on which request finishes last. Same-process callers also share one promise. A dead same-host lease holder is reclaimed, but its orphaned or transport- ambiguous write intent is exact-readback-only: it is never blindly repeated.
Publication decisions and connector observations share one service projection lock, so a remote version cannot appear between the immutable-version check and the local action append. Confirmation folds the accepted package and tarball together under that lock; content-addressed observation ids make a retry safe after a crash even when mutable remote tags or maintainers changed meanwhile. Distribution-tag actions store compositional tag overrides instead of packument snapshots, so a pending local tag change cannot hide versions or root metadata observed by a later pull/confirmation. An override is exposed only while its exact target version is visible, keeping packument, tag, version, and search reads coherent after replacement observations. Pending publications likewise store a per-version overlay: later nonconflicting remote versions and mutable roots remain visible, while the exact local version and its bytes remain authoritative until confirmation.
The world injector owns only the two official hosts. Public inspect-project and adoption coverage
both discover custom destinations from .npmrc, bounded bunfig.toml install.registry and
install.scopes, publishConfig.registry, and inspected NPM_CONFIG_REGISTRY/
BUN_CONFIG_REGISTRY values. Bun string, inline-table, and nested-table registry URLs use bounded
$VAR/${VAR} substitution from inspected env files. Discovery does not execute shell operators,
read ambient process credentials, or treat Bun auth/cache/CA fields as destinations. Custom hosts
remain loud, explicitly unknown adoption-coverage gaps; finding a destination never claims the
injector owns it. Public inspection output strips URL userinfo, query strings, and fragments before
returning or formatting destinations, while host/path classification remains intact.
The npm fidelity lane is unmodified npm 11 against unchanged official URLs. Bun is also unmodified,
but is pointed at an explicit registry URL because it does not use the Node injection seam.
The committed npm OpenAPI census is the complete 33-operation inventory at exact upstream commit
f9356baa0575cd7849d7bdc8fcaba7c3d8e80e56. Its fixture is an operation denominator only;
schemas, parameters, request/response shapes, components, and security stay authoritative in the
pinned first-party YAML files.
Planned capabilities are enumerated in npm-registry-capabilities.ts, including exact install-v1
abbreviated metadata, Quick Audit, replication feeds, owner/org/profile/access/team/token
administration, trusted publishing, staged-package review, version lifecycle status, provenance verification, download metrics, deprecation, and
the full unpublish conflict protocol. Unmodeled operations fail loudly.
No UI mirror
The registry protocol is the product. npm and Bun clients consume it directly; npmjs.com and GitHub's package pages are separate discovery/account-management products, so this pack does not fabricate a browser dashboard.
Out of scope
- CDN/edge-cache timing and geographically distributed durability: a local twin replays bytes and metadata, not npm's production delivery physics.
- Real npm/GitHub identity, billing, malware review, and policy enforcement: local fake auth proves client behavior without contacting either vendor.
