# Aikido Safe Chain puts a malware filter in front of npm and PyPI

> Safe Chain is a tokenless npm package that installs shell aliases and a local proxy so every npm, pnpm, yarn, bun, pip, uv, poetry or pdm install is checked against Aikido Intel before the package lands on disk. It is a good fit for laptops and CI runners that install from public registries, and a poor fit for anyone who needs a private registry or offline builds to stay untouched.

**AikidoSec/safe-chain** — Protect against malicious code installed via npm, yarn, pnpm, npx, pnpx, pip, uv and poetry with Aikido Safe Chain. Free to use, no tokens required.

- Repository: https://github.com/AikidoSec/safe-chain
- Website: https://www.aikido.dev
- Stars: 1,824 · Forks: 118
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/aikidosec-safe-chain

## The problem it targets: typosquats and fresh malware in public registries

Package registries are the weakest link in most JavaScript and Python build pipelines, because a dependency name is a string that anyone can publish. Safe Chain exists to put a decision point between the registry and your disk. It checks each package against Aikido Intel, the vendor's open source threat intelligence feed, and refuses the download when a hit comes back.

The threat model is deliberately narrow. It is about malicious packages arriving through npm and PyPI, not about vulnerabilities in code you already depend on. The README also advertises a second control aimed at the window right after publication: by default a package version younger than 48 hours is treated as untrusted, which is the period when a maintainer account has been compromised and a poisoned release is pushed under an existing, trusted name.

The distribution pitch matters too. The project describes itself as tokenless and free, with no build data shared, and it is installed from a public npm package. That makes it realistic to drop into a company laptop image, which is where the interesting part of the design sits.

## A local proxy and shell aliases, not a patched package manager

The mechanism is a lightweight proxy process that sits between the package manager and the registry. The README describes it as intercepting package downloads from the npm registry and PyPI, verifying each package in real time against the intelligence feed, and blocking the download before malicious code reaches your machine, including for deep dependencies rather than only the package you named.

Getting traffic through that proxy does not require changing your package manager configuration. Installation writes shell aliases for npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip, pip3, poetry, uv, uvx, pipx and pdm. Any command you run after that resolves to the alias, and the alias routes through the proxy. For Python it also intercepts module invocations where pip is available, such as `python -m pip install ...` and `python3 -m pip download ...`.

That alias approach is the main trade-off in the design. It covers interactive shells that load the profile, and it covers the sixteen listed binaries, but it depends on the shell session having picked up the new aliases. The README is explicit that a terminal restart is required, and that without it the aliases are simply not available, which means an unchanged long-lived shell keeps running the unprotected binary.

## Minimum package age is enforced in two different places per ecosystem

The 48 hour window is not implemented once. For npm based managers, Safe Chain suppresses versions newer than the configured minimum age from the registry metadata returned during normal package resolution. For a direct package download that bypasses that metadata flow, it falls back to a cached list of newly released packages and blocks the request at the proxy.

Python works the same shape of logic with different wording: too-young files and releases are suppressed from PyPI metadata responses, and direct download requests are blocked using the cached list. The two-layer design is the interesting part. Suppressing metadata means a normal `npm install` simply never sees the young version, so it can fall back to an older one without an error. Blocking the direct request covers the case where a user pins an exact fresh version by hand.

The README states the threshold is configurable and can be bypassed entirely, and that for urgent CVE fixes Aikido confirms the patch release is clean and Safe Chain exempts it from the age check. It does not document the configuration key, the environment variable, or the file that holds the minimum age setting in the README, so treat the exact knob as something you have to look up in the repository docs rather than assume.

## Installing on Unix and Windows with a pinned checksum

The installer is a shell script fetched from a versioned GitHub release URL, and the README pairs the download with an inline SHA-256 verification step. Pinning the version is your job: the command below targets 1.5.25, and a different release means a different hash.

```shell
curl -fsSL https://github.com/AikidoSec/safe-chain/releases/download/1.5.25/install-safe-chain.sh -o /tmp/install-safe-chain.sh \
  && echo "83069f6db4215cccf6a26bdd6e65a5c2366b9d24322b2d5d2862d47385e56682  /tmp/install-safe-chain.sh" | sha256sum -c - \
  && sh /tmp/install-safe-chain.sh \
  && rm /tmp/install-safe-chain.sh
```

The PowerShell path does the same thing and throws instead of executing the file when the hash does not match:

```powershell
$installer = Join-Path $env:TEMP "install-safe-chain.ps1"
$expectedHash = "9F813B5ADCA1574ED19F51E9B33C6BC0E2E492E0E85168131E180084F55375FF"
```

The reasoning behind versioned URLs is stated in the README: GitHub releases are immutable, so an artifact published at a versioned URL cannot be replaced, and the file you checksum is exactly what was released.

After installation, restart the terminal and confirm the aliases resolve:

```shell
npm safe-chain-verify
pnpm safe-chain-verify
pip safe-chain-verify
uv safe-chain-verify
```

Each one should print `OK: Safe-chain works!`. To see the blocking path actually fire, install the published test packages, which the README says are flagged as malware:

```shell
npm install safe-chain-test
pip3 install safe-chain-pi-test
```

Finally, `safe-chain --version` reports the installed version independently of the wrapper.

## Where the alias model leaks, and what it does not cover

The gaps are structural rather than bugs. Anything that does not go through an aliased binary is unprotected: a direct call to the absolute path of the npm CLI, a container image built with the base node image rather than the install script, or a CI step that resets the shell profile between steps. The README does not document a systemd unit, a CI-specific configuration, or a non-shell integration, so if your build system invokes package managers in an environment with no login shell, you need to verify the aliases are present there yourself.

There is also a scope limit that the README is upfront about. Safe Chain covers npm and PyPI. Everything else in the ecosystem story, including VS Code, Open VSX, Maven, NuGet, Chrome extensions, Go, Ruby, Rust and AI skills, belongs to the Aikido Device Protection product that is built on top of Safe Chain, with centralized policy and approval workflows. Choosing Safe Chain means accepting the npm and PyPI boundary.

The last limitation is dependency on a network service. Blocking decisions depend on the Aikido Intel feed and on the cached list of newly released packages. The README does not describe an offline mode, a local feed mirror, or behaviour when the feed is unreachable, so an air-gapped environment has no documented answer.

## How this differs from lockfiles, audit tools and allowlists

The usual comparison points are worth separating, because they operate at a different layer.

A lockfile pins exact resolved versions and integrity hashes, which makes installs reproducible and gives you a reviewable diff, but it does not tell you whether the pinned tarball is malware. A vulnerability scanner such as an audit command reports known advisories against code you already have, which is a different question from whether a newly published package is hostile. A private proxy or artifact mirror centralises egress control, and it can enforce allowlists, but it treats every proxied request the same way rather than consulting a malware intelligence feed per package.

Safe Chain's specific claim is the combination: it is per-machine and tokenless, it consults a threat feed at download time, it reaches deep dependencies rather than only direct ones, and it applies a publication age floor. The cost of that design is that the decision happens at the developer workstation and in CI rather than at a single governed network boundary, and the breadth of package manager support is broader than the set of registries actually inspected.

## Repository layout, release cadence and the licence question

The repository is a JavaScript workspace. The root package.json is private, declares `packages/*` and `test/e2e` as workspaces, and exposes `test`, `test:e2e`, `lint` and `typecheck` scripts that delegate into `packages/safe-chain`. Build tooling is pinned in devDependencies: `oxlint` for linting, `esbuild` for bundling and `@yao-pkg/pkg` at 6.10.1 for packaging. Two dependency overrides are declared for `@isaacs/brace-expansion` and `minimatch`, both floor-pinned to fixed versions, which is the kind of thing you see in a repository that ships an executable to end users.

Top-level entries include `build.js`, `install-scripts/`, `docs/`, `packages/`, `test/`, an `npm-shrinkwrap.json` and a LICENSE file.

Maintenance is current: the last push was on 2026-10-14, and releases 1.5.23, 1.5.24 and 1.5.25 landed on 2026-09-29, 2026-10-01 and 2026-10-09. The repository is not archived. That cadence also means the checksum in any install command you copy becomes stale quickly.

On licensing, be careful. GitHub classifies the licence as Other, a custom type it cannot map, while the root package.json declares `AGPL-3.0-or-later`. Read the LICENSE file yourself before shipping this inside a closed-source product; the mismatch is a question for the vendor, not something to resolve by guessing.

## Conclusion

Adopt Safe Chain on developer machines and CI runners that pull from the public npm registry or PyPI and want a second opinion before a package executes, and skip it if your pipeline resolves everything through a private proxy or vendored tarballs, since the interception layer never sees those. Before switching, check the LICENSE file yourself, because the repository declares AGPL-3.0-or-later in package.json while GitHub classifies the licence as a custom one, and confirm on your own machines that `safe-chain --version` reports the version you pinned, because the install script is fetched from a release URL that changes on every upgrade.

## FAQ

### Which package managers does Aikido Safe Chain support?

It installs aliases for npm, npx, yarn, pnpm, pnpx, rush, rushx, bun, bunx, pip, pip3, uv, poetry, uvx, pipx and pdm, and it also intercepts Python module invocations such as `python -m pip install ...`. Malware detection itself is scoped to the npm registry and PyPI.

### Does Aikido Safe Chain need an API token or account?

The README describes it as tokenless and free, with no build data shared, and verification is done through a `{packagemanager} safe-chain-verify` command that should print `OK: Safe-chain works!`. Broader ecosystems and centralized policy live in the separate Aikido Device Protection product.

### What happens when Safe Chain blocks a package, and can I bypass the minimum package age?

If malware is detected in any package, the proxy blocks the download and prompts you to exit the command. The minimum package age defaults to 48 hours and the README states the threshold can be configured or bypassed entirely, and that Aikido confirmed clean patch releases are exempted from the age check.

### Why is a terminal restart required after installing Aikido Safe Chain?

Installation writes shell aliases for the supported package managers, and those aliases only load in a newly started shell. Without a restart, commands keep resolving to the original binaries and no traffic reaches the local proxy.

### Can I install a specific version of Aikido Safe Chain?

Yes. The install commands reference a versioned release URL, and the README says to replace the version number with the one you want and to find all versions on the releases page. Changing the version also changes the SHA-256 hash you must verify.

## Sources

- [AikidoSec/safe-chain on GitHub](https://github.com/AikidoSec/safe-chain)
- [Issues](https://github.com/AikidoSec/safe-chain/issues)
- [Project website](https://www.aikido.dev)
- [README](https://github.com/AikidoSec/safe-chain/blob/main/README.md)
- [Releases](https://github.com/AikidoSec/safe-chain/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aikidosec-safe-chain
