1 unstable release

0.1.0 Apr 4, 2026

#1083 in Filesystem

Apache-2.0

87KB
2K SLoC

rusk logo

rusk

A package manager that actually checks what it installs.

rusk demo

rusk handles both JavaScript and Python packages from a single tool, while verifying every artifact before it touches your project. It's fast, it's strict about security by default, and it doesn't make you choose between safety and speed.

Works with existing projects. No config file needed.

rusk auto-detects package.json, requirements.txt, and pyproject.toml. Drop it into any project you already have.

cd my-express-app/       # has package.json
rusk install             # just works

cd my-flask-app/         # has requirements.txt
rusk install             # just works

cd my-modern-lib/        # has pyproject.toml
rusk install             # just works

Install, verify, and understand in three commands

$ rusk install
Installed 70 packages (70 cached) in 1.0s

$ rusk audit --strict
Verified 70/70 packages: 70 passed, 0 failed

$ rusk explain express --trace
Package: express@4.21.2
Digest: sha256:7b75c105719...
Policy evaluation:
  + npm ECDSA signature verified
  + Package has valid digest
Verdict: ALLOW - package is trusted

Works on existing JS and Python projects without migration.

Why rusk?

Most package managers trust the registry response and artifact served at install time. rusk doesn't. Every package goes through:

  • SHA-256 digest verification before anything gets written to disk
  • Content-addressed storage so the same bytes always produce the same hash
  • Lockfile pinning of the entire transitive closure with digests
  • Signature and provenance policy that you control
  • Tamper detection that catches corrupted or modified packages

Faster than npm, competitive with bun and uv — while doing more work on every install.

Speed

Benchmarked against real package managers on express@^4.21.0 (70 transitive dependencies):

JavaScript (vs bun and npm)

Scenario rusk bun npm
Cold install 5.1s 2.7s 6.3s
Warm cache 1.0s 1.9s 4.7s
No-op 0.17s 1.7s 4.8s

Python (vs uv)

Scenario rusk uv
Cold install 1.4s 0.2s
Warm cache 0.14s 0.27s
No-op 0.20s 0.27s

rusk wins where developers feel package managers most often: warm installs and no-op runs. And it's doing more work — verifying digests, checking CAS integrity, computing lockfile hashes — on every single run.

The cold install gap is network optimization. bun and uv have had years to tune their HTTP stacks. rusk's cold path will get faster.

Quick start

# Download the binary from GitHub releases (Linux/macOS/Windows, x86_64/aarch64)
curl -fsSL https://github.com/harishsg993010/rusk/releases/latest/download/rusk-$(uname -s)-$(uname -m) -o rusk
chmod +x rusk && sudo mv rusk /usr/local/bin/

# Or install via cargo
cargo install rusk-cli

# Or as a cargo subcommand
cargo install rusk-cli    # then use: cargo rusk install

# Or build from source (requires Rust 1.75+)
git clone https://github.com/harishsg993010/rusk.git && cd rusk
cargo build --release -p rusk-cli

Start a new project

rusk init --ecosystem js --name my-app
rusk add express@^4.21.0
rusk add cors@^2.8.5

rusk init --ecosystem python --name my-lib
rusk add "flask>=3.0.0"
rusk add "requests>=2.28.0"

Add packages

# npm style
rusk add lodash@^4.17.21
rusk add vitest@^1.0.0 -D          # dev dependency

# pip style
rusk add "requests>=2.28.0"
rusk add "flask>=3.0.0"

# Works with any manifest format
rusk add express                     # adds to package.json
rusk add "six>=1.16.0"              # adds to requirements.txt

How it works

rusk resolves the entire transitive dependency tree, downloads every artifact, verifies its SHA-256 digest, stores it in a content-addressed cache, and extracts it into the right place. The lockfile pins every package with its exact digest.

$ rusk install
Installed 70 packages (70 downloaded) in 5.1s
  Materialized JS packages to node_modules/

Second install? Already cached:

$ rm -rf node_modules
$ rusk install
Installed 70 packages (70 cached) in 1.0s

Nothing changed since last time? Instant:

$ rusk install
Already up to date. (0.17s)

JS packages go to node_modules/. Python wheels go to .venv/lib/site-packages/. Both ecosystems share the same CAS, lockfile, and security pipeline.

Supported manifest formats

Format Ecosystem Auto-detected
rusk.toml JS + Python Yes (primary)
package.json JS Yes
pyproject.toml Python (PEP 621 + Poetry) Yes
requirements.txt Python Yes

Security features

This isn't a checkbox exercise. These are things that actually protect you.

Digest verification on every install

Every artifact is hashed with SHA-256 during download. If the bytes don't match what the lockfile says, the install stops. No exceptions.

$ rusk verify
  OK  ms@2.1.3 (sha256:a101155c3cbdfb1e...)
  OK  express@4.21.2 (sha256:7b75c105719...)
Verified 70/70 packages: 70 passed, 0 failed

Tamper detection

Corrupt a package in the cache? rusk catches it:

$ echo "CORRUPTED" > .rusk/cas/a1/a101155c...
$ rusk install
error: CAS integrity failed for ms@2.1.3: digest mismatch
  (expected sha256:a101155c..., got sha256:3398b5c2...)

It reads the blob, recomputes the hash, and compares. Can't sneak corrupted data through.

Lockfile integrity

Modify a digest in rusk.lock? Caught immediately:

$ rusk verify
  FAIL  ms@2.1.3: not found in CAS
  (digest: sha256:000000000000000000000000000000000...)

Signature policy

You decide what level of trust you need:

[trust]
require_signatures = true
require_provenance = false
$ rusk audit --strict
[WARN] ms@2.1.3: package is not signed
[WARN] express@4.21.2: package is not signed
error: audit found 2 issues

Strict mode exits with a distinct exit code, so you can gate CI on it. See CI integration for all exit codes.

Trust explanation

Why was a package allowed or blocked? Ask:

$ rusk explain ms --trace
Package: ms@2.1.3
Ecosystem: js
Digest: sha256:a101155c3cbdfb1e...

Policy evaluation:
  - Signatures not required by policy
  + Package has valid digest

Verdict: ALLOW - package is trusted

Full evaluation trace:
  1. Load trust config from rusk.toml
  2. Look up ms@2.1.3 in lockfile
  3. Check signature requirement: not required
  4. Check provenance requirement: not required
  5. Check digest integrity: OK
  6. Final verdict: ALLOW

All commands

Command What it does
rusk install Resolve, download, verify, and install packages
rusk add <pkg> Add a package to the manifest and install it
rusk remove <pkg> Remove a package from manifest, lockfile, and disk
rusk update Re-resolve and update the lockfile
rusk lock Resolve and write lockfile without installing
rusk sync Install from lockfile and remove extraneous packages
rusk run <cmd> Run a command with ecosystem env vars (NODE_PATH/PYTHONPATH)
rusk list List installed packages with versions
rusk tree Display the dependency tree
rusk verify Check installed packages match lockfile digests
rusk audit Evaluate trust policy + check security advisories
rusk explain <pkg> Show why a package was allowed or blocked
rusk init Create a new project with rusk.toml
rusk migrate Import from package-lock.json / yarn.lock / pnpm-lock.yaml
rusk patch <pkg> Copy a package for editing, then commit as a patch
rusk link Register or consume local package symlinks
rusk venv Create a Python virtual environment
rusk gc Clean up unreferenced blobs from the cache
rusk config View or modify rusk configuration
rusk build Run build scripts in a sandbox
rusk publish Validate and publish a package
rusk python list Discover Python installations on PATH
rusk python find Find a Python matching a version
rusk python pin Write .python-version file
rusk tool run Run a Python CLI tool in an isolated venv
rusk tool install Persistently install a CLI tool
rusk x <pkg> Shorthand for rusk tool run (like uvx)

CI integration

rusk is built for CI pipelines. Every command returns a structured exit code and supports JSON output.

Exit codes

Code Name Meaning
0 success Everything passed
10 resolution_failed Dependency resolution error
11 download_failed Artifact download error
20 policy_denied Trust policy blocked the install
21 signature_missing Required signature not found
22 provenance_dropped Package lost its attestation
23 revocation_hit Package or signer was revoked
30 cas_corruption Content-addressed store integrity failure
31 lockfile_mismatch Lockfile doesn't match manifest
40 materialization_failed Failed to extract/link packages
50 manifest_error Invalid manifest file
70 audit_failed Audit found policy violations
71 verification_failed Installed packages don't match lockfile

Run rusk --exit-codes to print the full table. Use rusk --exit-codes --format json for machine-readable output.

JSON output

Every command supports --format json for structured output:

$ rusk install --format json
{"status":"success","exit_code":0,"resolved":70,"downloaded":0,"cached":70,"materialized":0,"elapsed_ms":170}

$ rusk audit --strict --format json
{"status":"error","exit_code":70,"total":70,"issues":[{"package":"ms","version":"2.1.3","severity":"warning","message":"package is not signed"}]}

$ rusk verify --format json
{"status":"success","total":70,"verified":70,"failed":0,"warnings":0}

Anomaly reporting webhook

Configure a webhook URL to get notified when rusk detects a security anomaly. Reports fire-and-forget via HTTP POST — they never block the install.

[trust]
require_signatures = true
require_provenance = true
report_url = "https://hooks.slack.com/services/T.../B.../xxx"

rusk sends a JSON report when it detects:

  • Provenance dropped — package previously had attestation, update doesn't
  • Provenance changed — different publisher, repo, or workflow
  • Signature missing — required signature not found
  • Signature invalid — cryptographic verification failed
  • Revocation hit — artifact or signer has been revoked
  • CAS corruption — content-addressed store integrity failure
{
  "timestamp": "2026-04-03T12:00:00Z",
  "anomaly_type": "provenance_dropped",
  "package": "litellm",
  "version": "1.82.8",
  "severity": "critical",
  "detail": "package previously had attestation but update does not",
  "hostname": "ci-runner-07"
}

Works with Slack webhooks, PagerDuty, Datadog, or any endpoint that accepts JSON.

Verifying release binaries

GitHub release checksums can be modified by anyone with repo write access. rusk releases include minisign signatures for out-of-band verification. See SECURITY.md for the public key and verification instructions.

Case study: how rusk would have stopped the litellm compromise

On March 24, 2026, a threat actor called TeamPCP published litellm versions 1.82.7 and 1.82.8 to PyPI. They got in by first compromising Trivy (an open-source security scanner) through a poisoned GitHub Action, which gave them access to LiteLLM's CI/CD pipeline and ultimately the maintainer's PyPI credentials.

The malicious release contained a file called litellm_init.pth — a 34KB double-base64-encoded payload. The .pth file format is special in Python: it executes automatically on every Python process startup when litellm is installed, no import litellm required. The payload stole SSL and SSH keys, cloud provider credentials, Kubernetes configs, git credentials, API keys, shell history, and crypto wallets. It also attempted lateral movement across Kubernetes clusters and installed a persistent systemd backdoor.

The package has about 480 million downloads. The malicious versions were live for roughly two hours before PyPI quarantined them.

Every pip and uv user who ran pip install --upgrade litellm or had an unpinned dependency got hit silently. No version tag existed on GitHub — the package was uploaded directly to PyPI, bypassing the normal release process. Nothing in the standard Python toolchain flagged it.

Here's what would have happened with rusk.

Layer 1 — Lockfile blocks the unknown version.

If you had litellm in your rusk.lock at version 1.82.6, running rusk install does nothing. rusk doesn't upgrade unless you explicitly run rusk update. The lockfile pins the exact SHA-256 digest of the wheel you already verified. The malicious 1.82.8 never enters your project.

$ rusk install
Already up to date. (0.17s)

The attacker published a new version, but rusk didn't care. Your lockfile said 1.82.6, that's what you got.

Layer 2 — Update triggers digest change detection.

If you did run rusk update litellm, rusk would resolve 1.82.8, download it, and compute its SHA-256. The lockfile diff would show:

$ rusk update litellm
  litellm: 1.82.6 -> 1.82.8
  digest changed: sha256:ab12... -> sha256:cd34...

That digest change is visible in your rusk.lock diff in version control. A code review would see the digest changed — and could ask "why did a new .pth file show up in a patch release?"

Layer 3 — Provenance flags the anomaly.

This is the big one. The malicious version was uploaded directly to PyPI — no corresponding tag or release existed on the litellm GitHub repository. If your policy requires provenance (require_provenance = true), rusk verifies that the artifact was built by a known CI system from a known repository. A direct PyPI upload has no provenance attestation at all:

$ rusk explain litellm
Policy evaluation:
  ! Provenance: missing (no build attestation found)
  ! No matching GitHub release tag for version 1.82.8
Verdict: DENY

This is exactly the signal that would have caught the attack. Legitimate litellm releases go through GitHub Actions. This one didn't.

Layer 4 — Signature policy catches the unauthorized publisher.

With require_signatures = true, rusk checks whether the package was signed by an expected identity. The attacker used stolen credentials obtained through the compromised Trivy supply chain, but if the signing key was different from the expected maintainer identity:

$ rusk audit --strict
[WARN] litellm@1.82.8: package is not signed
error: audit found 1 issue

CI fails. The package never reaches production.

Layer 5 — The .pth attack vector.

