Skip to content
strands-agentsPublic

About

Run AI agents in a sandbox that restricts what they can execute, read, write, and reach on the network. Box combines OS isolation with default-deny Dogwood policies and credential injection that keeps secrets outside the agent. Written in Rust. Supports macOS on Apple silicon and Linux.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

512 stars

Watchers

1 watching

Forks

Strands Box

An open source sandbox engine for AI agents.

Commit Activity Open Issues Open PRs Discord

Overview

Strands Box is an open source sandbox for AI agents. It combines operating-system isolation with semantic policies written in Dogwood, so you can control what agents can access and the conditions under which they can act.

Box is harness-agnostic. You choose the agent program or binary to run, configure its access in box.toml, and write its policies in policy.dw.

The host OS enforces the direct access restrictions in box.toml. Strands Shell, Monty for Python, the egress gateway, and the MCP broker send the operations they handle to the embedded Dogwood Local Engine, which evaluates the rules in policy.dw. Box enforces its allow or deny decisions outside the agent process.

Dogwood uses permit and forbid rules. Operations checked by the engine are denied by default: a matching permit must allow the operation, and a matching forbid overrides that permission.

Box's interpreters and gateways share an event history, so rules can use earlier actions and elapsed time to decide what is allowed next. For example, a file read through Shell or Python can cause the gateway to deny a later outbound HTTP request.

Preview: Box supports local execution on Linux ARM64 and macOS with Apple silicon. We welcome feedback through GitHub issues. Read CONTRIBUTING.md before submitting a change.

Key capabilities

  • File, program, and network restrictions. The agent's sandbox limits its direct access. Box reports the configured grants at startup.
  • Semantic and temporal policies. Dogwood rules can depend on the requested operation, its arguments, earlier actions, and elapsed time. Operations checked by the policy engine are denied unless a rule permits them.
  • Policy across code and tools. Strands Shell, Monty for Python, the egress gateway, and the MCP broker use one policy engine and event history. A file read through one interpreter can affect whether a later network request is allowed.
  • Request and tool-call checks. The egress gateway checks connections and HTTP requests, including their method and path. The MCP integration checks configured tool calls and their arguments.
  • Credential injection. The gateway authenticates permitted requests with configured API credentials or AWS SigV4 signing. The agent does not receive the underlying secrets.
  • Decision records. Box records policy decisions in OTLP JSON. By default, records go to <box_dir>/private/telemetry/records.jsonl.

Direct filesystem grants in box.toml are enforced by the OS and do not produce individual Dogwood decisions. To apply a policy to file operations, use the interpreters and keep those files out of the agent's direct grants. See filesystem access and policy.

Use Box as a Rust library

Link the strands-box package in crates/box, then call:

let exit_code = strands_box::run("/absolute/box.toml".into(), Vec::new(), Default::default());

This runs the existing Box command in the calling process and waits until it finishes. By default, it uses the process's terminal and returns its exit code. The second argument appends arguments to [agent] command. Use RunOptions::terminal to supply the workload's terminal and RunOptions::on_ready to start host threads after hardening and signal registration, before workload setup. Call it before starting application threads or loading secrets, and use one Box workload per host process. Later run calls fail, even if the first call failed. Ship the matching Box helper binaries beside the host application.

To replace the running box's policy from a host thread:

strands_box::reload(&[strands_box::Policy {
    origin: "policy.dw".into(),
    text: new_policy_text,
}])?;

reload waits until the change takes effect. Invalid policy returns ReloadRefused and leaves the current policy in place. An empty list denies all policy requests. Policy files and the OS sandbox stay unchanged. A box must be open; on_ready runs too early. Use ReloadRefused::is_not_running() to distinguish that case from a rejected policy. A rule for an MCP tool can reload only after Box loads that server's tool definitions. Without them, even reloading the unchanged policy is refused. If policy blocks discovery, waiting will not help. The current policy stays in place.

To generate policy schemas:

let exit_code = strands_box::generate_policy_schema(
    "/absolute/box.toml".into(),
    "/absolute/schemas".into(),
);

Architecture

Box runs the Dogwood Local Engine and its enforcement components outside the agent's sandbox, in either the Box command's process or the calling Rust application's process. Strands Shell, Monty, the egress gateway, and the MCP broker check operations with the engine before allowing them. They also record events that later policy decisions can use.

flowchart LR
    subgraph sandbox["Agent OS sandbox"]
        agent["Agent application"]
    end

    net["network requests"]:::label
    py["Python code"]:::label
    sh["shell commands"]:::label
    mc["local MCP calls"]:::label

    subgraph trusted["The box's trusted process (outside the agent sandbox)"]
        proxy["Egress gateway"]
        monty["Monty for Python"]
        shell["Strands Shell"]
        mcp["MCP broker"]
        policy[["Dogwood Local Engine and event history"]]
    end

    agent --- net --> proxy
    agent --- py --> monty
    agent --- sh --> shell
    agent --- mc --> mcp
    proxy -->|"policy checks"| policy
    monty -->|"policy checks"| policy
    shell -->|"policy checks"| policy
    mcp -->|"policy checks"| policy

    classDef label fill:none,stroke:none;
Loading

When the agent uses a program such as git or cargo, or a local MCP server, Box checks the launch against policy and runs the program in its own sandbox. You configure which files each program can read or change. The agent can send requests to Box's interpreters, but external programs and local MCP servers cannot. Box receives their output and exit status, and their network traffic goes through Box's gateway by default. See the security model for details.

For each part, where it runs, what it decides, and how one request moves through them, read Box architecture.

Getting started

Follow the getting-started guide to download Box for macOS or Linux ARM64. The Strands CLI walkthrough uses macOS. For that walkthrough, you need macOS 15 or later on Apple silicon and Node.js 22.21 or later from Homebrew. You also need access to Claude Opus 5 on Amazon Bedrock in us-west-2.

The download script checks the release checksum and unpacks the binaries into ./box-core. It does not change your PATH or system directories:

curl -fsSL https://raw.githubusercontent.com/strands-agents/box/main/download.sh | sh
./box-core/box --version

The downloader selects macOS Apple silicon or Linux ARM64 archives. For Linux, select a release that includes box-<version>-linux-arm64.tar.gz and its SHA256SUMS.txt entry. Set BOX_VERSION to that release tag when running the script. Set BOX_DOWNLOAD_DIR to change the destination. A release can contain macOS assets only; check its assets if a Linux download fails.

The script verifies the checksum and extracts into temporary storage before changing destination files. Linux packages must contain executable box, strands-box-sock-alias, and strands-box-contain-trampoline files. The Linux package requires glibc 2.34 or newer and the GCC runtime library (libgcc_s.so.1). Before replacing files, the downloader runs the candidate's box --version in a private directory inside an existing destination. For a new destination, it uses the parent directory. This staging location must be writable and allow execution. Temporary download storage can prohibit execution (noexec). A failed version check reports the loader error and leaves existing destination files unchanged. The final extraction can partially update the destination if it fails. Use a fresh BOX_DOWNLOAD_DIR to retain a previous installation.

Keep the entire download together in ./box-core, including Box.app in signed macOS packages. You can also build from source. Then follow the guide to write box.toml and policy.dw, and run the box.

Change what the box allows

Edit box.toml to change the agent's environment and direct access grants. Edit policy.dw to change the rules for operations Box checks through its interpreters and gateways. You can give your coding agent the policy-authoring skill for help with Dogwood syntax and Box's supported actions:

https://raw.githubusercontent.com/strands-agents/box/main/.agents/skills/authoring-box-policy/SKILL.md

Examples

Run the Strands Python SDK example for a small agent whose three tools use Shell.

See the example index for dependencies and run instructions.

Build from source

This is a Cargo workspace. rust-toolchain.toml pins the compiler, and rustup installs it on the first build. Build the box binaries from a clone with rustup:

git clone https://github.com/strands-agents/box.git
cd box
cargo build --release -p strands-box -p strands-box-containment

Run the compiled binary as ./target/release/strands-box. Keep its helper binaries in the same directory.

The release guide covers version selection and publication for macOS and Linux ARM64.

Linux workspace tests run on Ubuntu ARM64 with user, mount, and process namespaces enabled. Unexpected prerequisite skips fail the job. The accepted alias-read limitation and missing credential-daemon fixture remain explicit in the report. Deterministic integration failures on either platform fail CI Gate.

The macOS release guide covers the native-feature build, automated signing, notarization, package verification, and release gates.

The fuzzing guide covers the long libFuzzer run of the tool grammar shell parser.

Common tasks run through just:

just build         # the CLI, the alias image, and the containment trampoline
just test          # workspace tests
just check         # pre-push gate: fmt-check + clippy + test
just test-all      # workspace tests and box example checks

cargo test --workspace --all-features runs the full suite. Include --all-features to run the box_shell and box_credentials suites, which require the test-support feature.

In pull-request CI, changes to docs/site-manifest.json, docs/site-manifest-retirements.json, or a publishable page under docs/user/ run the focused site-manifest contract test instead of the full Rust test matrix. The test compares the base contract to keep the product ID stable and require an append-only retirement before a page or route is removed.

Test the release downloader

Run python3 -m unittest discover -s scripts -p 'test_*.py' -v to test release packaging and the downloader. Continuous integration runs the downloader tests on Linux and macOS. These tests use local fixture archives and simulated HTTP clients; they do not qualify a published binary or its containment behavior. On Linux, the mount test requires util-linux and permission to create a user and mount namespace. It creates a noexec temporary mount only inside the test process's private namespace.

Documentation

Guide Contents
docs/user/ How to install Box and run a box.
docs/design/ Architecture, enforcement boundaries, and design decisions.

See AGENTS.md for the repository rules.

Reporting a problem

Open an issue on this repository. For a suspected security problem, read SECURITY.md first.

License

Apache License 2.0.

About

Run AI agents in a sandbox that restricts what they can execute, read, write, and reach on the network. Box combines OS isolation with default-deny Dogwood policies and credential injection that keeps secrets outside the agent. Written in Rust. Supports macOS on Apple silicon and Linux.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

512 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages