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 ~/.bashrcYarn 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=3pnpm
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 minutesYarn
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 secondsWhat 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
latestmoves 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.3for a held version reports that no matching version exists. A lockfile that names it downloads the tarball directly and gets403withRetry-Afterand the time it becomes available. - Malware from the OSV and GitHub feeds is refused with
451and 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) andpackage-lock.jsonrecords 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 ciTarball URLs come from SlowShield's public URL, so the build must be able to reach SlowShield under that name. Building images 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: 3Limits
- 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.
For agents: this page as Markdown · llms.txt · the SlowShield skill