Appearance
npm registry
ReleaseDesk includes an npm-compatible registry for publishing and installing packages with the stock npm, pnpm, yarn and bun clients. Tarballs live in the same storage backend as your artifacts (disk or S3/R2).
The registry is off by default. Enable it with RD_NPM_ENABLED=true. It is served either at the root of a dedicated host (RD_NPM_HOST, for example npm.example.com) or, without extra DNS, under /npm/ on the dashboard origin. RD_NPM_MAX_PUBLISH_BYTES caps the publish body (default 100 MB).
Packages are always scoped. The scope is the slug of the organization that owns the package, so @acme/ui belongs to the org acme and no other org can publish into that scope. Unscoped names and a few reserved scopes are rejected.
Tokens
Create an API token under Settings → Tokens with npm:read to install private packages, plus npm:publish to publish. Leave App on All apps. Public packages can be read anonymously.
npm login is not supported; it returns a message pointing here. Use a token instead.
Configure clients
Pin the scope to ReleaseDesk so it can never resolve to registry.npmjs.org (this prevents dependency confusion). Put this in the project or user .npmrc:
ini
@acme:registry=https://npm.example.com/
//npm.example.com/:_authToken=${RD_TOKEN}Without a dedicated host, use the path prefix on both lines:
ini
@acme:registry=https://releasedesk.example.com/npm/
//releasedesk.example.com/npm/:_authToken=${RD_TOKEN}| Client | Setup |
|---|---|
| npm, pnpm | The .npmrc above. |
| yarn berry | In .yarnrc.yml: npmScopes: { acme: { npmRegistryServer: "https://npm.example.com", npmAuthToken: "${RD_TOKEN}" } } |
| bun | The .npmrc above, or [install.scopes] in bunfig.toml: "@acme" = { url = "https://npm.example.com/", token = "$RD_TOKEN" } |
Check the setup with npm ping --registry <url> and npm whoami --registry <url>.
Publish
sh
npm publishSet "publishConfig": { "registry": "https://npm.example.com/" } in package.json, or pass --registry. Rules:
- Versions are immutable. Publishing an existing version returns 409, and an unpublished version number is reserved forever.
- A prerelease (
1.0.0-beta.1) must be published with--tag.latestis only set by a stable publish without--tag. - Integrity is computed server-side; the tarball's
package.jsonmust match the published name and version.
Dist-tags, deprecate, unpublish
sh
npm dist-tag add @acme/ui@1.2.0 next
npm dist-tag rm @acme/ui next # latest cannot be removed
npm deprecate @acme/ui@1.1.0 "use 1.2.0"
npm unpublish @acme/ui@1.1.0 # hidden, tarball returns 404Unpublishing a whole package is restricted to org admins. All publish and management actions are written to the audit log.
GitHub Actions
yaml
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://npm.example.com/
scope: "@acme"
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.RD_NPM_TOKEN }}Dashboard
Packages appear under Packages: versions (ordered by semver precedence), dist-tags, deprecation status, install snippet and visibility (private or public). Deprecate and unpublish are done from the CLI for now.
Not supported
Search, npm audit data, provenance, and proxying registry.npmjs.org.