Skip to content

Strands Shell in a box

Strands Shell is the shell that runs each command the agent sends to zsh, bash, or sh in a box. Policy decides each command, and each file operation the command makes. A program outside the Shell runs in its own sandbox, separate from the agent’s.

This page is the reference. For the steps, read Write policy for shell commands. For every field a rule reads, see the action reference.

On the agent’s PATH, zsh, bash, and sh are each the alias, a client program that sends one command to the Shell. Each accepts one of these forms:

FormThe Shell runs
zsh -c COMMAND, -lc COMMAND, -c -l COMMAND, -l -c COMMANDCOMMAND
zsh SCRIPTThe text of SCRIPT. The alias reads the file in the agent’s sandbox, so [agent.filesystem] must grant the agent a read of it.

Any other form is refused with Shell alias accepts only -c COMMAND, -lc COMMAND, -c -l COMMAND, or SCRIPT.

Each invocation is one command. A variable exported in one invocation is unset in the next. The Shell starts each command with HOME set to the agent’s HOME, and with the workspace as its working directory.

Once the alias hands a command to the Shell, each command in it runs as follows:

Inside a command, the agent runsActionWhere it runsWhat bounds its reach
A builtin or a command the Shell implementsshell:exec, then one fs:* action for each file operationIn the ShellPolicy, then the reachable paths check
A program on the PATH of the host OS’s shell, such as gitshell:spawn on the program’s resolved pathIts own sandboxIts [tool.<name>] table or exec entry, as in Configuration
A program named by a path, such as ./target/debug/appshell:spawn on the program’s resolved pathIts own sandboxIts [tool.<name>] table or exec entry

The reachable paths check is deny-only and runs after policy. It refuses a path outside the operator’s home, the agent’s home, and the workspace, a path inside the box directory, and the box.toml and policy.dw the run loaded, whatever a permit says.

The Shell implements these, each in place of a program of the same name on the PATH:

  • Builtins: cd, export, set, alias, trap, test, printf, echo, read, find, xargs, and others.
  • Text: cat, grep, sed, head, tail, sort, uniq, cut, tr, wc, tee.
  • Files: ls, cp, mv, rm, mkdir, rmdir, ln, chmod, touch, mktemp, readlink.
  • Data and network: jq, curl.
  • Interpreters: lua, and python and python3, which run in Monty, Box’s Python interpreter.

Paths outside the operator’s home, the agent’s home, and the workspace, such as /tmp, exist only in the Shell’s memory, for one command. A file written to /tmp is gone when the command ends.

  • shell:exec is decided once for each command the Shell implements, after expansion. That includes each command that command substitution, eval, source, find -exec, xargs, or Lua’s io.popen runs.
  • shell:spawn is decided once for each program the Shell doesn’t implement, before Box starts it. A permit starts the program only under the rule in Configuration. Its program_path is the file that runs, with every link resolved, and ~/ under the operator’s home.
  • fs:* is decided once for each file operation one of the Shell’s commands makes, on the path with every link resolved.

The action reference lists every field of these actions and their ::response events. To print the whole action schema for your box, run box policy generate-schema.

Policy decides whether a program outside the Shell runs. These box.toml keys decide where. After a shell:spawn permit, the program runs under the first of these that covers it:

  1. A [tool.<name>] table that matches it.
  2. An [agent.filesystem] exec entry.
  3. Neither: the program is refused with status 126.
KeyTypeDefaultMeaning
commandstring arrayrequiredElement 0 is the program, and the rest are fixed leading arguments. A bare name resolves on the table’s env.PATH, else on the PATH of the host OS’s shell that runs box run.
workspacepaththe Shell’s working directory when it is inside the agent’s workspace or a path the tool’s lists name, else the agent’s workspaceThe program’s initial working directory.
envtableemptyVariables the program receives.
filesystemtableemptyread, write, read_file, write_file, list, and deny: the paths the program’s own system calls reach.
network.contain_egressbooltrueHow the program reaches the network. See Let a tool skip the gateway.

A table matches a shell:spawn when its command resolves to the same file as program_path, and its fixed arguments match the leading arguments of the command. When several tables match, the one with the longest command is used. When a table names the file and none matches the arguments, the program is refused.

[tool.git]
command = ["git"]
[tool.git.filesystem]
read = ["~/src/project", "/Library/Developer/CommandLineTools"]
write = ["~/src/project/.git"]
deny = ["~/src/project/.env"]

The configuration reference lists every key a tool table takes.

A program that no [tool.<name>] table names runs with the agent’s own filesystem lists when an exec entry covers it and no deny entry covers it. An entry is a file, or a directory that covers every file under it.

[agent.filesystem]
exec = ["~/src/project/target/debug"]

A program that runs under an exec entry, and not a tool table, always goes through the egress gateway.

A bare program name in a command resolves on the PATH of the shell that runs box run, else on /usr/bin:/bin.

curl sends each request through the egress gateway, which decides net:connect and http:request, as Network and credentials describes. A host with no net:connect permit is refused before any HTTP is sent: curl prints curl: (6) error sending request for url (...) and exits 6. A refused http:request prints http:request gate: policy denied this operation, then the deciding rule.

LimitWhat happens past it
30 seconds per commandThe command stops, prints strands-shell: execution timeout exceeded, and exits 1.
256 KiB of command textThe Shell refuses the command with strands-shell: input too large, status 1.
10 MiB per file in the Shell’s memory, such as a file under /tmpThe write fails with file size limit exceeded (10485760 bytes).
10,000 files and directories in the Shell’s memoryCreating another fails.

The file limits apply to the paths that exist only in the Shell’s memory. A file in the workspace or your home is the real file, and has no Shell limit.

StatusMeaningstderr
125Box failed to serve the command.The failure
126Policy refused the command.strands-shell: effect denied: policy denied this operation on '<program>', then [policy: <id>] or [default-deny]
126Policy permitted a program, and nothing in Configuration covers it.strands-shell: <program>: no tool runs <path>: no `[tool.<name>] command` matches this program and its leading arguments
127The program isn’t in the Shell, and not on the PATH.strands-shell: <program>: command not found
Any otherThe command’s own status. A refused file operation fails the command that made it, for example cat with 1.policy denied this operation on '<path>', then [policy: <id>] or [default-deny]