The litellm attack was particularly nasty because it used a .pth file that runs on every Python process startup — not just when you import litellm. This means the malware activates even if your code never touches litellm directly.

rusk's sandboxed build system blocks arbitrary code execution during install. But .pth files execute at Python runtime, not install time. This is a Python-level vulnerability that no package manager can fully prevent after installation.

What rusk does prevent: the malicious version from ever being installed in the first place. Layers 1 through 4 all independently block it before it reaches your site-packages.

What actually stops this class of attack:

The litellm compromise was a cascading supply-chain attack: Trivy compromised first, then used to steal LiteLLM's CI credentials, then used to publish a malicious version to PyPI. rusk stops it at multiple layers:

  1. Lockfile pins prevent silent upgrades (the attacker published 1.82.8, but your lockfile says 1.82.6)
  2. Digest changes are visible in version control (code review catches new .pth files)
  3. Provenance verification detects the missing CI attestation (no GitHub Actions build, no release tag)
  4. Signature policy catches unauthorized publishers
  5. Build sandbox limits blast radius for install-time code execution

No single layer is perfect. But stacking five layers means the attacker has to beat all of them. With pip, they had to beat zero.

How the cache works

rusk has three speed levels:

No-op (0.17s): Lockfile exists, install state exists, all node_modules/site-packages directories present. Returns immediately.

Warm cache (1.0s): Lockfile exists, all blobs in CAS. Skips resolution entirely. Verifies CAS blob integrity, then hardlinks from the extracted package cache. Zero network.

Cold install (5.1s): Fetches metadata in parallel, downloads tarballs/wheels, verifies SHA-256 on every artifact, stores in CAS, extracts, hardlinks.

Every level still verifies integrity. The warm path reads each CAS blob and recomputes its SHA-256 before using the extracted cache. There's no "trust the cache" shortcut.

Architecture

25 Rust crates, each with a clear responsibility:

  • rusk-core — Digests, IDs, versions, error types
  • rusk-cas — Content-addressed store (SHA-256 keyed)
  • rusk-transport — Parallel HTTP downloads with streaming verification
  • rusk-registry-npm — npm registry client
  • rusk-registry-pypi — PyPI registry client
  • rusk-resolver — Dependency resolver with conflict and cycle detection
  • rusk-materialize-js — npm tarball extraction, node_modules layout
  • rusk-materialize-python — Wheel extraction, site-packages layout
  • rusk-policy — Trust policy engine with declarative rules
  • rusk-tuf — TUF metadata verification (rollback/freeze protection)
  • rusk-signing — Signature verification (keyless + static key)
  • rusk-provenance — SLSA provenance attestation parsing
  • rusk-revocation — Signer/artifact revocation with epoch tracking
  • rusk-sandbox — Build isolation (process, container, namespace)
  • rusk-enterprise — Internal registries, air-gap bundles, SBOM export
  • rusk-orchestrator — Wires everything together
  • rusk-cli — The rusk binary

Both ecosystems share the same CAS, lockfile, policy engine, revocation system, and verification pipeline. The only ecosystem-specific parts are registry clients and file layout.

Live registry signature verification

rusk doesn't just check digests. It verifies cryptographic signatures against live registry infrastructure.

npm: Fetches registry ECDSA-P256 signing keys, verifies signatures on every package:

npm ECDSA signature verified, package: express, keyid: SHA256:DhQ8wR5APBvFHLF/+Tc+...
npm ECDSA signature verified, package: axios, keyid: SHA256:DhQ8wR5APBvFHLF/+Tc+...
provenance attestation found (SLSA), package: axios

PyPI: Fetches PEP 740 attestations from the Integrity API, verifies Trusted Publisher identity:

PyPI attestation bundle verified, package: litellm, publisher: GitHub,
  repository: BerriAI/project-releaser, workflow: publish-litellm.yml
PyPI attestation bundle verified, package: idna, publisher: GitHub,
  repository: kjd/idna, workflow: deploy.yml

Provenance change detection

rusk stores provenance metadata in the lockfile. On update, it compares old vs new and flags:

  • PROVENANCE DROPPED: package had attestation, update doesn't (litellm attack pattern)
  • PUBLISHER CHANGED: different CI system
  • SOURCE REPOSITORY CHANGED: different repo (fork attack)
  • BUILD WORKFLOW CHANGED: different pipeline

What rusk checks that others don't

Check rusk npm bun pip uv
SHA-256 every artifact Yes Partial No No No
CAS verify-on-read Yes No No No No
Lockfile digest pinning Yes Yes Yes No Yes
npm ECDSA signature verification Yes npm audit signatures No N/A N/A
PyPI PEP 740 attestation verification Yes N/A N/A No No
Provenance change detection on update Yes No No No No
Trust explanation per package Yes No No No No
Revocation epoch tracking Yes No No No No
Build script sandbox Yes No No No No
Auto-detect package.json/requirements.txt Yes N/A N/A N/A N/A
Security advisory scanner Yes npm audit No No No
Lockfile migration (npm/yarn/pnpm) Yes N/A N/A N/A N/A
Git dependencies Yes Yes Yes N/A N/A
Optional dependencies (skip on fail) Yes Yes Yes N/A N/A
.npmrc auth token support Yes Yes Yes N/A N/A
Package patching Yes No Yes N/A N/A
Local package linking Yes Yes Yes N/A N/A
Version overrides Yes Yes Yes N/A N/A
Isolated node_modules (pnpm-style) Yes No Yes N/A N/A
Python tool runner (like uvx) Yes N/A N/A No Yes
Structured CI exit codes Yes (13 codes) No No No No
JSON output for all commands Yes No No No No
Anomaly reporting webhook Yes No No No No
cargo rusk subcommand Yes N/A N/A N/A N/A
Out-of-band release signing Yes (minisign) No No No No

Testing

385 unit/integration tests, 33 PowerShell failure scenario tests, and 40 end-to-end command tests. Includes 66 adversarial security tests that simulate real supply-chain attacks:

  • CAS corruption detection
  • Lockfile digest tampering
  • Revocation cache invalidation
  • Policy bypass attempts (deny always overrides allow)
  • Dependency confusion blocking
  • Artifact substitution detection
  • Signer identity matching
  • Merkle inclusion proof verification
  • TUF rollback/freeze detection
  • Extracted cache eviction on CAS corruption

Run them:

cargo test --workspace

Building from source

git clone https://github.com/harishsg993010/rusk.git
cd rusk
cargo build --release -p rusk-cli

Requires Rust 1.75 or later. The release binary is about 8MB.

Project status

rusk is a working package manager. You can use it today to install JavaScript and Python packages with stronger security guarantees than any mainstream alternative.

26 commands covering the full package lifecycle:

Package management: install, add, remove, update, lock, sync, run, list, tree, migrate, patch, link Security: verify, audit (with advisory scanning), explain, build (sandboxed) Python tools: venv, python list/find/pin, tool run/install (rusk x) Infrastructure: init, gc, config, publish, --format json, --exit-codes

Feature-complete for both ecosystems:

  • Full transitive dependency resolution for JS (npm) and Python (PyPI)
  • Auto-detection of package.json, requirements.txt, pyproject.toml, rusk.toml
  • Ecosystem-aware rusk add — JS packages go to JS, Python to Python
  • Parallel metadata fetching across dependency tree levels
  • Content-addressed storage with verify-on-read integrity checking
  • Lockfile-first installs with three-tier caching (no-op / warm / cold)
  • Extracted package cache with hardlinks for near-instant reinstalls
  • Platform-aware wheel selection (Python version + OS + arch)
  • Custom index URLs (PyTorch CPU from download.pytorch.org)
  • Git dependencies (git+https://, github:user/repo)
  • Optional dependencies (warn-and-skip on failure)
  • Version overrides for transitive dependencies
  • Isolated (pnpm-style) node_modules layout option
  • Package patching and local symlink development
  • Lockfile migration from npm, yarn, and pnpm
  • .npmrc auth token support with env var expansion

Security on every install:

  • SHA-256 digest verification on every artifact
  • npm ECDSA signature verification against live registry keys
  • PyPI PEP 740 attestation verification via Integrity API
  • Provenance change detection on update (catches litellm-style attacks)
  • Security advisory scanning against npm vulnerability database
  • Policy engine with audit, verify, and explain commands
  • Revocation checking with epoch-based cache invalidation
  • Build sandbox with env scrubbing (process and container backends)
  • Anomaly reporting webhook for Slack/PagerDuty/Datadog
  • Structured CI exit codes (13 distinct codes)
  • JSON output for all commands
  • Out-of-band release binary signing

Tested against real production frameworks: Express (70 deps), Fastify (46 deps), Flask (15 deps), litellm (60+ deps), PyTorch (22 deps from custom index), and mixed JS+Python monorepos. 40/40 end-to-end command tests passing.

Everything listed above runs on every rusk install. Use -v to see the full security pipeline in action.

License

Apache-2.0

Dependencies

~13–18MB
~266K SLoC