rust-bash
A sandboxed bash interpreter for AI Agents, built in Rust. Execute bash scripts safely with a virtual filesystem — no containers, no VMs, no host access.
Status: alpha / Milestones 1–7 Complete — Core interpreter, text processing, execution safety, filesystem backends, CLI binary, C FFI, WASM target, npm package, AI SDK integration, shell language completeness, and command coverage are implemented.
🌐 Try it in the browser →
Interactive showcase with 80+ commands running via WASM. Includes an AI agent you can talk to. See examples/website/ for the source.
Highlights
- Virtual filesystem — all file operations happen in memory by default. No host files are touched.
- 80 commands — echo, cat, grep, awk, sed, jq, find, sort, diff, curl, and many more.
- Full bash syntax — pipelines, redirections, variables, control flow, functions, command substitution, globs, brace expansion, arithmetic, here-documents, case statements.
- Execution limits — 10 configurable bounds (time, commands, loops, output size, call depth, string length, glob results, substitution depth, heredoc size, brace expansion).
- Network policy — sandboxed
curlwith URL allow-lists, method restrictions, redirect and response-size limits. - Multiple filesystem backends — InMemoryFs (default), OverlayFs (copy-on-write), ReadWriteFs (passthrough), MountableFs (composite).
- npm package —
rust-bashwith TypeScript types, native Node.js addon, and WASM support. - AI tool integration — framework-agnostic JSON Schema tool definitions for OpenAI, Anthropic, Vercel AI SDK, LangChain.js.
- MCP server — built-in Model Context Protocol server for Claude Desktop, Cursor, VS Code.
- Embeddable — use as a Rust crate with a builder API. Custom commands via the
VirtualCommandtrait. - CLI binary — standalone
rust-bashcommand with-c,--files,--env,--cwd,--jsonflags, MCP server mode, and an interactive REPL.
Installation
npm (TypeScript / JavaScript)
Rust crate
Build from source (Rust)
# Binary is at target/release/rust-bash
Install via Cargo
Quick Start (TypeScript)
import { Bash, tryLoadNative, createNativeBackend, initWasm, createWasmBackend } from 'rust-bash';
// Auto-detect backend: native addon (fast) or WASM (universal)
let createBackend;
if (await tryLoadNative()) {
createBackend = createNativeBackend;
} else {
await initWasm();
createBackend = createWasmBackend;
}
const bash = await Bash.create(createBackend, {
files: {
'/data.json': '{"name": "world"}',
'/script.sh': 'echo "Hello, $(jq -r .name /data.json)!"',
},
env: { USER: 'agent' },
});
const result = await bash.exec('bash /script.sh');
console.log(result.stdout); // "Hello, world!\n"
console.log(result.exitCode); // 0
Quick Start (Rust)
use RustBashBuilder;
use HashMap;
let mut shell = new
.files
.env
.build
.unwrap;
let result = shell.exec.unwrap;
assert_eq!;
assert_eq!;
Custom Commands
TypeScript
import { Bash, defineCommand } from 'rust-bash';
const fetch = defineCommand('fetch', async (args, ctx) => {
const url = args[0];
const response = await globalThis.fetch(url);
return { stdout: await response.text(), stderr: '', exitCode: 0 };
});
const bash = await Bash.create(createBackend, {
customCommands: [fetch],
});
await bash.exec('fetch https://api.example.com/data');
Rust
use ;
;
let mut shell = new
.command
.build
.unwrap;
let result = shell.exec.unwrap;
assert_eq!;
AI Tool Integration
rust-bash exports framework-agnostic tool primitives — use with any AI agent framework:
import {
bashToolDefinition,
createBashToolHandler,
formatToolForProvider,
createNativeBackend,
} from 'rust-bash';
const { handler } = createBashToolHandler(createNativeBackend, {
files: { '/data.txt': 'hello world' },
maxOutputLength: 10000,
});
// Format for your provider
const openaiTool = formatToolForProvider(bashToolDefinition, 'openai');
const anthropicTool = formatToolForProvider(bashToolDefinition, 'anthropic');
// Handle tool calls from the LLM
const result = await handler({ command: 'grep hello /data.txt' });
// { stdout: 'hello world\n', stderr: '', exitCode: 0 }
OpenAI
import OpenAI from 'openai';
import { createBashToolHandler, formatToolForProvider, bashToolDefinition, createNativeBackend } from 'rust-bash';
const { handler } = createBashToolHandler(createNativeBackend, { files: myFiles });
const openai = new OpenAI();
const response = await openai.chat.completions.create({
model: 'gpt-4o',
tools: [formatToolForProvider(bashToolDefinition, 'openai')],
messages: [{ role: 'user', content: 'Count lines in /data.txt' }],
});
for (const toolCall of response.choices[0].message.tool_calls ?? []) {
const result = await handler(JSON.parse(toolCall.function.arguments));
}
Anthropic
import Anthropic from '@anthropic-ai/sdk';
import { createBashToolHandler, formatToolForProvider, bashToolDefinition, createNativeBackend } from 'rust-bash';
const { handler } = createBashToolHandler(createNativeBackend, { files: myFiles });
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
tools: [formatToolForProvider(bashToolDefinition, 'anthropic')],
messages: [{ role: 'user', content: 'Count lines in /data.txt' }],
});
for (const block of response.content) {
if (block.type === 'tool_use') {
const result = await handler(block.input);
}
}
Vercel AI SDK
import { tool } from 'ai';
import { z } from 'zod';
import { createBashToolHandler, createNativeBackend } from 'rust-bash';
const { handler } = createBashToolHandler(createNativeBackend, { files: myFiles });
const bashTool = tool({
description: 'Execute bash commands in a sandbox',
parameters: z.object({ command: z.string() }),
execute: async ({ command }) => handler({ command }),
});
LangChain.js
import { tool } from '@langchain/core/tools';
import { z } from 'zod';
import { createBashToolHandler, createNativeBackend } from 'rust-bash';
const { handler, definition } = createBashToolHandler(createNativeBackend, { files: myFiles });
const bashTool = tool(
async ({ command }) => JSON.stringify(await handler({ command })),
{ name: definition.name, description: definition.description, schema: z.object({ command: z.string() }) },
);
See AI Agent Tool Recipe for complete agent loop examples.
MCP Server
The CLI binary includes a built-in Model Context Protocol server:
Exposed tools: bash, write_file, read_file, list_directory. State persists across calls.
Claude Desktop
VS Code (GitHub Copilot)
See MCP Server Setup for Cursor, Windsurf, Cline, and other clients.
Browser / WASM
import { Bash, initWasm, createWasmBackend } from 'rust-bash/browser';
await initWasm();
const bash = await Bash.create(createWasmBackend, {
files: { '/hello.txt': 'Hello from WASM!' },
});
const result = await bash.exec('cat /hello.txt');
console.log(result.stdout); // "Hello from WASM!\n"
Performance
| Feature | just-bash | rust-bash |
|---|---|---|
| Language | Pure TypeScript | Rust → WASM + native addon |
| Performance | JS-speed | Near-native (native addon) / WASM |
| API | new Bash(opts) |
Bash.create(backend, opts) |
| Custom commands | defineCommand() |
defineCommand() (same API) |
| AI integration | Vercel AI SDK only | Framework-agnostic (OpenAI, Anthropic, Vercel, LangChain) |
| MCP server | ❌ | ✅ Built-in (rust-bash --mcp) |
| Browser | ✅ | ✅ (WASM) |
| Node.js native | ❌ | ✅ (napi-rs) |
| C FFI | ❌ | ✅ (shared library) |
| Filesystem backends | In-memory only | InMemoryFs, OverlayFs, ReadWriteFs, MountableFs |
| Execution limits | ✅ | ✅ (10 configurable bounds) |
| Network policy | ❌ | ✅ (URL allow-list, method restrictions) |
CLI Binary
# Execute a command
# Seed files from host disk into the virtual filesystem
# Set environment variables
# Set working directory
# JSON output for machine consumption
# {"stdout":"hello\n","stderr":"","exit_code":0}
# Execute a script file with positional arguments
# Read commands from stdin
|
# MCP server mode
# Interactive REPL (starts when no command/script/stdin is given)
Interactive REPL
When launched without -c, a script file, or piped stdin, rust-bash starts an
interactive REPL with readline support:
- Colored prompt —
rust-bash:{cwd}$reflecting the current directory, green (exit 0) or red (non-zero last exit) - Tab completion — completes built-in command names
- Multi-line input — incomplete constructs (e.g.,
if true; then) wait for more input - History — persists across sessions in
~/.rust_bash_history - Ctrl-C — cancels the current input line
- Ctrl-D — exits the REPL with the last command's exit code
exit [N]— exits with code N (default 0)
Use Cases
- AI agent tools — give LLMs a bash sandbox without container overhead
- Code sandboxes — run user-submitted scripts safely
- Testing — deterministic bash execution with a controlled filesystem
- Embedded scripting — add bash scripting to Rust applications
- MCP server — provide bash execution to Claude Desktop, Cursor, VS Code
Built-in Commands
Registered commands (80)
| Category | Commands |
|---|---|
| Core | echo, cat, true, false, pwd, touch, mkdir, ls, test, [ |
| File ops | cp, mv, rm, tee, stat, chmod, ln, readlink, rmdir, du, split |
| Text | grep, egrep, fgrep, sort, uniq, cut, head, tail, wc, tr, rev, fold, nl, printf, paste, od, tac, comm, join, fmt, column, expand, unexpand, strings |
| Text processing | sed, awk, jq, diff |
| Search | rg |
| Navigation | realpath, basename, dirname, tree, find |
| Utilities | expr, date, sleep, seq, env, printenv, which, base64, md5sum, sha1sum, sha256sum, whoami, hostname, uname, yes, xargs, timeout, file, bc, clear |
| Compression | gzip, gunzip, zcat, tar |
| Network | curl (feature-gated) |
All commands support --help for built-in usage information.
Interpreter builtins (40)
exit, cd, export, unset, set, shift, readonly, declare, read, eval, source / ., break, continue, : / colon, let, local, return, trap, shopt, type, command, builtin, getopts, mapfile / readarray, pushd, popd, dirs, hash, wait, alias, unalias, printf, exec, sh / bash, help, history
Additionally, if/then/elif/else/fi, for/while/until/do/done, case/esac, ((...)), [[ ]], and time are handled as shell syntax by the interpreter.
Configuration (Rust)
use ;
use HashMap;
use Duration;
let mut shell = new
.files
.env
.cwd
.execution_limits
.network_policy
.build
.unwrap;
Execution limits defaults
| Limit | Default |
|---|---|
max_call_depth |
100 |
max_command_count |
10,000 |
max_loop_iterations |
10,000 |
max_execution_time |
30 s |
max_output_size |
10 MB |
max_string_length |
10 MB |
max_glob_results |
100,000 |
max_substitution_depth |
50 |
max_heredoc_size |
10 MB |
max_brace_expansion |
10,000 |
Filesystem Backends
| Backend | Description |
|---|---|
InMemoryFs |
Default. All data in memory. Zero host access. |
OverlayFs |
Copy-on-write over a real directory. Reads from disk, writes stay in memory. |
ReadWriteFs |
Passthrough to real filesystem. For trusted execution. |
MountableFs |
Compose backends at different mount points. |
OverlayFs — Read real files, sandbox writes
use ;
use Arc;
// Reads from ./my_project on disk; writes stay in memory
let overlay = new.unwrap;
let mut shell = new
.fs
.cwd
.build
.unwrap;
let result = shell.exec.unwrap; // reads from disk
shell.exec.unwrap; // writes to memory only
ReadWriteFs — Direct filesystem access
use ;
use Arc;
// Restricted to /tmp/sandbox (chroot-like)
let rwfs = with_root.unwrap;
let mut shell = new
.fs
.cwd
.build
.unwrap;
shell.exec.unwrap; // writes to /tmp/sandbox/output.txt
MountableFs — Combine backends at mount points
use ;
use Arc;
let mountable = new
.mount // in-memory root
.mount // overlay on real project
.mount; // separate temp space
let mut shell = new
.fs
.cwd
.build
.unwrap;
shell.exec.unwrap; // reads from disk
shell.exec.unwrap; // writes to in-memory /tmp
C FFI
rust-bash can be used from any language with C FFI support (Python, Go, Ruby, etc.) via a shared library.
Build the shared library
# Output: target/release/librust_bash.so (Linux), .dylib (macOS), .dll (Windows)
# Header: include/rust_bash.h
Minimal C example
int
For complete Python and Go examples, see examples/ffi/. For the full FFI guide, see the FFI Usage recipe.
Public API (Rust)
| Type | Description |
|---|---|
RustBashBuilder |
Builder for configuring and constructing a shell instance |
RustBash |
The shell instance — call .exec(script) to run commands |
ExecResult |
Returned by exec(): stdout, stderr, exit_code |
ExecutionLimits |
Configurable resource bounds |
NetworkPolicy |
URL allow-list and HTTP method restrictions for curl |
VirtualCommand |
Trait for registering custom commands |
CommandContext |
Passed to command implementations (fs, cwd, env, stdin, limits) |
CommandResult |
Returned by command implementations |
RustBashError |
Top-level error: Parse, Execution, LimitExceeded, Network, Vfs, Timeout |
VfsError |
Filesystem errors: NotFound, AlreadyExists, PermissionDenied, etc. |
Variable |
A shell variable with value, exported, readonly metadata |
ShellOpts |
Shell option flags: errexit, nounset, pipefail, xtrace |
ExecutionCounters |
Per-exec() resource usage counters |
InterpreterState |
Full mutable shell state (advanced: direct inspection/manipulation) |
ExecCallback |
Callback type for sub-command execution (xargs, find -exec) |
InMemoryFs |
In-memory filesystem backend |
OverlayFs |
Copy-on-write overlay backend |
ReadWriteFs |
Real filesystem passthrough backend |
MountableFs |
Composite backend with path-based mount delegation |
VirtualFs |
Trait for filesystem backends |
Documentation
- Guidebook — architecture, design, and implementation details
- Recipes — task-oriented guides for common use cases
- npm package README — TypeScript API reference
- AI Agent Guide — quick-start guide for AI agents consuming rust-bash
Roadmap
The following milestones track the project's progress:
- ✅ Milestone 1–4: Core interpreter, text processing, execution safety, filesystem backends
- ✅ Milestone 5.1: Standalone CLI binary — interactive REPL,
-ccommands, script files, stdin piping,--jsonoutput - ✅ Milestone 5.2: C FFI — shared library, generated C header, JSON config, 6 exported functions
- ✅ Milestone 5.3: WASM target —
wasm32-unknown-unknown, npm packagerust-bashwith TypeScript types - ✅ Milestone 5.4: AI SDK integration — framework-agnostic tool definitions, MCP server, documented adapters
- ✅ Milestone 6: Shell language completeness — arrays, shopt, process substitution, special variables, advanced redirections, missing builtins, differential testing
- ✅ Milestone 7: Command coverage & discoverability — 80 commands with
--help, compression/archiving, search (rg), binary inspection, command fidelity infrastructure, AI agent documentation - Planned: Embedded runtimes — SQLite, yq, Python, JavaScript (M8)
- Planned: Platform features — cancellation, lazy files, AST transforms, fuzz testing (M9)
License
MIT