# npm Repositories

> Use a CloudRepo npm repository: the registry URL, npm login and repository tokens in .npmrc, publishing, scoped packages, unpublishing and the 409 Conflict.

Use the npm CLI with a CloudRepo npm repository: publish your packages to it and install them from it.

## Before you start

- Node.js and npm. Use npm 10.9.4 or later.
- A CloudRepo npm repository. If you have none, see [Creating a Repository](/docs/manage/repositories.html#creating-a-repository).
- A way to authenticate: a repository token (see [Repository Tokens: Create One and Authenticate](/docs/authenticate/repository-tokens.html)), or an account that can sign in to the [admin portal](https://admin.cloudrepo.io) to run `npm login`. npm sends the token alone, as `_authToken` in `.npmrc`. There is no username.

## Connect npm

**npm**

**1. Put the registry and the credential in `~/.npmrc`.** npm replaces `${CLOUDREPO_TOKEN}` with the environment variable of that name, so the file holds no token and can be copied or committed:

`~/.npmrc`

```ini
registry=https://your-org.mycloudrepo.io/repositories/your-repo/
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}
```

Keep the trailing `/` on both lines, and keep the second line identical to the registry URL without `https:`. npm sends the token only to the registry that prefix names: if the two differ, even by the slash, npm sends no token at all. npm sends the token on every request to that registry, installs included, so no `always-auth` setting is needed (npm 11 warns that `always-auth` is an unknown option).

**2. Check the connection.**

**Terminal**

```bash
npm whoami
npm ping
```

Expected: `npm whoami` prints your email address, and `npm ping` ends with `PONG`.

**3. Publish.** Run this in the directory of your package:

**Terminal**

```bash
npm publish
```

A package may be scoped (`@scope/name`) or unscoped.

**4. Install it from another project.** Because the registry is in `~/.npmrc`, any project on your machine reads from CloudRepo:

**Terminal**

```bash
mkdir consumer && cd consumer
npm init -y
npm install docs-npm
```

**Alternative: sign in with `npm login`.** For a person at a keyboard, `npm login` opens the admin portal in your browser. Set the registry first, so npm knows which CloudRepo registry to sign in to:

**Terminal**

```bash
npm config set registry https://your-org.mycloudrepo.io/repositories/your-repo/
```

**Terminal**

```bash
npm login
```

npm opens `https://admin.cloudrepo.io/npm-login?session=<id>`. Sign in with your CloudRepo email and password (or reuse a portal session), authorize on the **Authorize npm CLI** page, and npm writes the token to `~/.npmrc` for you. That token is bound to the repository you ran `npm login` against, and only if it is a local npm repository: a proxy npm repository refuses it with `403` (`npm_token_not_supported_on_proxy`), even though the login itself succeeds. Use a token from **Repository Tokens** there. The token is read-only unless you choose **Read-write** on that page and your user has write access, so `npm publish` answers `403` with a read-only one.

If the terminal never returns after you authorize, the session expired: it lasts five minutes. Run `npm login` again.

**Alternative: Basic authentication.** Some older CI tooling needs `_auth`. Its value is the base64 of your email address, a colon and the token. Build it from the variable:

**Terminal**

```bash
printf '%s' "you@example.com:$CLOUDREPO_TOKEN" | base64
```

`~/.npmrc`

```ini
registry=https://your-org.mycloudrepo.io/repositories/your-repo/
//your-org.mycloudrepo.io/repositories/your-repo/:_auth=BASE64_ENCODED_CREDENTIALS
```

Prefer `_authToken` above: it needs no encoding step.

The legacy `npm login --auth-type=legacy` flag is no longer supported. CloudRepo answers it with `410 Gone` and points you at the web login or a repository token. Modern npm rejects an email address as a username, and CloudRepo’s users are identified by email address.

## Scoped packages and several registries

To install private packages from CloudRepo and everything else from the default registry, route a scope to CloudRepo in `.npmrc`:

`.npmrc`

```ini
@yourorg:registry=https://your-org.mycloudrepo.io/repositories/your-repo/
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}
```

This sends `@yourorg/*` packages to CloudRepo and every other package to npm’s default registry. CloudRepo accepts both scoped (`@scope/name`) and unscoped packages.

To publish a scoped package, create it with the scope and publish as usual:

**Terminal**

```bash
npm init --scope=@yourorg -y
npm publish
```

If `npm install @scope/package` answers `404`, npm is asking the default registry for that scope. Add the scope line above.

## Package size limit

CloudRepo accepts an npm publish of up to 210 MiB (220,200,960 bytes), which holds a tarball of about 155 MiB (base64 encoding adds about a third). A larger publish is refused with `413`. If you need to publish larger packages, split them into several.

## Unpublish a package

Remove one version, or every version of a package. CloudRepo lets a write-capable credential unpublish any version at any time, and Overwrite Protection does not block it.

**Terminal**

```bash
npm unpublish my-package@1.0.0
npm unpublish my-package --force
```

> **Removing every version cannot be undone:** Prefer removing one version. Removing all versions is irreversible, and it may break builds that depend on any version of the package.

### Remove a package through the REST API

For scripts, the registry’s own paths remove a package too. Send the same credential as npm: put it in `~/.netrc` and ask curl to read it (see [Maven Repositories](/docs/formats/maven.html) for the file). First read the current revision from the package metadata, then delete with it.

**Terminal**

```bash
# Remove one version: read the current revision, then delete the tarball with it
REVISION=$(curl --netrc --silent \
  https://your-org.mycloudrepo.io/repositories/your-repo/my-package/ \
  | jq -r '._rev')
curl --netrc --request DELETE \
  "https://your-org.mycloudrepo.io/repositories/your-repo/my-package/-/my-package-1.0.0.tgz/-rev/$REVISION"
```

**Terminal**

```bash
# Remove the whole package (all versions)
REVISION=$(curl --netrc --silent \
  https://your-org.mycloudrepo.io/repositories/your-repo/my-package/ \
  | jq -r '._rev')
curl --netrc --request DELETE \
  "https://your-org.mycloudrepo.io/repositories/your-repo/my-package/-rev/$REVISION"
```

## Token management

npm reads two kinds of CloudRepo token. The one the `npm login` web flow writes into your `.npmrc` has the npm format (`crn_v1_`) and works on local npm repositories only. One you create under **Repository Tokens** (`crp_v1_`) works on every repository type it reaches. Each is revocable from the admin portal without affecting your login or your other credentials. See [Repository Tokens: Create One and Authenticate](/docs/authenticate/repository-tokens.html).

`npm token create` is not supported against CloudRepo: it answers `410 Gone` and points you at the portal. `npm token list` shows the tokens `npm login` created for you, and `npm token revoke <id>` revokes one. A token from **Repository Tokens** appears and is revoked only in the admin portal.

After `npm login`, npm stores the token in your `.npmrc`:

`~/.npmrc`

```ini
//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=crn_v1_...
```

npm sends it as a `Bearer` header on every later request to the registry.

### In CI

Keep the token in your CI platform’s secret store, expose it as the environment variable `CLOUDREPO_TOKEN`, and let `.npmrc` name the variable, as in the first step. Then the build runs plain npm:

**Terminal**

```bash
npm install
npm publish
```

To write the `.npmrc` at build time instead:

**Terminal**

```bash
echo '//your-org.mycloudrepo.io/repositories/your-repo/:_authToken=${CLOUDREPO_TOKEN}' >> .npmrc
```

The single quotes matter: they write the text `${CLOUDREPO_TOKEN}` into the file, and npm replaces it with the variable’s value when it runs, so the file never holds the token.

> **Never commit a token:** Use your CI platform’s secret management (GitHub Actions secrets, GitLab CI variables, Jenkins credentials) to supply the token at build time, and commit only the `.npmrc` that names the variable.

## Troubleshooting

### 401, 403 or 404 on `npm install`

npm sends `_authToken` as a Bearer token. A token that is mistyped, expired, revoked or from another organization answers `404 Not Found`, not `401`, so npm reports the package as not found. If npm reports `404` for a package you know exists, check the token first. Check, in order:

- The `//your-org.mycloudrepo.io/repositories/your-repo/:` prefix on the `_authToken` line matches your `registry=` line exactly, without `https:` and with the trailing slash.
- The token reaches this repository. A token from `npm login` (`crn_v1_`) works only on the local npm repository you logged in to, and a proxy npm repository refuses it with `403`: use a token from **Repository Tokens** for a proxy repository.
- If `npm publish` answers `403`: a token from `npm login` is read-only unless you chose **Read-write** when you authorized it and your user has write access.
- The token has not expired or been revoked: check the **Repository Tokens** page.

### The registry URL has no trailing slash

When the registry URL or the token prefix lacks the trailing slash, they no longer match, so npm sends no token and commands fail as if you had not signed in. End both with `/`:

**Terminal**

```bash
# Right
npm config set registry https://your-org.mycloudrepo.io/repositories/your-repo/


# Wrong: the prefix on the token line no longer matches
npm config set registry https://your-org.mycloudrepo.io/repositories/your-repo
```

### `npm publish` fails with “Version already exists”

`npm publish` answers `409 Conflict` when a package with that version already exists and [Overwrite Protection](/docs/manage/repositories.html#overwrite-protection) is on, which is the default on every npm repository. npm prints CloudRepo’s explanation: `Version already exists. Overwrites are disabled for this repository.`

Increment the version in `package.json` before publishing. If the repository is meant to accept republished versions, turn Overwrite Protection off in its settings instead. npm has no mutable-version convention, so there is no equivalent of Maven’s `-SNAPSHOT` escape hatch here: no version is exempt.

## Next steps

- [Repository Management](/docs/manage/repositories.html): repository configuration options.
- [Continuous Integration and Deployment](/docs/integrations/cicd/overview.html): CI/CD pipeline integration.

---

The page: https://www.cloudrepo.io/docs/formats/npm.html
