Wraps npm, pip, cargo, yarn, pnpm, gem, bun, and go as a PATH shim — refuses to install packages with known CVEs.
$ npm install lodash@4.17.10
refuse: blocked — CVE-2019-10744 (high)
Prototype pollution in lodash <= 4.17.11
suggested safe version: 4.17.21refuse sits in front of your package managers. Every install call is vetted against a refuse server (self-hosted) or refuse.dev (hosted) before the real binary runs. If the package has a known advisory above your severity threshold, the install is blocked with the CVE and a suggested safe version.
Works:
- On a developer's laptop.
- In a CI runner.
- Inside a Docker build stage.
- As a Claude Code PreToolUse hook — same gate for autonomous agent installs.
Homebrew (macOS):
brew install refusehq/tap/refuseDirect binary on macOS / Linux (with sha256 checksum verification):
curl -sSL https://raw.githubusercontent.com/RefuseHQ/refuse-cli/main/scripts/install.sh | shDirect binary on Windows (PowerShell, with sha256 checksum verification):
irm https://raw.githubusercontent.com/RefuseHQ/refuse-cli/main/scripts/install.ps1 | iexFrom source:
go install github.com/RefuseHQ/refuse-cli/cmd/refuse@latestVerified releases are cosign-signed with SLSA build provenance — see SECURITY.md for the verification command.
Platforms. Pre-built binaries are published for:
| OS | Architectures |
|---|---|
| macOS | x86_64, arm64 |
| Linux | x86_64, arm64, i386, armv6, armv7 |
| Windows | x86_64, arm64, i386 |
Other platforms can go install from source.
refuse init # interactive: server URL + API key
refuse install # writes shims to ~/.refuse/bin + updates PATH
refuse hook install claude-code # PreToolUse hook in ~/.claude/settings.jsonThen run anything you'd normally run:
npm install express
pip install requests
cargo add tokioIf the install is clean, it goes through. If it isn't, refuse blocks it and tells you why.
flowchart LR
USER[Developer] -->|"npm install …"| SHIM[refuse-cli<br/>aliased as 'npm']
AGENT[Coding agent] -->|PreToolUse hook| GATE[refuse gate]
SHIM --> PARSE[parser]
GATE --> PARSE
PARSE -->|"(eco, name, ver)"| DECIDE[decide]
DECIDE -->|HTTP| SERVER[(refuse server)]
SERVER -->|verdict| DECIDE
DECIDE -->|allow| REAL[real binary]
DECIDE -->|block exit 2| FAIL[stderr]
A single Go binary, symlinked into ~/.refuse/bin/ under each manager's name. When npm is invoked, the shim parses the argv, asks the server, and either execs the real npm on PATH or exits with code 2 and a message.
For details, see ARCHITECTURE.md.
| Manager | Ecosystem | Install verbs | Lockfile parsing |
|---|---|---|---|
npm |
npm | install, i, add |
package-lock.json |
pnpm |
npm | install, add |
pnpm-lock.yaml |
yarn (classic + Berry) |
npm | add, install |
yarn.lock |
bun |
npm | install, add |
bun.lockb / bun.lock |
pip / pip3 |
PyPI | install, install -r |
requirements.txt |
cargo |
crates.io | add, install |
Cargo.lock |
gem |
RubyGems | install |
Gemfile.lock |
go |
Go modules | get, install |
go.sum |
| Agent | Status |
|---|---|
| Claude Code | ✓ supported |
| Cursor | tracked in #? |
| Continue | tracked |
| Aider | tracked |
| Codex CLI | tracked |
| Cline | tracked |
PRs welcome — see internal/hook/claudecode.go as the reference.
| Command | What it does |
|---|---|
refuse init |
First-time setup wizard |
refuse install |
Install shims for the supported package managers |
refuse uninstall |
Remove shims + revert shell-rc edits |
refuse hook install <agent> |
Write a pre-tool-use hook for <agent> |
refuse hook remove <agent> |
Remove that hook |
refuse hook list |
Show all installed hooks |
refuse check <eco> <pkg>[@<ver>] |
One-off check |
refuse check-lockfile <path> |
Scan an entire lockfile |
refuse gate |
The decision engine — shims + hooks call this on stdin |
refuse config show | set | get |
Manage ~/.refuse/config.yaml |
refuse status |
Diagnose install state |
refuse doctor |
Verify PATH / hooks / server reachability |
refuse config set <key> <value>, or edit ~/.refuse/config.yaml directly:
server_url: http://localhost:8080 # or https://mcp.refuse.dev
api_key: rfs_... # optional, required for hosted
policy:
severity_threshold: high # low | medium | high | critical
fail_closed: false # true = block if server unreachable (default false → fail open with stderr warning)
allow_yanked: false # allow yanked versions when no advisory matches
allow_prerelease: false # allow prerelease versions
override_env: REFUSE_ALLOW_VULNERABLE # env var that forces a block to pass throughEnvironment variables override the file (useful in CI):
REFUSE_SERVER_URLREFUSE_API_KEYREFUSE_POLICY— setsseverity_thresholdREFUSE_FAIL_CLOSED—1/trueto enableREFUSE_ALLOW_VULNERABLE—1/trueto bypass a single install
Hosted (refuse.dev):
refuse config set server_url https://mcp.refuse.dev
refuse config set api_key rfs_...Self-hosted (RefuseHQ/refuse):
docker run --rm -p 8080:8080 ghcr.io/refusehq/refuse:latest
refuse config set server_url http://localhost:8080# .github/workflows/ci.yaml
jobs:
install:
runs-on: ubuntu-latest
env:
REFUSE_SERVER_URL: https://mcp.refuse.dev
REFUSE_API_KEY: ${{ secrets.REFUSE_API_KEY }}
steps:
- uses: actions/checkout@v4
- name: Install refuse
run: curl -sSL https://raw.githubusercontent.com/RefuseHQ/refuse-cli/main/scripts/install.sh | sh
- run: $HOME/.refuse/bin/refuse check-lockfile package-lock.json # exits 2 on a vulnerable hit
- run: npm ci| What it is | When to use it | |
|---|---|---|
| refuse-cli (this) | PATH shim in front of package managers | You want to block installs before they happen, on a laptop / CI / Docker build |
| refuse | Self-hostable HTTP server | You want to run your own backend |
| refuse.dev | Hosted service | You don't want to run anything; sign up and point the CLI at it |
Alpha. The gate is production-ready; some convenience subcommands are still being filled in. See ROADMAP.md and open issues for the path to 0.1.0.
PRs welcome — particularly for new package managers, agent hooks, and platforms. See CONTRIBUTING.md.
Security policy: SECURITY.md. Report privately via hello@refuse.dev.
Apache License 2.0 © RefuseHQ.