# npm / yarn / pnpm / bun

import { Tabs, TabItem, Aside } from '@astrojs/starlight/components';

Avrea caches packages from the public npm registry so
that `npm install`, `yarn install`, `pnpm install`, and `bun install` resolve
from local storage on repeat runs. The cache serves the npm registry protocol,
so it works with any package manager that installs from the npm registry, not
only the npm CLI.

## On Avrea runners

The registry URL is pre-configured:

```bash
npm_config_registry="https://cache.avrea.com:8443/npm/"
```

This applies to npm, pnpm, yarn classic (v1), and bun. Your workflow needs no changes:

<Aside type="note" title="Yarn Berry (v2+)">
The `npm_config_registry` environment variable works for npm, pnpm, yarn classic
(v1), and bun. Yarn Berry (v2+) ignores this variable and uses `.yarnrc.yml`
instead. Set `npmRegistryServer` in your `.yarnrc.yml`:

```yaml
npmRegistryServer: "https://cache.avrea.com:8443/npm/"
```
</Aside>

```yaml title="workflow.yml"
jobs:
  build:
    runs-on: avrea-ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: 'npm'
      - run: npm ci
```

## Manual setup

<Aside type="caution">
`cache.avrea.com` is reachable only from Avrea runners. Apply these settings on
Avrea runners that don't already pick up `npm_config_registry`; off-platform
environments should keep using the public npm registry.
</Aside>

<Tabs>
  <TabItem label="npm">
```bash
npm config set registry https://cache.avrea.com:8443/npm/
```
  </TabItem>
  <TabItem label="pnpm">
```bash
pnpm config set registry https://cache.avrea.com:8443/npm/
```
  </TabItem>
  <TabItem label="yarn">
```bash
yarn config set registry https://cache.avrea.com:8443/npm/
```
  </TabItem>
  <TabItem label="bun">
Bun respects the `npm_config_registry` environment variable, so no `bunfig.toml`
is needed on Avrea runners. To opt in explicitly from a Bun project:

```bash
npm_config_registry="https://cache.avrea.com:8443/npm/"
```

Or add to `bunfig.toml`:

```toml title="bunfig.toml"
[install]
registry = "https://cache.avrea.com:8443/npm/"
```
  </TabItem>
</Tabs>

## Private npm registries

Avrea caches private npm tarballs beside your runners for faster, lower-latency
downloads across builds. Your registry checks access on every request, including
cache hits. Private npm caching is opt-in.

### Enable private packages

