stonewall.sh
Stonewall

stonewall.sh

Kernel-enforced sandbox for AI coding agents

Build Release OpenSSF Scorecard Security Rating License: MIT

Available on:

Linux macOS


🧱 What it is

AI, like everything else, can and should not “secure itself”. Most agentic security harnesses are a joke. Likewise, prompts, plugins, and markdown files don’t provide real security. They are best-effort recommendations that agents can hallucinate about, or even ignore in the worst case.

Stonewall is a local, kernel-enforced sandbox for AI coding agents. It enforces access to files and tools independently of agent prompts or configuration, based on configurable policies:

⚡ Usage

stonewall [options] <agent> [arguments]

Basically, stonewall runs any coding agent (i.e. claude, codex or qwen) in a local sandbox. Options go before the agent. Everything after the agent name is passed to it untouched. Usage is as simple as:

stonewall claude

📦 Installation

Simple install the latest release for your platform using the installer:

curl -fsSL https://stonewall.sh/install.sh | sh

The script is short, read it first.

Alternatively, download the binary for your platform from the latest release, or build from source with Go 1.27 or newer:

go install github.com/stonewall-sh/stonewall/v2@latest

📜 Policy

Stonewall works based on a policy called .stonewall.yml, at your project root. It is automatically created on first run and contains rules for binaries, paths and project directories being accessible from within the sandbox. See the following example:

include:
  - https://stonewall.sh/policies/base.yml  # remote policy include, see below
  - ~/.stonewall/policies/corporate.yml     # local policy include
project:
  hidden:
    - .env.local        # local override for a hidden project file
  writable:
    - .git
bin:
  allowed:
    - sh                # whitelist of allowed binaries
    - git
    - npm
    - npx
expose:
  write:
    - ~/.npm            # directories outside the project that should be accessible

Include Policies

Every policy can include other policies, using an include property. Included policies can be local file paths or remote URLs. Stonewall.sh provides official remote policies. Keep in mind that they come without any warranties. The CLI will ask you to review remote policies before using them.

You can use the interactive policy picker to include official policies:

stonewall policy pick

To manage any other remote policy, you can use the following:

stonewall policy include https://stonewall.sh/policies/claude.yml # include a remote policy
stonewall policy update # updates & cache remote policies
stonewall policy --help # show help for all policy commands

Additionally use the CLI to scaffold your own include policies:

stonewall policy create

⚙️ How it Works

Inside the sandbox:

PathLinux (bubblewrap)macOS (sandbox-exec)
Projectread-writeread-write
project.readonly entriesmounted read-onlywrites denied
project.hidden directoriesappear emptyreads and writes denied
project.hidden filesread as emptyreads and writes denied
$HOMEempty, except expose entriesdenied, except expose entries
expose.read entriesread-only bindreads allowed, writes denied
/usr, /etc, /opt, …read-onlyunchanged, read-write
PATHa directory of allowlisted programs onlythe same directory
Executing binarieskernel-denied unless in bin.allowedkernel-denied unless in bin.allowed
Networkshared with the hostshared with the host

Linux makes hidden content vanish. macOS still lists the names but denies every access. Both stop the agent from reading the secret.

Linux builds the sandbox from nothing and mounts in only what is listed. macOS starts from the full system and denies your home directory, so locations such as /opt/homebrew stay writable there.

Exec itself is kernel-enforced, not just PATH: a binary outside bin.allowed is denied even by absolute path. An allowlisted interpreter such as bash or python can still run anything given to it, though.

Known limitations

🛠️ Development

make build       # ./stonewall
make test        # unit tests
make e2e         # end-to-end on this machine, macOS or Linux
make e2e-linux   # same, inside Docker with bubblewrap (runs --privileged)
make site        # website preview at http://localhost:1313

main.go is the CLI. internal/policy finds the root, loads and merges a policy with its includes, and scaffolds one. internal/sandbox resolves a policy into a Plan of absolute host paths and renders it as bwrap arguments or a Seatbelt profile. Both renderers are pure functions with golden tests. test/e2e.sh runs the same assertions on both platforms.

site/ is the Hugo project behind stonewall.sh. It mounts the README and policies/ from the repository root, so the policy pages and policies/_index.yml are generated from the yml files; nothing is copied.

--dry-run shows exactly what the backend receives. Start there when something is unexpectedly blocked or visible.

Release. CI runs gofmt, go vet, the unit and end-to-end tests on Ubuntu and macOS, and a snapshot cross-build on every push. To release, tag and push:

git tag v2.1.0 && git push origin v2.1.0

The same checks run again, then GoReleaser builds Linux and macOS binaries for amd64 and arm64 and publishes them with checksums as a GitHub release. The tag is the only version source: it is stamped into the release binaries and reported by go install builds. A local make build says dev. A new major version needs the module path suffix bumped, for example stonewall/v2 to stonewall/v3, in go.mod and the imports.

🤝 Contributing

Issues and pull requests are very welcome, be it new / updated policies or actual CLI features.

⚖️ License

Released under the MIT License.