---
title: 'Introducing Bashkit: Bash That Cannot Hurt You'
description: 'Sandboxed bash interpreter written in Rust. No fork/exec, no real filesystem, no subprocess surprises. Built for LLM agents, multi-tenant sandboxes, and CI.'
date: 2026-04-18
cover: ./cover.png
coverAlt: 'Bashkit interactive shell running jq against embedded JSON'
tags:
  - rust
  - bash
  - ai
  - sandboxing
  - developer-experience
---

Back in September 2025 I was solving super typical agent problem — agent needs to process some complex data, call multiple APIs, glue results together, do few conditional things on top. Classic tool-use mess. You can do it with many round-trips and many tools, but this is slow, expensive, and agent tends to get confused somewhere in the middle.

Good solution for this is to give the agent access to some scripting language. So it can write one small script and just run it. One turn, done.

So I spent some time evaluating options. JavaScript — fine, but runtime is heavy and sandboxing is annoying. Python — same story, plus packaging. Lua — small and nice, but LLMs do not really know Lua that well. And at some point it just clicked: bash is already solving this. Agents know bash extremely well, bash is literally designed to glue tools together, and every existing CLI speaks bash natively.

Only problem — giving an agent real bash is terrifying. One hallucinated `rm -rf /` and your afternoon is ruined.

So, let me introduce [Bashkit](https://github.com/everruns/bashkit/).

## What Is It

Bashkit is a sandboxed bash interpreter written in Rust. The key word here is "interpreter." It does not spawn `bash`. It does not fork/exec. It does not shell out to `/bin/ls` somewhere on your system. A Bashkit script is literally Rust code running inside your process.

Few things that make it weird in a good way:

- ~160 bash builtins reimplemented in pure Rust. `ls`, `grep`, `sed`, `curl`, `git`, `ssh` — all native functions, no subprocesses, zero spawning.
- Virtual filesystem by default. `rm -rf /` is a no-op on your real disk.
- Each instance fully isolated, zero shared state. Multi-tenant safe out of the box.
- POSIX-serious. Full quoting, heredocs, process substitution, arrays, functions, pipes, glob expansion. Not a toy.

That is the whole pitch.

## What Else It Is Good For

Agent scripting is the main story. But once you have a safe, in-process bash, few other nice things fall out:

- **Tool orchestration.** Instead of five LLM round-trips to call five tools, compose them into one bash script and you have one turn. Big latency and cost win.
- **Multi-tenant SaaS.** Give every user a sandboxed shell. No container per user, no noisy neighbors. Just an instance of a Rust struct.
- **CI for untrusted code.** PR previews, code grading, student submissions. Run their script, throw away the VFS, move on.
- **Deterministic testing.** No process variance, no real filesystem weirdness. Same input, same output.
- **MCP server out of the box.** Ships with MCP mode so your agent can talk to it immediately.

## Custom Builtins Are the Real Thing

If I have to point at one feature that is more important than others, it is custom builtins. You can register your own Rust, Python or JS function as a bash command. `my-cool-tool arg1 arg2` can literally be your Rust fn. The agent does not care, it just sees bash.

This is super powerful, because it lets you expose whole taxonomies of CLIs and tools to the agent as native commands. Your internal APIs, your SaaS endpoints, your weird in-house gadgets — all of that becomes `verb --flag value` in bash. Agent already speaks this language fluently.

This is also what enables code mode out of the box. Instead of describing your tools as JSON schemas and doing tool-use ping-pong, you expose them as builtins and let the agent write one bash script that calls them in the order it needs. One turn. Composition for free. No schema drift, no tool-call overhead.

![Code mode flow: agent writes a bash script, Bashkit resolves each command to your registered Rust, Python or JS function](code-mode.svg)

For me this is the actual reason Bashkit exists. Sandboxing is the table stakes that makes it safe. Custom builtins is the thing that makes it useful.

## Does It Actually Work

There is an eval harness with 58 tasks covering what agents actually do in bash. Claude Haiku hits 97% on it, using Bashkit as a tool. So yeah, it works. Well enough for real agent workflows.

And not only in evals. Bashkit is the main agent runtime behind [Everruns](https://everruns.com/) — most agents running on the platform use it. Platform chat alone exposes hundreds of operations as bash builtins, all running inside Bashkit. More on this in the [Virtual Bash docs](https://docs.everruns.com/capabilities/virtual-bash/). Bashkit integrates seamlessly into the Everruns agent virtual filesystem — the agent sees one unified VFS, with Bashkit running right inside of it, sharing the same files, the same builtins, the same sandbox.

## Also, Maybe We Are Accidentally Building an OS

Half joke, half not. At some point I realized you can run a whole agent written in bash inside Bashkit — [here is a tiny harness that calls OpenAI in a loop, all in bash](https://github.com/everruns/bashkit/blob/main/examples/harness-openai-joke.sh). Agent loop, tool calls, the whole thing, in a shell script. Running inside Bashkit. Which is a sandboxed process with its own filesystem, its own tools, its own everything.

![Bashkit running an OpenAI agent harness written in pure bash](harness.png)

So, yeah. In-process shell, in-memory filesystem, virtual network, builtin tool registry, runs programs… I am not saying it is an OS. But I am not not saying it either.

## What It Is Not

Bashkit is not a Docker replacement. The sandbox boundary is "in-process Rust," not kernel isolation. If you need hard isolation against a motivated attacker with a Rust memory corruption bug, use a container. If you need soft isolation against "the LLM hallucinated a destructive command," Bashkit is way, way lighter.

Also, it is intentionally not 100% POSIX. No `exec`, no job control, no symlink following — all skipped for sandboxing reasons. No raw sockets either, HTTP only via allowlisted builtins. These are features, not bugs.

## How to Try It

Bashkit ships in three flavors:

- Rust crate: `cargo add bashkit`
- Python package: `pip install bashkit`
- npm package: `npm install @everruns/bashkit` (works on Node, Bun, Deno)

The minimal Rust example is short enough to fit in a tweet:

```rust
use bashkit::Bash;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mut bash = Bash::new();
    let result = bash.exec("echo hello from bashkit").await?;
    println!("{}", result.stdout);
    Ok(())
}
```

That is it. No setup, no config, no container — just a `Bash` struct and you are running shell commands inside your Rust process.

For Python folks, Bashkit also plugs straight into LangChain DeepAgents. Drop-in backend, full sandboxed bash for your agent, no extra glue:

```python
from deepagents import create_deep_agent
from bashkit.deepagents import BashkitBackend

backend = BashkitBackend()

agent = create_deep_agent(
    model="claude-opus-4-7",
    backend=backend,
    middleware=[backend.create_middleware()],
    system_prompt="You are a coding assistant with a sandboxed bash environment.",
)

task = "Create /home/user/hello.py that prints hello, then show the file."

await agent.ainvoke(
    {"messages": [{"role": "user", "content": task}]},
    config={"recursion_limit": 30},
)
```

Agent gets full bash, full VFS, fully sandboxed. Nothing touches your real disk.

More examples for all three languages live in the repo: [github.com/everruns/bashkit](https://github.com/everruns/bashkit/).

Closest thing in spirit is Vercel's [just-bash](https://github.com/vercel/just-bash) for TypeScript. Bashkit is the Rust take, with embedded Python and TypeScript, git and ssh builtins, snapshotting, and a bunch of LLM-specific tooling on top.

This is still pre-release (v0.1.x), shipping actively. If you have weird use cases, edge cases, or a bash script that breaks, please welcome — send it over, that is how this gets better.

Bashkit: the bash your LLM deserves :)
