Strands Shell
Agents run shell commands in tight loops: install dependencies, run tests, grep for errors, iterate. Those loops need to be fast, and they need to be contained. An agent that can run curl can also read your cloud credentials, reach your internal network, and overwrite files you never meant to expose.
Strands Shell is a Bourne-compatible shell that runs inside your own process. It ships grep, sed, jq, curl, find, and dozens of other commands without ever calling fork, exec, or a raw syscall. You declare what the agent can reach (files, URLs, credentials) up front, and everything else does not exist as far as the agent is concerned.
Why a virtual shell
Section titled “Why a virtual shell”Most agent setups reach for a container or a cloud sandbox to isolate command execution. Both work, and both cost you something on every command: a Docker container adds a cold start and a daemon to manage, a cloud sandbox adds a network round trip and a platform to depend on. When an agent runs hundreds of commands per task, that overhead compounds.
Strands Shell takes a different position. The isolation boundary is an in-process virtual filesystem and a mediation layer, not an operating-system primitive. There is no container to start and no VM to provision, so constructing a shell and running a command costs under a millisecond.
| Docker | Cloud sandbox | Strands Shell | |
|---|---|---|---|
| Cold start | ~200ms | ~1s (network) | under 1ms |
| Isolation | Container namespace | MicroVM | In-process VFS |
| Network | iptables or sidecar | Platform policy | URL allowlist plus SSRF guard |
| Secrets | Environment variables the agent can read | Platform-specific | Injected per request, agent never sees them |
| Setup | Docker daemon | API key plus network | pip install strands-shell |
| Platforms | Linux | Cloud only | macOS, Linux, WASM |
This is a different tradeoff, not a strictly better one. A container isolates at the kernel; Strands Shell isolates at the process. The security model section below draws that line precisely, because picking the wrong tool for an adversarial workload is a real risk.
How it works
Section titled “How it works”Your code talks to the shell through one of three surfaces: an MCP server, the Python API, or the Node.js API. Every command, file read, and network request flows through the Kernel, which is the single mediation boundary.
flowchart TB agent["Your agent code"] agent -->|"MCP, Python, or Node.js"| shell
subgraph shell ["Strands Shell"] direction TB subgraph kernel ["Kernel (mediation boundary)"] vfs["VFS: isolated filesystem"] net["Network: SSRF guard plus allowlist"] creds["Credentials: injected per URL"] limits["Limits: timeout, output, fds"] end engine["Shell engine: parser, builtins, commands, Lua"] endThe engine parses and runs shell syntax: pipelines, loops, functions, subshells. When a command needs to touch the outside world, it asks the Kernel, and the Kernel decides. The filesystem is an in-memory VFS with explicit bind mounts. Network access goes through an SSRF guard that blocks private address ranges by default. Credentials are injected per request and stripped before the agent can read them.
State persists across run calls. Environment variables, the working directory, and shell functions set in one command are visible in the next, so an agent can cd into a directory and keep working there across turns.
Strands Shell is written in Rust and compiles from one source to native bindings for Python (via PyO3) and Node.js (via napi-rs), plus a WASM target. The three language surfaces are intentionally parallel: the same config shape, the same command set, the same mediation guarantees.
What you control
Section titled “What you control”A fresh shell is an empty sandbox: no files, no network, no credentials. You grant access explicitly through three mechanisms.
Binds map a host directory into the shell’s filesystem. A copy bind snapshots the directory at construction time, isolating the agent from your live files. A direct bind passes reads and writes through to the host in real time. Prefer copy for source code and reserve direct for output directories.
Credentials attach a secret to a URL prefix. When a command makes a request to a matching URL, the Kernel injects the credential at request time. The agent never holds the secret, and the Kernel never re-injects it on a redirect, even back to the same host.
Allowed URLs widen the network policy. By default the SSRF guard blocks private ranges (RFC1918, link-local, loopback, and cloud metadata endpoints) while letting public URLs through. Add a prefix to the allowlist to permit a specific internal host.
The Configuration guide covers each of these in depth, including the TOML format that lets you declare the whole policy in a file.
What Strands Shell is not
Section titled “What Strands Shell is not”Strands Shell is a mediation layer, not a hardened sandbox. The Kernel enforces what the agent should reach through deny-by-default policy, and it runs in the same process as your code. It does not protect against memory-safety exploits in the shell engine itself, timing side channels, or an attacker who already controls the host process.
The distinction matters for your threat model:
- For “my agent should not touch anything I have not explicitly allowed,” the Kernel handles it. This is the common case: a coding agent, a research agent, a CI assistant.
- For “an untrusted tenant is running arbitrary adversarial code,” you need OS-level isolation. Run each Strands Shell instance inside a container or microVM, and let the Kernel handle the in-process mediation on top.
Resource limits (timeouts, output caps, file-descriptor and inode limits) are best-effort. They stop a runaway agent from filling memory or hanging forever; they do not stop someone actively trying to break out. For hard guarantees, use OS-level cgroups.
A Strands Shell instance is single-owner. If you serve multiple agents, create one shell per session. Construction is cheap (no containers, no VMs, just an in-memory VFS), so spinning one up per request is the intended pattern, not a workaround.
Next steps
Section titled “Next steps”- Quickstart: install the shell and run your first sandboxed command through MCP, Python, or Node.js.
- Configuration: bind directories, inject credentials, set the network allowlist, and load it all from TOML.
- Commands: the full command inventory, supported flags, and known gaps versus GNU coreutils.
- MCP Server: expose the shell to any MCP-compatible agent framework over stdio.
- Security Model: the Kernel boundary, the SSRF guard, credential handling, and how to layer OS isolation for adversarial workloads.