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/dist/sbrunsbrun binary packaged for pip install, via maturin bin bindings) with maturin build --profile distsbrun-v0.0.3-macos-arm64.tar.gz, sbrun-v0.0.3-linux-x86_64.tar.gz)For the local release flow:
ship-release - it tags the version already in Cargo.toml, pushes branch and tag (CI publishes), then bumps and pushes the bumpNote: this repo previously bumped before releasing, so on the first use of this flow check that Cargo.toml carries the version you intend to ship (one above the last release); if not, run ship-bump, review, and commit first.
Publish the Python package with:
maturin publish --profile dist
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.