Enable private npm caching for your organization or repository in the
[console](https://console.avrea.com), or via the CLI:

```bash
avr settings set cache.npm-private-registry.enabled true
```

**Package Manager Cache** must remain enabled. Repository settings override
organization settings.

### Configure the scope and token

Configure the private scope in the project's `.npmrc`. The token can come from
a GitHub Actions secret or an OIDC exchange with your registry:

```ini title=".npmrc"
@example:registry=https://cache.avrea.com:8443/npm-proxy/registry.example.com/
//cache.avrea.com:8443/npm-proxy/registry.example.com/:_authToken=${NODE_AUTH_TOKEN}
//registry.example.com/:_authToken=${NODE_AUTH_TOKEN}
```

Replace `@example` and `registry.example.com` with your package scope and upstream
registry. Keep the literal `${NODE_AUTH_TOKEN}` placeholder. For a stored token,
add a read-only token as the GitHub Actions secret `PRIVATE_NPM_TOKEN` and pass
it to the install step:

```yaml
- run: npm ci
  env:
    NODE_AUTH_TOKEN: ${{ secrets.PRIVATE_NPM_TOKEN }}
```

npm, pnpm, and bun read scoped registries and tokens from `.npmrc`, so the
same file works with `pnpm install --frozen-lockfile` and
`bun install --frozen-lockfile`. Yarn Berry (v2+) ignores `.npmrc`; configure
the scope in `.yarnrc.yml` instead:

```yaml title=".yarnrc.yml"
npmScopes:
  example:
    npmRegistryServer: "https://cache.avrea.com:8443/npm-proxy/registry.example.com/"
    npmAuthToken: "${NODE_AUTH_TOKEN}"
```

Only `@example` packages use this registry. Other packages keep using the public
npm cache. npm does not fall back between registries.

### Short-lived tokens with OIDC

Keep your registry's OIDC authentication step before the install step. GitHub
issues the workflow identity token; your registry exchanges it for a short-lived
registry access token. Pass that **registry access token** as `NODE_AUTH_TOKEN`.
Avrea forwards it to your registry for authorization, including on cache hits.

Configure your registry to trust the intended repository, workflow, and branch,
and grant the mapped service account read access to the packages. Login and token
exchange use the upstream service directly. If a login step writes credentials
only for the upstream URL, also configure the proxy token entry shown above.

The following example uses Cloudsmith’s private npm registry and its official
action. The same pattern applies to other providers that support OIDC: exchange
the workflow’s identity for a registry access token, then pass it to npm as
`NODE_AUTH_TOKEN`.

```yaml
permissions:
  contents: read
  id-token: write
steps:
  # After checkout and Node.js setup:
  - name: Authenticate to Cloudsmith
    uses: cloudsmith-io/cloudsmith-cli-action@69e169e2ad9bc9a1599c2dff464061a00fe9f8ba # v3.1.1
    with:
      cli-version: "1.27.0"
      oidc-namespace: YOUR_WORKSPACE
      oidc-service-slug: YOUR_READ_ONLY_SERVICE
      verify-auth: "true"
      export-auth-token: "true"
  - run: |
      export NODE_AUTH_TOKEN="$CLOUDSMITH_API_KEY"
      npm ci
```

For an upstream URL of `https://npm.cloudsmith.io/YOUR_WORKSPACE/YOUR_REPOSITORY/`,
use this `.npmrc`, replacing the scope and path placeholders:

```ini
@example:registry=https://cache.avrea.com:8443/npm-proxy/npm.cloudsmith.io/YOUR_WORKSPACE/YOUR_REPOSITORY/
//cache.avrea.com:8443/npm-proxy/npm.cloudsmith.io/YOUR_WORKSPACE/YOUR_REPOSITORY/:_authToken=${NODE_AUTH_TOKEN}
//npm.cloudsmith.io/YOUR_WORKSPACE/YOUR_REPOSITORY/:_authToken=${NODE_AUTH_TOKEN}
```

No stored registry secret is needed for this flow. See
[Cloudsmith's OIDC setup guide](https://docs.cloudsmith.com/authentication/setup-cloudsmith-to-authenticate-with-oidc-in-github-actions)
for the provider and service-account configuration. The token must remain valid
throughout the install; cached packages do not bypass token expiry.

### Registries with a port or base path

Preserve the upstream host, optional port, and base path after `/npm-proxy/`.
For `https://registry.example.com:9443/team/npm/`, use:

```ini
@example:registry=https://cache.avrea.com:8443/npm-proxy/registry.example.com:9443/team/npm/
//cache.avrea.com:8443/npm-proxy/registry.example.com:9443/team/npm/:_authToken=${NODE_AUTH_TOKEN}
//registry.example.com:9443/team/npm/:_authToken=${NODE_AUTH_TOKEN}
```

Match ports and paths exactly between the registry and token entries so npm
attaches credentials to tarball requests.

### Migrate an existing lockfile

The same lockfile requirements apply to stored tokens and OIDC tokens. Avrea
does not automatically rewrite the lockfile before `npm ci`.

<Aside type="caution" title="Check private lockfile URLs">
Changing registry configuration does not reroute private tarball URLs already
recorded in a lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or
`bun.lock`). Frozen installs continue fetching those URLs directly. The
upstream token line in the examples lets existing direct pins authenticate
during migration; it does not provide fallback from the proxy.
</Aside>

Regenerate the private packages' resolved URLs through the proxy on an Avrea
runner. Compare the resulting lockfile with the original: private `resolved`
URLs should use the intended `/npm-proxy/` prefix, while versions and integrity
hashes must stay unchanged. Validate with a frozen install (`npm ci`,
`pnpm install --frozen-lockfile`, `yarn install --immutable`, or
`bun install --frozen-lockfile`) using a fresh package manager cache.

Running `npm install --package-lock-only` against an existing lockfile may retain
its old URLs. Do not use `replace-registry-host=always`: it can send private
packages to the default public cache instead of their scope registry.

### Compatibility

- **Avrea runners only.** The proxy address is not reachable from developer
  machines or other CI providers. Keep direct registry configuration and
  upstream lockfile URLs for those environments.
- **HTTPS and bearer tokens.** The registry must be publicly reachable with a
  certificate trusted by Avrea. Tokens travel through Avrea over HTTPS and are
  not persisted in package cache storage or organization settings.
- **Same-origin tarballs.** Tarball URLs must contain `/-/` and end in `.tgz`.
  The initial tarball URL must use the registry host without a query string.
  Avrea follows up to three HTTPS redirects, including signed CDN download
  URLs used by registries such as Cloudsmith. Private-network registries are
  unsupported. Publishing and login use the upstream directly.
- **Cacheable responses.** Tarballs need a strong ETag and a known, nonempty size
  within the configured limit (128 MiB by default). Other eligible downloads
  stream without being stored.

Private tarballs count toward the **repository cache quota**. Different
repositories store separate copies.

### Troubleshooting and rollback

For **401 or 403**, check the token's read permission, workflow environment
binding, matching `.npmrc` URL prefixes, and the repository's cache settings.
If installs succeed but bypass Avrea, check private `resolved` URLs and retry
with a fresh npm cache.

To roll back, restore the direct scope registry, token entry, and upstream
lockfile URLs without changing versions or integrity. Verify a direct install
before disabling private npm caching. Proxy-pinned lockfiles fail when the
setting is disabled; they do not redirect upstream.