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/kvmaccessible to the running user - Tools:
firecracker(≥ v1.15),nftables,iptables,systemd-journald(forjournalctl -k),docker(for building rootfs images),curlorwget - Toolchain: Rust ≥ 1.74 with the
x86_64-unknown-linux-musltarget 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:
BUNSEN_CORE_BINenvironment variable (a full argv string, space-separated)bunsen/bin/bunsen-coreadjacent to the installedbunsen/package (the published-wheel layout)target/release/bunsen-corewalking up from the installedbunsen/package (cargo dev build)bunsen-coreon$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 glossarydocs/adr/— architectural decisions (ADR-0001…0011)docs/adapter-contract.md— how to implement a new Adapterdocs/macos.md— macOS / remote Linux setup
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bunsen-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: bunsen-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.3 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6d4e4836bb83e8981ee97d83166de3bad80a12d765650f393ae7a27a8bd05e00
|
|
| MD5 |
7d1f38860aae282ac744d7a2ae827939
|
|
| BLAKE2b-256 |
d216c01d0d6288b6cd4688f655096aff0ff9a0ded5626b4c5c182ae1e78249b4
|
Provenance
The following attestation bundles were made for bunsen-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on xenolf/bunsen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bunsen-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
6d4e4836bb83e8981ee97d83166de3bad80a12d765650f393ae7a27a8bd05e00 - Sigstore transparency entry: 1702030776
- Sigstore integration time:
-
Permalink:
xenolf/bunsen@8ed2e5a4bc82c4e2ff461b0f87ebf89450432b11 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/xenolf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8ed2e5a4bc82c4e2ff461b0f87ebf89450432b11 -
Trigger Event:
push
-
Statement type:
File details
Details for the file bunsen-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: bunsen-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
40392b9e77378f52c25abb1a58bba5909b3f3c266107800f6f5fb3a676327fb0
|
|
| MD5 |
61b6eb4252f9cbf4beff58e230bd9261
|
|
| BLAKE2b-256 |
d4c4d98f9cfa15d866eb8f5d0d5c5eea6aacb9e5ce84991c7406948166a0ec13
|
Provenance
The following attestation bundles were made for bunsen-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on xenolf/bunsen
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bunsen-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
40392b9e77378f52c25abb1a58bba5909b3f3c266107800f6f5fb3a676327fb0 - Sigstore transparency entry: 1702030787
- Sigstore integration time:
-
Permalink:
xenolf/bunsen@8ed2e5a4bc82c4e2ff461b0f87ebf89450432b11 -
Branch / Tag:
refs/tags/v0.3.4 - Owner: https://github.com/xenolf
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8ed2e5a4bc82c4e2ff461b0f87ebf89450432b11 -
Trigger Event:
push
-
Statement type: