Launches commands under in a macOS or Linux sandbox and only allows writes beneath the directory where it was started
Developer workflow and release notes for sbrun.
src/main.rs: CLI entrypointsrc/lib.rs: shared runtime (CLI dispatch, sandbox orchestration, env setup)src/cli.rs: argument parsing and help textsrc/admin.rs: --kernel-install implementationsrc/prompt.rs: --prompt-init shell hook generationsrc/sandbox.rs: platform dispatcher (#[cfg] selects backend)src/sandbox_macos.rs: macOS Seatbelt FFI bridgesrc/sandbox_linux.rs: Linux user/mount namespace sandboxsrc/profile.rs: Seatbelt profile generation (macOS only)src/config.rs: TOML config loadingsrc/pathutil.rs: path resolution and validationsrc/host.rs: host info detection (shell, home, user)src/error.rs: error typestests/test_sbrun.py: pytest integration teststools/test.sh: standard local verification entrypointBuild a debug binary and install the sbrun CLI into the active venv:
./tools/local-build.sh
Or manually:
python -m pip install -e '.[dev]' # dev deps (pytest, maturin)
cargo build # produces target/debug/sbrun (used by the tests)
maturin develop # installs the sbrun binary onto the venv PATH
Run the full local verification suite with:
tools/test.sh
That runs:
cargo test — Rust unit tests (CLI parsing, env helpers, config, path utils, host detection)cargo buildpytest -q tests/test_sbrun.py — integration tests (sandbox enforcement, config, environment)Tests run on both macOS and Linux.
GitHub Actions runs the full suite on macOS from .github/workflows/test.yml on pushes to main.
On GitHub-hosted Linux it only runs cargo test and cargo build, because the hosted environment blocks the
user-namespace setup needed for the Linux sandbox integration tests. Use a self-hosted Linux runner, or any Linux
environment whose policy allows unprivileged user and mount namespaces, for full Linux integration coverage.
The canonical version lives in Cargo.toml.
Bump the patch version with:
ship-rs-bump
Push a tag like v0.0.3 to trigger the GitHub Actions release workflow in
.github/workflows/release.yml.
The workflow builds on both macOS and Linux in parallel:
target/release/sbrunsbrun binary packaged for pip install, via maturin bin bindings) with maturin build --release --stripsbrun-v0.0.3-macos-arm64.tar.gz,
sbrun-v0.0.3-linux-x86_64.tar.gz)For the local release flow:
ship-rs-bumpship-rs-releasePublish the Python package with:
maturin publish --release --strip
The CI workflow publishes both macOS and Linux wheels to PyPI automatically.
macOS: sandbox is applied via Seatbelt (libsandbox). Requires macOS.
Linux: default sandbox uses unprivileged user namespaces + mount
namespaces (inspired by bubblewrap).
When the native sbrun binary is installed setuid root, the same binary
automatically switches to a privileged mount-namespace backend instead and
drops back to the caller before exec(). Default unprivileged mode still
requires kernel.unprivileged_userns_clone=1 (default on most distros). The
CLI also supports sudo sbrun --kernel-install, which writes
/etc/sysctl.d/90-sbrun.conf and runs sysctl --system on Linux.