Model or dataset
kaplanelad/shellfirm avatar
kaplanelad/shellfirm

shellfirm asks you to add two numbers before it lets a command through

Safety guardrails for ai coding agents and human terminal commands

934 stars34 forksRustApache-2.0

At a glance

What is it?
A Rust shell guard that intercepts risky commands, prints a severity, a blast radius estimate and a safer alternative, then makes you solve a small arithmetic challenge. The useful ideas are the blast radius count, the context-aware escalation and the project policy file that can only add rules, never remove them.
Who is it for?
shellfirm fits anyone who types destructive commands, or lets an agent type them for them, and wants a moment of friction plus a concrete safer command instead of a rule nobody reads. It does not fit a workflow where commands run unattended, because the challenge is interactive by design and the escape hatch is a keyboard interrupt.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 141 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 3, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The challenge is friction, not a lock

Here is the whole interaction for a recursive delete:

code
$ rm -rf ./src
============ RISKY COMMAND DETECTED ============
Severity: Critical
Blast radius: [PROJECT] — Deletes 347 files (12.4 MB) in ./src
Description: You are going to delete everything in the path.

Solve the challenge: 8 + 0 = ? (^C to cancel)

The gate is arithmetic. That is the design, and it is worth being clear about what it buys: it inserts a pause and a physical keypress between a command and its effect, and it gives you a place where the severity, the blast radius and an alternative can be read. It is not an authorisation system, and nothing on the page suggests a script can answer the prompt or that there is a mode where a policy is enforced without a human. The second example shows the other half of the value, a force push caught as High severity with the branch reported as three commits behind remote, and a concrete replacement offered instead of a warning:

code
Alternative: git push --force-with-lease
  (Checks that your local ref is up-to-date before force pushing, preventing accidental overwrites of others' work.)

The force-with-lease suggestion is the kind of output that changes behaviour, where a block alone would only teach people to work around the tool.

Blast radius is measured before you answer

The line that makes the warning more than a keyword match is the blast radius, and it is labelled with a scope. The delete case says PROJECT and counts 347 files and 12.4 megabytes in the target path, which means the tool walked that directory before deciding what to tell you, and the force-push case says RESOURCE and names the branch and its distance from the remote. The feature list names the mechanism, blast radius detection, with runtime context signals feeding into risk scoring, so the same command can score differently depending on what is on disk and what branch you are on. Two practical consequences. A pattern match alone would have told you the same thing about `rm -rf ./src` in a directory with three files as in one with a thousand, and the number is what makes the difference legible. And the cost is real work before execution, on the interactive path, which is a second reason the tool is built around a human answering rather than a batch process.

Escalation comes from context, not from the command text

The escalation rules are the part that cannot be faked by rewriting a command. Harder challenges are triggered when you are connected over SSH, when you are running as root, when you are on a protected git branch, or when you are in a production Kubernetes cluster, and the force-push example shows the same principle with a smaller fact, that the tool knows the branch is three commits behind the remote rather than only knowing the string of the command. Severity levels are configurable across five values, Critical, High, Medium, Low and Info, and the documentation has a page for context-aware protection listing SSH, root, git branches, Kubernetes and environment variables, plus a separate page for challenge types, severity thresholds and custom checks. The design point for anyone borrowing this: a dangerous verb is not a dangerous command. The same command is a different risk on a laptop, inside a production cluster and on a release branch, and only runtime signals tell you which one you are in. The limitation follows from that design, since a container that hides SSH state, root context or cluster identity will also hide the escalation.

A project policy file can only add rules

Team rules live in a `.shellfirm.yaml` file in the repository, and the property that matters is stated in one clause: the file is additive-only and never weakens. So a repository can require stricter checks for its own contributors, and it cannot lower the thresholds you have already configured, and it cannot switch off a rule your own setup depends on. For a tool that runs inside a shell hook, that is the load-bearing decision, because a repository is untrusted input by definition: any project you clone could ship a configuration that turns your guardrails off if configuration were allowed to weaken. A hundred patterns across nine ecosystems, filesystem, git, Kubernetes, Terraform, Docker, AWS, GCP and Azure, Heroku and databases, plus eight supported shells, Zsh, Bash, Fish, Nushell, PowerShell, Elvish, Xonsh and Oils, is the surface the additive policy has to cover. The same reasoning is why the audit trail is worth having: every intercepted command and the decision taken on it is logged as JSON lines, which gives you a record to argue from after the fact.

One command wires hooks and an MCP server

The agent integration is a single command:

bash
shellfirm connect claude-code

It adds two things. Hooks check every Bash command before execution and block the risky ones, and an MCP server lets the model ask questions instead of guessing, through four tools: `check_command` to get a severity, the matched rules and alternatives, `suggest_alternative` for a safer replacement, `explain_risk` for the reasoning, and `get_policy` to read the active configuration and the project policy. That last tool is the interesting one, because it lets a model discover that a repository has stricter rules rather than assume the machine is unprotected. The documentation also has a page for agents and automation covering the MCP server, LLM analysis and an agent mode, none of which appear in the feature list on the main page. The framing of the project is on that point: the opening line is that humans make mistakes and AI agents make them faster, so the hook is the enforcement point and the model is a caller of the same rules.

Three package channels, eight shells, one init command

Installation is offered four ways, and the choice matters more than it looks:

bash
npm install -g @shellfirm/cli
bash
brew tap kaplanelad/tap && brew install shellfirm
bash
cargo install shellfirm

plus a binary from the releases page. A shell hook, a Homebrew tap, a Cargo install and a release artefact can each be a version apart, and a guard that silently stops matching new patterns is a worse failure than one that is obviously missing. The setup is two steps after that. `shellfirm init` writes the hook and auto-detects your shell, then you restart the shell or source your rc file, and the smoke test is deliberately violent:

bash
git reset --hard  # Should trigger shellfirm!

Per-shell instructions and an Oh My Zsh plugin live in the documentation rather than the readme, which is the right place for eight shells' worth of snippets. The three newest releases, v0.3.8, v0.3.9 and v0.3.10, are patch-level, and the last commit came eight days after the newest tag.

One crate, and a CI that treats warnings as errors

The Rust side is smaller than the feature list suggests. The workspace has a single member, `shellfirm`, with a `build.rs` at the root, and the rest of the repository is the npm wrapper package, scripts, documentation and the usual repository files. That shape suits a binary that installs into a shell, since there is no library surface to stabilise. The Makefile is where the project's own standards are written down, and they are strict for a tool this size. Formatting is checked rather than applied, with `cargo fmt --all -- --check`, clippy runs with warnings promoted to errors through `-D warnings`, and documentation is validated with `RUSTDOCFLAGS="-D warnings"` together with `--workspace --all-features --no-deps --document-private-items`, which means internal comments are held to the same documentation standard as the public API. A combined target runs the tests, formatting, clippy and doc validation together as the CI equivalent. The help target is the traditional one built on `fgrep`, which quietly assumes a grep with the flags that command needs.

Editorial conclusion

shellfirm fits anyone who types destructive commands, or lets an agent type them for them, and wants a moment of friction plus a concrete safer command instead of a rule nobody reads. It does not fit a workflow where commands run unattended, because the challenge is interactive by design and the escape hatch is a keyboard interrupt. Four things to check before you rely on it: what the guard does in a script, since every example is a human at a prompt and nothing on the page covers a non-interactive run; which context signals your machine exposes, since escalation depends on SSH, root, branch state and Kubernetes detection and a container may hide those; how you version it, because there are three install channels and a release binary and they can drift apart; and what you want the audit log to go to, since it is JSON lines on local disk with no shipping story described. The pattern worth copying even if you never install it is the additive-only policy file. The project is Apache-2.0, the newest release is v0.3.10 from May 7, 2026, and the last commit is dated May 15, 2026.

Frequently asked questions

What is shellfirm?

A Rust command-line guard installed as a shell hook that intercepts risky commands before they run. It prints a severity, a blast radius estimate and a safer alternative, then asks you to solve a small arithmetic challenge to let the command proceed, with control-C to cancel.

How do I install shellfirm?

With `npm install -g @shellfirm/cli`, `brew tap kaplanelad/tap && brew install shellfirm`, `cargo install shellfirm`, or a binary from the releases page. Then `shellfirm init` installs the shell hook and auto-detects your shell, after which you restart the shell or source your rc file.

Does shellfirm work with Claude Code?

Yes, with one command, `shellfirm connect claude-code`. It adds hooks that check every Bash command before execution and an MCP server with four tools, so Claude can ask for a severity, a safer alternative, an explanation of a risk, or the active project policy.

What can a project put in .shellfirm.yaml?

Stricter rules for its own contributors. Project policies are additive only, so a file in a repository can add checks but never weaken or disable the ones you have configured yourself.

Which shells does shellfirm support?

Eight: Zsh, Bash, Fish, Nushell, PowerShell, Elvish, Xonsh and Oils. The `shellfirm init` command auto-detects the shell, and the documentation carries per-shell setup instructions plus an Oh My Zsh plugin.

Official sources

  1. kaplanelad/shellfirm on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kaplanelad-shellfirm.svg)](https://hysenlabs.com/projects/kaplanelad-shellfirm)