Deploy a scoped private npm registry to your own Cloudflare account.
Publish, inspect, and install packages with familiar npm commands. pkgflare provisions the Cloudflare resources and deploys the registry through one pkgflare deploy command.
- Standard package clients. Publish with npm or Bun; install with npm, pnpm, Yarn Classic, or Bun.
- Storage in your account. A Cloudflare Worker serves package archives from R2 and metadata from D1.
- Read and publish access. Authenticate with Cloudflare Secret tokens or short-lived GitHub Actions OIDC tokens with per-package grants.
Get started · Compatibility · Authentication · Operations
pkgflare serves private packages under your configured scopes. Public dependencies continue to use the client's default registry. It does not provide a hosted service, web UI, npmjs.org proxy, user database, or team model.
You need Node.js 22 or later and a Cloudflare account with Workers, D1, and R2 available. The steps below use npm and Bash or Zsh.
Use three separate directories:
| Project | Purpose | Credential |
|---|---|---|
acme-registry |
Configure and deploy the registry | Wrangler authentication |
acme-example |
Publish @acme/example |
Publish token |
acme-app |
Install and use @acme/example |
Read token |
Create a dedicated, private deployment project:
mkdir acme-registry
cd acme-registry
npm init -y
npm install --save-dev @ponharu/pkgflare
npx pkgflare init
npx wrangler loginEdit the generated pkgflare.config.ts:
import { defineConfig } from "@ponharu/pkgflare";
export default defineConfig({
name: "acme-registry",
// Use your Cloudflare account ID, not a zone ID.
accountId: "0123456789abcdef0123456789abcdef",
scopes: ["@acme"],
auth: {
provider: "secrets",
tokens: [
{ binding: "PKGFLARE_READ_TOKEN", permissions: ["read"] },
{ binding: "PKGFLARE_PUBLISH_TOKEN", permissions: ["publish"] },
],
},
});Replace the account ID, deployment name, and scope with your own. You can omit accountId when Wrangler can resolve exactly one account. The default endpoint uses workers.dev; for a custom hostname or CI credentials, see Cloudflare authentication and domains.
In acme-registry, run:
npx pkgflare deployThis creates the D1 database and R2 bucket, applies migrations, deploys the Worker, and prints the registry URL and Secret registration commands. Requests require a registered token before they can succeed.
Run the following command twice to generate separate read and publish tokens. Save each value in your password manager:
npx pkgflare token generateRegister the read token, then the publish token. Paste each value at Wrangler's prompt:
npx wrangler secret put PKGFLARE_READ_TOKEN --config .pkgflare/wrangler.json
npx wrangler secret put PKGFLARE_PUBLISH_TOKEN --config .pkgflare/wrangler.jsonKeep token values out of source files and Git. Commit the configuration, lockfile, and generated state files to your private deployment repository as described in deployment state.
In a new terminal, create the package project:
mkdir acme-example
cd acme-exampleCreate .npmrc using the registry URL printed by deploy. Replace the example hostname on both lines, and use the scope from your registry configuration:
@acme:registry=https://acme-registry.example.workers.dev
//acme-registry.example.workers.dev/:_authToken=${NPM_TOKEN}Set NPM_TOKEN to the publish token. This prompt hides the value and keeps it out of shell history:
printf 'Publish token: '
read -r -s NPM_TOKEN
printf '\n'
export NPM_TOKENCreate package.json. Replace @acme if you configured a different scope:
{
"name": "@acme/example",
"version": "1.0.0",
"type": "module",
"exports": "./index.js",
"files": ["index.js"]
}Create index.js:
export const greeting = "Hello from pkgflare";Publish and inspect the package:
npm publish
npm view @acme/examplenpm view should report version 1.0.0. Every publish needs a new version. Do not set "private": true in the package manifest: npm uses that field to prevent publication to any registry.
In another terminal, create a consumer project:
mkdir acme-app
cd acme-app
npm init -yCopy the package project's .npmrc into acme-app. Set NPM_TOKEN to the read token:
printf 'Read token: '
read -r -s NPM_TOKEN
printf '\n'
export NPM_TOKENInstall the package and run its exported code. Use your configured scope in both commands:
npm install @acme/example
node --input-type=module -e 'import { greeting } from "@acme/example"; console.log(greeting)'The final command prints Hello from pkgflare.
The .npmrc contains an environment variable reference and can be committed. Keep the token value in your shell environment or CI secret store.
| Operation | Tested clients |
|---|---|
| Publish | npm, Bun |
| Metadata and install | npm, pnpm, Yarn Classic, Bun |
| List, add, and remove dist-tags | npm |
Yarn Berry and other clients are outside the tested compatibility baseline. pkgflare does not implement npm login, npm adduser, npm unpublish, npm deprecate, search, or the npm audit API.
Published versions are immutable. Use a publish token with npm dist-tag to promote or roll back an existing version:
npm dist-tag add @acme/example@1.1.0 latest
npm dist-tag add @acme/example@1.0.0 latestBoth versions must already be published. Moving a tag affects future tag-based resolution; it does not rewrite consumers' lockfiles or remove a version.
Package sizes are subject to Cloudflare request-body and Worker CPU limits. The request-body limit applies to the complete Base64-encoded publish request, which is larger than the tarball. Non-attachment metadata is limited to 1 MiB and JSON nesting to 128 levels. See Workers limits when choosing a plan.
| Credential | Access |
|---|---|
| Secret read token | Read every package in the registry |
| Secret publish token | Read and publish every package, and change dist-tags across all configured scopes |
| GitHub Actions OIDC token | Access the packages and operations granted to a trusted workflow |
Secret-token permissions cannot be restricted per package or scope. Use GitHub OIDC package grants or separate registry deployments when publishers need narrower access.
For GitHub Actions, follow the OIDC setup guide. It covers registry trust rules, authentication before dependency installation, and reusable workflows. For Secret tokens, see rotation and revocation.
Run the CLI from the deployment project where @ponharu/pkgflare is installed:
pkgflare init
pkgflare deploy [--config <path>] [--secrets-file <path>] [--adopt-existing]
pkgflare token generate
pkgflare auth github --audience <audience>
Use npx pkgflare to invoke the installed binary. --help or -h displays usage without executing a command. Unknown options, extra arguments, repeated options, and missing option values are rejected. deploy does not support --dry-run.
token generate creates a random token locally and prints it once. pkgflare does not store, distribute, list, or revoke tokens. auth github prints a short-lived JWT for command substitution and requires the GitHub Actions OIDC environment.
| Task | Guide |
|---|---|
| Configure a custom domain or deployment credentials | Cloudflare authentication and domains |
| Preserve resource IDs between deployments | Deployment state |
| Upgrade pkgflare or deploy from CI | CI deployment and updates |
| Retry a deployment or adopt existing resources | Deployment recovery |
| Back up or restore package data | Backups and restoration |
| Investigate an error response | Troubleshooting |
Install Node.js 22 or later and the Bun version declared in package.json, then run:
bun install --frozen-lockfile
bun run check
bun run test| Task | Document |
|---|---|
| Choose checks, contribute a change, or understand releases | Contributing |
| Change request handling or deployment behavior | Specification |
| Understand storage, authentication, or deployment design | Architecture |
Report vulnerabilities using SECURITY.md. Keep tokens, authorization headers, Cloudflare credentials, and private package contents out of public issues.