# JavaScript

SlowShield serves the npm registry at `https://slowshield.example.com/npm/`. npm, pnpm, Yarn and Bun use it like registry.npmjs.org, and so does everything that installs through them, such as `npx`, MCP servers and CI steps.

## Set it up

npm and pnpm read `npm_config_registry`, so a line in your shell profile covers them in every new terminal (the Setup page has bash on macOS, zsh and fish too):

```
cat >> ~/.bashrc <<'EOF'
export PIP_INDEX_URL=https://slowshield.example.com/pypi/simple/
export UV_DEFAULT_INDEX=https://slowshield.example.com/pypi/simple/
export npm_config_registry=https://slowshield.example.com/npm/
export GOPROXY=https://slowshield.example.com/go
export PIP_UPLOADED_PRIOR_TO=P3D
export npm_config_min_release_age=3
EOF
source ~/.bashrc
```

Yarn 2 and later ignores `.npmrc` and the npm variables, and Bun's `bunfig.toml` loses to an `npm_config_registry` in the environment. Each tool, with the version its own release age needs:

### npm

Release age: npm 11.10 or later (11.0 to 11.9 warn about an unknown setting, 10 ignores it)

*command*

```
npm config set registry https://slowshield.example.com/npm/
npm config set min-release-age 3
```

*.npmrc (project or ~)*

```
registry=https://slowshield.example.com/npm/
min-release-age=3
```

### pnpm

Release age: pnpm 10.16 or later (older versions ignore it)

*command*

```
pnpm config set registry https://slowshield.example.com/npm/
```

*pnpm-workspace.yaml*

```
minimumReleaseAge: 4320  # 3 days, in minutes
```

### Yarn

Release age: Yarn 4.10 or later (older versions refuse to run with it)

*.yarnrc.yml (Yarn Berry)*

```
npmRegistryServer: "https://slowshield.example.com/npm/"
npmMinimalAgeGate: "3d"
```

### Bun

Release age: Bun 1.3 or later (older versions ignore it)

*bunfig.toml*

```
[install]
registry = "https://slowshield.example.com/npm/"
minimumReleaseAge = 259200  # 3 days, in seconds
```

## What changes

- **Versions wait.** A version becomes installable 7 days after the time the registry recorded for it. Versions younger than that are left out of the package documents (packuments), and `latest` moves to the newest stable version that is old enough. Other dist-tags that point at a held version are removed.

- **A pin that is too new.** `npm install pkg@1.2.3` for a held version reports that no matching version exists. A lockfile that names it downloads the tarball directly and gets `403` with `Retry-After` and the time it becomes available.

- **Malware** from the OSV and GitHub feeds is refused with `451` and the advisory.

- **Brand-new packages** none of whose versions is old enough are served and recorded as fail-open, unless your administrator set `fail_open = false`. That's what the second layer is for.

- **Lockfiles record SlowShield.** npm needs absolute tarball URLs, so SlowShield's point at itself (`https://slowshield.example.com/npm/pkg/-/pkg-1.2.3.tgz`) and `package-lock.json` records them. Integrity hashes don't change: the tarballs are npm's, byte for byte.

## The second layer

npm 11.10, pnpm 10.16, Yarn 4.10 and Bun 1.3 can refuse releases younger than a few days themselves (Deno 2.6 too). Set them to 3 days, below SlowShield's 7, and they stay silent on a normal day. Mind the units, checked on 2026-10-06: npm's `min-release-age` counts days, pnpm's `minimumReleaseAge` minutes and Bun's `minimumReleaseAge` seconds. npm 11.0 to 11.9 warn about the unknown setting, npm 10 ignores it, and Yarn before 4.10 refuses to run with it. To let one package through early: `min-release-age-exclude` in npm.

## In Docker and CI

A build container doesn't read your shell profile. Pass the registry as a build argument: an `ARG` is visible to `RUN` as an environment variable and isn't kept in the image.

```
FROM node:22-slim
ARG npm_config_registry=https://slowshield.example.com/npm/
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
```

Tarball URLs come from SlowShield's public URL, so the build must be able to reach SlowShield under that name. [Building images](https://slowshield.org/docs/container-builds/) covers the base image too, and how to check that nothing in a build goes around SlowShield. In CI, set the variables for the job:

```
# GitHub Actions (workflow or job)
env:
  PIP_INDEX_URL: https://slowshield.example.com/pypi/simple/
  UV_DEFAULT_INDEX: https://slowshield.example.com/pypi/simple/
  npm_config_registry: https://slowshield.example.com/npm/
  GOPROXY: https://slowshield.example.com/go
  PIP_UPLOADED_PRIOR_TO: P3D
  npm_config_min_release_age: 3
```

## Limits

- Dependencies from git or a tarball URL don't go through the registry, so SlowShield doesn't see them.

- Scoped registries (`@company:registry=…`) stay as they are: private packages keep coming from your private registry.

- Block registry.npmjs.org at the firewall for machines that should only use SlowShield.

---

This page as HTML: https://slowshield.org/docs/javascript/. All of the guide in one file: https://slowshield.org/llms-full.txt
