Skip to main content

Python library for orchestrating coding-agent Runs inside Firecracker microVMs

Project description

bunsen

A Python library for orchestrating Coding Agent Runs inside Firecracker microVMs. Bunsen launches an agent (Claude Code, aider, …) inside a sandbox, streams its output as a normalised event stream, and enforces a default-deny network egress policy.

See CONTEXT.md for the domain glossary and docs/adr/ for the architectural decisions behind v1.

Status

v1 runs on Linux + KVM. Apple Silicon developers should SSH to a remote Linux box — see docs/macos.md for setup notes.

Requirements

  • OS: Linux x86_64 with /dev/kvm accessible to the running user
  • Tools: firecracker (≥ v1.15), nftables, iptables, systemd-journald (for journalctl -k), docker (for building rootfs images), curl or wget
  • Toolchain: Rust ≥ 1.74 with the x86_64-unknown-linux-musl target installed (rustup target add x86_64-unknown-linux-musl)
  • Python: 3.11+

A Hetzner CCX13 (or any cloud instance that exposes nested virtualization) is enough for a Run.

Install

git clone https://github.com/<you>/bunsen.git
cd bunsen

# 1. Build the host binary and the in-guest init
cargo build --release
cargo build --release -p bunsen-init --target x86_64-unknown-linux-musl

# 2. Fetch the pinned Firecracker guest kernel (~30 MB, cached at
#    ${XDG_CACHE_HOME:-~/.cache}/bunsen/kernel/vmlinux).
./kernel/fetch-vmlinux.sh

# 3. Install the Python library (editable install from repo root; maturin backend)
python -m venv .venv && source .venv/bin/activate
pip install -e .

# 4. (Optional) build the smoke-test rootfs so you can run end-to-end
#    without pulling an OCI image.
./adapters/_smoke-test/build-rootfs.sh

After step 4, the rootfs lives at target/smoke-rootfs.ext4.

bunsen-core discovery

The Python library finds the bunsen-core host binary in this order:

  1. BUNSEN_CORE_BIN environment variable (a full argv string, space-separated)
  2. bunsen/bin/bunsen-core adjacent to the installed bunsen/ package (the published-wheel layout)
  3. target/release/bunsen-core walking up from the installed bunsen/ package (cargo dev build)
  4. bunsen-core on $PATH

If none match, bunsen.run(...) raises FileNotFoundError with all four options.

First Run

import asyncio, bunsen

async def main():
    spec = {
        "adapter": "black-box",
        "cmd": ["sh", "-c", "echo hello from inside the sandbox"],
        "workspace-disk-mb": 128,
    }
    async with bunsen.run(spec) as r:
        async for event in r.events:
            print(event)

asyncio.run(main())

To run it under a real Firecracker sandbox, point BUNSEN_CORE_BIN at the binary plus the kernel/rootfs flags:

export BUNSEN_CORE_BIN="$(pwd)/target/release/bunsen-core \
    --kernel ${XDG_CACHE_HOME:-$HOME/.cache}/bunsen/kernel/vmlinux \
    --rootfs $(pwd)/target/smoke-rootfs.ext4"
python examples/hello.py

On a stock Ubuntu host with UFW enabled, also pass manage_firewall=True so bunsen can install a per-TAP allow rule for the lifetime of the Run:

async with bunsen.run(spec, manage_firewall=True) as r:
    ...

A Run's outputs land under ${XDG_RUNTIME_DIR:-/tmp}/bunsen/runs/<run_id>/: the normalised transcript (transcript.jsonl), the materialised workspace (workspace/), and any agent-native history files (agent-history/).

Running the tests

cargo test                              # host-side Rust + bunsen-init unit tests
cargo check --target x86_64-unknown-linux-musl -p bunsen-core   # cross-check
pip install -e '.[dev]' && pytest -q python/tests                  # Python unit tests

# Acceptance suite (Linux + KVM required)
BUNSEN_KERNEL=~/.cache/bunsen/kernel/vmlinux \
BUNSEN_ROOTFS=$(pwd)/target/smoke-rootfs.ext4 \
pytest -v python/tests/test_egress_acceptance.py

Inspecting a Pool ref

After a Session runs an agent, the agent's commits live in the Session's Pool — a bare git repo at ~/.local/share/bunsen/sessions/<id>/pool/. There is no host-side workspace tree under runs/<run-id>/ to browse (see ADR-0010). To inspect files at a Pool ref (an audit ref like runs/<run-id>, or the user-named output_branch):

SESSION_DIR=~/.local/share/bunsen/sessions/<session-id>
git -C "$SESSION_DIR/pool" worktree add /tmp/inspect-<run-id> runs/<run-id>
# ...inspect files under /tmp/inspect-<run-id>...
git -C "$SESSION_DIR/pool" worktree remove /tmp/inspect-<run-id>

A bunsen inspect <run-id> ergonomic wrapper is intentionally not built — see the PRD's "Out of Scope" section.

Documentation

  • CONTEXT.md — domain glossary
  • docs/adr/ — architectural decisions (ADR-0001…0011)
  • docs/adapter-contract.md — how to implement a new Adapter
  • docs/macos.md — macOS / remote Linux setup

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

bunsen-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

bunsen-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.2 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

File details

Details for the file bunsen-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for bunsen-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f02b15fab84a1d30affc224686573eef972a6f03bb1e8671b527cdd3481e1488
MD5 19d7bca5151b048e0489a0acb9130b58
BLAKE2b-256 f7b2e6fe94cf116f6b48edc0bc957681cfe5cba79d8980657ccae15effdb143c

See more details on using hashes here.

Provenance

The following attestation bundles were made for bunsen-0.3.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on xenolf/bunsen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file bunsen-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for bunsen-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 65b5a744a3efea55aa2c36fbc1a5fd1e5bfb0987a95434fe59a0dd73cdbfc30a
MD5 48ae4e4e4fa943e4604730defb21c116
BLAKE2b-256 321c82366d58fd731fb0dc1f1ff1c7d3e180be8fea771783de081e3d9719d47e

See more details on using hashes here.

Provenance

The following attestation bundles were made for bunsen-0.3.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on xenolf/bunsen

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page