$_ bashkit
Virtual bash for AI agents

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.

Built-in commands 160
Threats mitigated 250+
Haiku 4.5 eval 97%
Product surface

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.

Quick start

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.

Rust

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(())
}
Python

PyO3 wheel + LangChain integration

pip install bashkit
from bashkit import BashTool

tool = BashTool()
result = await tool.execute("echo 'Hello, World!'")
print(result.stdout)
TypeScript

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);
End-to-end

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
Security

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.

LLM evals

How well do models use bashkit?

Bashkit ships with a 58-task eval harness across 15 agentic categories. A snapshot of the latest run:

ModelScoreTasks 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
Open source

Built for real agents. Open under MIT.

Drop it into your agent, your CLI, your editor, your eval harness. Issues and PRs welcome.