An awesomely fast virtual bash sandbox. Written in Rust.
Bashkit runs untrusted shell scripts from AI agents without spawning a single OS process. 160 POSIX-compliant commands, a virtual filesystem, resource limits, and streaming LLM tool calls — all in-memory, all sandboxed.
What bashkit gives you
A single runtime you can embed in agents, CLIs, editors, and evaluation harnesses. No sidecar process, no container overhead, no external dependencies at runtime.
POSIX-compliant interpreter
Substantial IEEE 1003.1-2024 Shell Command Language coverage, plus bash extensions: arrays, [[ ]], brace expansion, extended globs, coprocesses, traps.
160 reimplemented commands
grep, sed, awk, jq, curl, tar, find, xargs, and 150+ more — pure Rust, no shelling out.
LLM tool contract
BashTool with discovery metadata, streaming output, and system prompts. Plug into any agent framework.
MCP server
bashkit mcp exposes the interpreter over Model Context Protocol for Claude, Cursor, and other clients.
Snapshotting
Serialize shell state and VFS contents to bytes. Checkpoint any workload, resume anywhere.
Scripted tool orchestration
Compose ToolDef + callback pairs into an OrchestratorTool driven by a bash script.
Three languages, one runtime
Ship in Rust for the lowest overhead, or use the Python wheel and npm package when you need to drop bashkit into an existing stack.
The core crate
cargo add bashkit use bashkit::Bash;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut bash = Bash::new();
let out = bash.exec("echo hello world").await?;
println!("{}", out.stdout);
Ok(())
} PyO3 wheel + LangChain integration
pip install bashkit from bashkit import BashTool
tool = BashTool()
result = await tool.execute("echo 'Hello, World!'")
print(result.stdout) NAPI-RS for Node, Bun, Deno
npm i @everruns/bashkit import { BashTool } from "@everruns/bashkit";
const tool = new BashTool({
username: "agent",
hostname: "sandbox",
});
const { stdout } = await tool.execute("echo hi");
console.log(stdout); A tiny Rust program that uses bashkit as an in-process shell. No container, no subprocess — just a crate.
$ cargo add bashkit
$ cat > demo.rs <<'RS'
use bashkit::Bash;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut bash = Bash::new();
bash.exec("mkdir -p /tmp/data").await?;
bash.exec("echo 'hello' > /tmp/data/out.txt").await?;
let r = bash.exec("cat /tmp/data/out.txt | tr a-z A-Z").await?;
println!("{}", r.stdout); // HELLO
Ok(())
}
RS Hostile input is the default assumption
Defense in depth across every layer — process, filesystem, network, parser, and runtime. See the full threat model for 250+ mitigations.
No process spawning
160 commands reimplemented in Rust — no fork, exec, or shell escape.
Virtual filesystem
Scripts see an in-memory FS by default. No host access unless mounted.
Network allowlist
HTTP is denied by default. Each domain must be explicitly allowed.
Resource limits
Caps on commands (10K), loops (100K), function depth, output (10MB), input (10MB).
Parser limits
Timeout, fuel budget, AST depth — pathological input can't hang the interpreter.
Panic recovery
Every builtin is wrapped in catch_unwind. A panic in one command can't crash the host.
How well do models use bashkit?
Bashkit ships with a 58-task eval harness across 15 agentic categories. A snapshot of the latest run:
| Model | Score | Tasks passed |
|---|---|---|
| Claude Haiku 4.5 | 97% | 54/58 |
| Claude Sonnet 4.6 | 93% | 48/58 |
| Claude Opus 4.6 | 91% | 50/58 |
| GPT-5.3-Codex | 91% | 51/58 |
| GPT-5.2 | 77% | 41/58 |