Use this as a baseline across all Macs to reduce exposure to fast-moving supply-chain attacks, where a malicious package version is published, installed by early updaters, then removed hours later.
The default policy I would use is:
- Personal/dev machines: 3 days.
- CI and production lock refresh jobs: 3 to 7 days.
- Emergency security fixes: bypass deliberately, one package at a time, with a reviewed lockfile diff.
This does not replace lockfiles, checksums, audit tooling, or script restrictions. It just adds a time buffer before newly published versions become installable.
| Ecosystem | Native minimum-age gate? | Where to set it | Unit |
|---|---|---|---|
| npm | Yes | .npmrc |
days |
| pnpm | Yes | pnpm-workspace.yaml or global pnpm config |
minutes |
| Yarn Berry | Yes | .yarnrc.yml |
duration, for current Yarn |
| Deno | Yes, unstable | deno.json or CLI flag |
minutes, ISO-8601 duration, or RFC3339 timestamp |
| Bun | Yes | bunfig.toml or CLI flag |
seconds |
| Python / PyPI via pip | No native pip gate | Use uv, a mirror/proxy, or generated constraints |
uv supports timestamps/durations |
| Homebrew | No native age gate | Use pins, delayed brew update, or your own tap |
n/a |
| Rust / Cargo | No native age gate | Use Cargo.lock, vendoring, or alternate registry/mirror |
n/a |
Use a 3-day gate unless a project has a specific reason not to.
Equivalent values:
- npm:
3 - pnpm:
4320 - Yarn:
"3d" - Deno:
"P3D"or4320 - Bun:
259200 - uv:
"3 days"
Requires npm 11.10.0 or newer.
In a repo .npmrc:
min-release-age=3This tells npm to resolve only versions that were available more than the configured number of days ago. npm's docs also note that this cannot be combined with before.
For "all my Macs", set it in the user-level npm config:
npm config set min-release-age 3 --location=userStill commit project lockfiles and use:
npm ciin CI instead of casual npm install.
Requires pnpm 10.16.0 or newer. pnpm 11 defaults minimumReleaseAge to 1440 minutes, but the built-in default is non-strict for compatibility. If you want enforcement, set it explicitly.
In pnpm-workspace.yaml:
minimumReleaseAge: 4320
minimumReleaseAgeStrict: true
minimumReleaseAgeIgnoreMissingTime: falseOptional allowlist:
minimumReleaseAgeExclude:
- "@your-org/*"
- "typescript"For all Macs, use pnpm's global config file (~/.config/pnpm/config.yaml):
minimumReleaseAge: 4320
minimumReleaseAgeStrict: true
minimumReleaseAgeIgnoreMissingTime: falseUse modern Yarn, not Yarn Classic. Current Yarn docs expose npmMinimalAgeGate in .yarnrc.yml.
In .yarnrc.yml:
npmMinimalAgeGate: "3d"Optional allowlist:
npmPreapprovedPackages:
- "@your-org/*"
- "typescript"Review note: the docs confirm npmMinimalAgeGate; Yarn's security page says Yarn 4.12 introduced it, while release notes around 4.11 mention the duration-setting work. To avoid version-edge weirdness, standardize on Yarn 4.12 or newer across machines.
Also consider:
enableHardenedMode: truefor CI or outside-contributor PRs, since it validates lockfile resolution against the registry.
Deno supports a minimum dependency age, but the schema currently labels it unstable.
In deno.json:
{
"minimumDependencyAge": "P3D",
"lock": { "frozen": true }
}You can also use the object form to exclude specific npm or JSR packages:
{
"minimumDependencyAge": {
"age": "P3D",
"exclude": ["npm:@your-org/pkg", "jsr:@your-scope/pkg"]
}
}CLI equivalent:
deno update --minimum-dependency-age P3DDeno accepts an age in minutes, an ISO-8601 duration such as P3D, or an RFC3339 absolute timestamp.
In bunfig.toml:
[install]
minimumReleaseAge = 259200 # seconds, 3 days
minimumReleaseAgeExcludes = ["@your-org/pkg", "typescript"]For all Macs, put the same config in:
~/.bunfig.toml
or:
$XDG_CONFIG_HOME/.bunfig.toml
CLI one-off:
bun add some-package --minimum-release-age 259200Bun applies this during new resolution; existing versions already locked in bun.lock are not changed just because the gate is enabled.
pip itself does not currently have a native "minimum release age" setting comparable to npm/pnpm/Yarn/Bun.
Best practical option if you can use uv:
[tool.uv]
exclude-newer = "3 days"For uv's pip-compatible interface:
[tool.uv.pip]
exclude-newer = "2026-05-11T00:00:00Z"Important difference: uv compares against the upload time of each distribution artifact, not just the release version. For normal tool.uv, durations such as "3 days" are accepted. For tool.uv.pip, use a full timestamp for consistent behavior across time zones.
If staying with pip, use one of these instead:
- Generate and commit pinned constraints with hashes:
pip-compile --generate-hashes
pip install --require-hashes -r requirements.txt- Use a private PyPI mirror/proxy that only syncs releases after a delay.
- Run scheduled dependency updates instead of ad-hoc installs.
Homebrew does not have a native minimum-age gate for formulae or casks.
Use these softer controls:
brew pin <formula>
brew unpin <formula>Delay metadata refresh on machines where you want upgrades to happen only during a reviewed maintenance window:
export HOMEBREW_NO_AUTO_UPDATE=1Then run upgrades intentionally:
brew update
brew outdated
brew upgradeFor long-term control, maintain your own tap or extract specific versions, but that makes you responsible for security updates and formula maintenance.
Practical Mac policy: do not run blind brew upgrade during critical production/show windows. Run a weekly reviewed Homebrew maintenance pass instead.
Cargo does not have a native crates.io minimum-age gate.
Use the standard safety controls:
cargo update
cargo build --locked
cargo test --lockedFor applications, commit Cargo.lock. In CI, use:
cargo build --locked
cargo test --lockedFor stronger control:
cargo vendorand configure vendored sources in .cargo/config.toml, or route Cargo through an internal registry/mirror. Cargo supports alternate registries and source replacement, which is the right place to enforce an organizational age policy if you need one.
Use dotfiles or your machine bootstrap script. I would standardize these user-level files:
~/.npmrc
~/.config/pnpm/config.yaml
~/.yarnrc.yml
~/.bunfig.toml
~/.config/uv/uv.toml
~/.cargo/config.toml
~/.zshrc
Example snippets:
# ~/.npmrc
min-release-age=3# ~/.config/pnpm/config.yaml
minimumReleaseAge: 4320
minimumReleaseAgeStrict: true
minimumReleaseAgeIgnoreMissingTime: false# ~/.yarnrc.yml
npmMinimalAgeGate: "3d"# ~/.bunfig.toml
[install]
minimumReleaseAge = 259200# ~/.config/uv/uv.toml
exclude-newer = "3 days"# ~/.cargo/config.toml
# No native minimum-age gate. Put registry/mirror/source-replacement policy here if you run one.# ~/.zshrc
export HOMEBREW_NO_AUTO_UPDATE=1Run this on each Mac:
npm --version
pnpm --version
yarn --version
deno --version
bun --version
uv --version
cargo --version
brew --versionThen inspect effective config:
npm config get min-release-age
pnpm config get minimumReleaseAge
yarn config get npmMinimalAgeGateFor Bun, Deno, uv, Homebrew, and Cargo, verify by reading the config files and by doing dependency refreshes in a test repo before relying on the policy.
- npm config docs: https://docs.npmjs.com/cli/v11/using-npm/config/
- npm changelog: https://docs.npmjs.com/cli/v11/using-npm/changelog/
- pnpm settings: https://pnpm.io/settings#minimumreleaseage
- Yarn settings: https://yarnpkg.com/configuration/yarnrc#npmMinimalAgeGate
- Yarn security: https://yarnpkg.com/features/security
- Deno update CLI docs: https://docs.deno.com/runtime/reference/cli/update/
- Deno config schema: https://raw.githubusercontent.com/denoland/deno/v2.7.14/cli/schemas/config-file.v1.json
- Bun install docs: https://bun.sh/docs/pm/cli/install#minimum-release-age
- uv settings: https://docs.astral.sh/uv/reference/settings/#exclude-newer
- pip install docs: https://pip.pypa.io/en/stable/cli/pip_install/
- Homebrew versions/pinning docs: https://docs.brew.sh/Versions
- Cargo registries docs: https://doc.rust-lang.org/cargo/reference/registries.html