sbrun

Launches commands under in a macOS or Linux sandbox and only allows writes beneath the directory where it was started

View the Project on GitHub AnswerDotAI/sbrun

Development

Developer workflow and release notes for sbrun.

Layout

Local Build

Build 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

Testing

Run the full local verification suite with:

tools/test.sh

That runs:

GitHub Actions runs the full suite on macOS arm64, Linux x86_64, and Linux aarch64 from .github/workflows/test.yml on pushes to main. The Linux jobs enable unprivileged user namespaces and allow their use under AppArmor before running the integration tests. Local Ubuntu installations need the one-time setup documented in the README’s Linux platform notes.

Versioning

The canonical version lives in Cargo.toml.

Bump the patch version with:

ship-rs-bump

Release

Push a tag like v0.0.3 to trigger the GitHub Actions release workflow in .github/workflows/release.yml.

The workflow builds natively on macOS arm64, Linux x86_64, and Linux aarch64 in parallel:

For the local release flow:

  1. run ship-release - it tags the version already in Cargo.toml, pushes branch and tag (CI publishes), then bumps and pushes the bump

Note: 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.

PyPI

Publish the Python package with:

maturin publish --profile dist

The CI workflow publishes macOS arm64, Linux x86_64, and Linux aarch64 wheels to PyPI automatically.

Platform notes

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.