Get started
Install #
With Node 18 or newer there is nothing else to set up:
$ npx -y @jondot/blastradius demo # run it once $ npm i -g @jondot/blastradius # or put br on your PATH
Without Node, the installer:
$ curl -fsSL https://raw.githubusercontent.com/jondot/blastradius/main/install.sh | shThis downloads the latest release for your OS and architecture, checks its sha256, and installs br to ~/.local/bin (or $BR_INSTALL_DIR). Pin a version with sh -s -- --version v0.1.2.
For an AI agent, add the skill. It teaches the agent when to reach for br, how to phrase a hunt, and how to read the answer:
$ npx skills add jondot/blastradiusOr give it br as an MCP server (see MCP for agents):
$ claude mcp add blastradius -- npx -y @jondot/blastradius mcpIn GitHub Actions, use jondot/blastradius@v1 (see CI and the Action).
Builds: Linux (x86_64 and arm64, static musl), macOS (x86_64 and arm64) and Windows (x86_64). Archives are on the releases page. From source: cargo install --path .
Get started
Try it with no token #
br demo uses a sample org built into the binary. No token, no network, nothing of yours is read. It prints a six-step tour of a made-up incident. The first step:
$ br demo hunt --advisory GHSA-q7vr-4xhc-m2jw --format summary Yes. 7 repos, 2 images, 2 machines, 2 caches and 2 build artifacts contain or contained the affected packages (GHSA-q7vr-4xhc-m2jw). 4 people and 2 machines may also be affected but could not be confirmed. Still present: 5 repos, 2 images, 2 machines, 2 caches and 2 build artifacts. ... Data as of 05-08 10:00Z–05-12 06:30Z (oldest: laptop-priya). Not checked: 4 sources (acme/mobile-web; ci-runner-04; laptop-priya; laptops: 11 of 12 members visible to this token in GitHub have no laptop scan here). The number can still change.
Get started
Build the index #
Build it once. A read-only GitHub token is enough.
$ export GITHUB_TOKEN=... $ br init --org acme
The index lives in ~/.blastradius (override with BR_HOME). Keep one per client with br --home ~/clients/acme init --org acme (the same as BR_HOME=~/clients/acme). Re-scans use conditional requests, so an unchanged repo costs about 3 API calls.
Get started
Your first hunt #
Ask from an advisory id, an indicator, or pasted advisory text.
$ br hunt --advisory GHSA-xxxx-xxxx-xxxx $ br hunt --advisory GHSA-xxxx-xxxx-xxxx --owner frontend # one team's places $ br hunt --advisory GHSA-xxxx-xxxx-xxxx --repo acme/web # is my repo ok?
The answer lists every repo, CI run, cache, artifact, image and machine that holds the bad version, now or at any time in the exposure window. It names the secrets the CI runs could read and the team that owns each place. It says where the thing was installed, not that the bad code ran.
A hunt reads only the index. It touches the network or a machine only when you pass --now, --live or --path.
Use
Indicators #
An indicator says what to look for. Names are matched whole: lodash never matches lodash.merge. Advisories are matched by their real version ranges, per package.
| Form | Finds |
|---|---|
npm:@scope/name@1.2.3 | A package version. Also pypi:, cargo:, go: |
action:owner/repo@ref | A GitHub Action at a ref |
sha256:HASH | A file by hash, including inside image layers |
runtime:NAME@fixed:V1,V2 | A language runtime below the fixed release of its line |
cert:HEX, cert:subject:TEXT | A CA certificate in a trust store, by fingerprint or subject |
secret:NAME | A secret by name, with * globs and --since DATE |
Use
Runtimes, certificates and secrets #
Runtimes
$ br hunt runtime:php@fixed:8.5.12,8.4.27,8.3.36,8.2.35 $ br hunt runtime:node@fixed:22.23.4,24.22.1,26.12.1
fixed: lists the first fixed release of each line. A version on one of those lines but below the fix is found, and so is a line with no fix at all. br reads version files, Dockerfile FROM tags and ENV versions, setup-php and setup-node steps, the runtimes installed in images, and the runtimes on runners and laptops. A floating tag such as node:lts is reported apart, as possible.
Certificates
$ br hunt "cert:subject:emSign ECC Root CA - G3" $ br hunt cert:SHA256-FINGERPRINT
It reads the certificates themselves out of OS bundles in every image layer, certifi copies, Java cacerts, PEM bundles in repos, and the trust stores on scanned machines.
Secrets
$ br hunt 'secret:RAILWAY_*' --since DATEEvery matching org, repo or environment secret, with when it was last updated. Anything not updated since --since is NOT ROTATED. It also lists the workflows that read it, the images that name it in their environment, and the config files that mention it.
It works from names and dates only. br never reads a secret value.
Use
After the hunt #
$ br fix --advisory GHSA-xxxx-xxxx-xxxx # per repo: how to reach a fixed version $ br notify --advisory GHSA-xxxx-xxxx-xxxx # one draft per owning team $ br purge --advisory GHSA-xxxx-xxxx-xxxx # caches, artifacts, images and paths to delete $ br hunt --advisory GHSA-xxxx-xxxx-xxxx --progress # the day after: still open, fixed, new
Someone sent you a sample? br hunt --similar suspect.js finds files that look like it, and br hunt sha256:HASH --similar-related also finds the files shipped with it in the same install.
Use
Laptops and hosts #
Laptops with no network path to the index scan themselves and send the result.
$ br scan --local --export laptop.brx --seal # on the laptop $ br import laptop.brx # on your machine $ br hosts # who is still missing
A second file for a machine already in the index is merged by path, and the newer scan wins. --replace swaps the whole machine. Tell br what it can't know from outside with br hosts add NAME --kind runner|laptop|server.
Use
CI and agents #
$ br hunt npm:name@1.2.3 --path . --fail-if found --strict # no index, no network; exit 10 when found $ br hunt --advisory GHSA-xxxx-xxxx-xxxx --format sarif # SARIF 2.1.0 for code scanning $ br claimcheck --path . --org acme --fail-if exposed # asks the public registries about your names; exit 10 when exposed, 3 if it checked nothing
The GitHub Action (jondot/blastradius@v1), the SARIF output and the pre-commit hook are in CI and the Action. br mcp is a read-only MCP server over stdio so an AI agent can ask br during an incident: MCP for agents.
Use
All commands #
| Command | What it does |
|---|---|
init | Build the index from a GitHub org, a directory, or hosts |
hunt | Find where an advisory or indicator lives, with owners |
fix | Per repo, how to reach a fixed version |
notify | One draft message per owning team |
evidence | A sealed record, optionally signed, optionally with a customer-safe statement |
verify | Check an evidence file's manifest and signature |
purge | The caches, artifacts, images and paths to delete |
claimcheck | Which internal package names a stranger could already own on npm, PyPI, crates.io or the Go proxy |
mcp | A read-only MCP server for an AI agent |
status | How fresh each source is, and what the answer did not cover |
demo | The same commands on a built-in sample org |
scan, import, hosts | Re-read sources, bring in laptop files, list machines |
diff, teams, advisory, sql | Compare, inspect owners, manage advisories, query the index |
Run br --help or br COMMAND --help for every flag.
Trust
Answers and exit codes #
Every answer says how old the index is, how old its oldest source is, and which sources it could not check: a 403, an expired log, an unreadable lockfile. A clear answer with gaps is reported as such, never as plain clear. --max-age DUR refuses to answer (exit 5) if a source is older.
| Code | Meaning |
|---|---|
0 | Clear |
10 | Found |
3 | Clear with gaps, under --strict |
5 | No index, or a stale one |
11 | Nothing found, under --fail-if none |
4, 2, 1 | Auth problem, usage error, failure |
Trust
What it reads #
br never writes to anything it reads. Every GitHub call is a GET or HEAD, and the only POSTs are the registry token exchanges. Why br lists what it sends, and where.
- GitHub org: repos, lockfiles and manifests with their commit history (npm, yarn, pnpm, PyPI, Cargo, Go), CODEOWNERS, workflow files, Actions runs, caches and artifacts, releases and their assets, teams and members, registered runners, and org container images on GHCR.
- Container registries: images, layers and their files by hash, including files a later layer deleted. Uses your existing
docker login. - Machines: a directory on this machine, ssh-reachable hosts and runners, and
.brxfiles exported by laptops. - Advisories: OSV, for GHSA and MAL ids; advisory ids in pasted text are fetched too.
- Public registries, only for claimcheck: read-only GETs to npmjs.org, pypi.org, crates.io and proxy.golang.org about your own package names. A hunt never does this.
Trust
Evidence #
Evidence is sealed with a sha256 manifest, which proves integrity, and with --sign it is signed with ssh-keygen -Y sign, which proves identity.
$ br evidence --advisory GHSA-xxxx-xxxx-xxxx --out evidence.json --sign ~/.ssh/id_ed25519 $ br evidence --advisory GHSA-xxxx-xxxx-xxxx --out evidence.json --audience customers $ br verify evidence.json --allowed-signers allowed_signers
--audience customers adds a plain statement with no internal names.