Self-hosted npm registry
A self-hosted npm registry serves private packages through the standard npm install protocol from infrastructure you control, rather than a third-party host. Caisson runs one on a Cloudflare Worker: it answers bun install @caisson/<module> with real abbreviated packuments and tarballs from R2, gated by the same offline license-token check as the public index, no forked npm client required.
In code
// D3 gate: entitled → allowed (null); unentitled + no auth → 401 (retry with token); unentitled +
// auth present → 404 (indistinguishable from unknown, ADR-0076 no-existence-leak).
function gateStatus(
entitled: Set<string>,
id: string,
hasAuth: boolean,
): number | null {
if (entitled.has(id)) return null;
return hasAuth ? 404 : 401;
}
// ...
const pk = PACKUMENT_RE.exec(path);
if (pk) {
const name = pk[1];
if (name === undefined) return errorJson(404, "not_found");
const id = `@caisson/${name}`;
const status = gateStatus(entitled, id, hasAuth);
if (status !== null) return errorJson(status, "not_found");
return json(
abbreviatedPackument(validated, sidecar, id, url.origin),
200,
);
}How it holds
Reuses the index Worker's entitlement math, not a second gate
resolveGate() runs the same baseModuleIds() union expandEntitlements() computation the read-only index route already runs; the npm surface doesn't reimplement license checking, it calls the injected resolveEntitlements against the same offline-Ed25519 verify.
No-existence-leak gating
gateStatus() returns 401 (retry with a token) when the request carries no Authorization header, and 404 (indistinguishable from an unknown package) when it does and still isn't entitled, so a probe can never learn whether an unpurchased module even exists (the D3 no-existence-leak lock, ADR-0223).
Real abbreviated packuments, not a redirect
abbreviatedPackument() synthesizes the application/vnd.npm.install-v1+json shape straight from the inlined index plus the tarball sidecar, exposing only versions actually packed and uploaded to R2, so the client resolves a normal dependency tree with no forked install tool.
Every response is per-caller, never shared across buyers
gatedHeaders() stamps cache-control: private, no-store and Vary: Authorization on every packument and tarball response, so a commercial package served against one buyer's license token is never served from a shared cache to a different, unentitled caller.