vbash 0.1.0

A virtual bash environment for AI agents
Documentation

vbash

A virtual bash environment for AI agents. Runs bash scripts in-process with an in-memory filesystem. No real shell, no real files unless you opt in.

Inspired by just-bash by Vercel Labs.

use vbash::Shell;

let mut shell = Shell::builder()
    .file("/data/names.txt", "alice\nbob\ncharlie")
    .build();

let result = shell.exec("cat /data/names.txt | sort | head -n 2").unwrap();
assert_eq!(result.stdout, "alice\nbob\n");

What's included

Bash syntax: variables, arrays, pipes, redirections, loops, conditionals, functions, subshells, arithmetic, globs, heredocs, brace expansion, and more.

115+ built-in commands including full sed, awk, and jq interpreters, plus grep, sort, find, tar, curl (behind feature flag), and the usual coreutils.

What's not included

  • select and coproc statements
  • Background jobs run synchronously
  • Some jq edge cases may differ from the C implementation

Custom commands

use vbash::{Shell, CommandContext, ExecResult, Error};
use std::collections::HashMap;

fn greet(args: &[&str], _ctx: &mut CommandContext<'_>) -> Result<ExecResult, Error> {
    let name = args.first().copied().unwrap_or("world");
    Ok(ExecResult {
        stdout: format!("hello {name}\n"),
        stderr: String::new(),
        exit_code: 0,
        env: HashMap::new(),
    })
}

let mut shell = Shell::builder().command("greet", greet).build();
shell.exec("greet alice").unwrap();

You can also call shell.register_command("name", func) after building.

Filesystem backends

By default everything lives in memory. You can also read from a real directory (writes still stay in memory), or go full read-write on the host filesystem.

use vbash::{Shell, OverlayFs};

let shell = Shell::builder()
    .fs(OverlayFs::new("/path/to/project").unwrap())
    .build();
Backend Reads from Writes to Typical use
InMemoryFs Memory Memory Testing, sandboxing
OverlayFs Disk, then memory Memory Read real files safely
ReadWriteFs Disk Disk Full host access
MountableFs Routed Routed Mix backends per path

Limits

Scripts run inside configurable limits. If any limit is exceeded the call returns an error.

use vbash::{Shell, ExecutionLimits};

let mut shell = Shell::builder()
    .limits(ExecutionLimits {
        max_loop_iterations: 1000,
        max_command_count: 5000,
        ..ExecutionLimits::default()
    })
    .build();

Cancellation

Cancel a running script from another thread, or set a timeout:

use vbash::Shell;
use std::time::Duration;

let mut shell = Shell::new();
let r = shell.exec_with_timeout("sleep 100", Duration::from_secs(1));
assert!(r.is_err());

Network access

Disabled by default. Enable with the network feature flag and configure which URLs are allowed:

use vbash::{Shell, NetworkPolicy};

let mut shell = Shell::builder()
    .network_policy(NetworkPolicy {
        allowed_url_prefixes: vec!["https://api.example.com/".into()],
        ..NetworkPolicy::default()
    })
    .build();

Private IPs are blocked by default. Redirect targets are re-validated.