Sandbox
Last updated
Every
run_command / background_command, script tool, and test_tool execution is wrapped by go-pkg/sandbox:| Platform | Mechanism |
|---|---|
| Linux | bubblewrap (bwrap) |
| macOS | sandbox-exec |
Sandbox restrictions: no privileged execution, restricted filesystem write scope, per-call network access, configurable CPU/memory limits.
Since v1.0.25
run_command (and, since v1.3.1, the background variant; run_command_background was renamed background_command (Removed in v1.3.2)) runs with networking denied (NetworkDeny) unless the call sets network: true, which switches that one call to NetworkAllow; a networked call never counts as read-only, so it confirms under safe and runs without a prompt under auto. Since v1.3.0 sandbox: false passes AllowAll, so that one call runs with no sandbox at all, only after the user's system password is verified. Since v1.3.2 an argv ['sudo', '<binary>', ...] also runs outside the sandbox, as sudo -n <binary> ... on the sudo ticket, once its password-backed sudo: <cmd> confirmation is approved. Script tools (script_*, ext_*) and test_tool call sandbox.Wrap with no option, so they keep network access. The networked run_script tool that briefly existed during v1.0.25 development never shipped in a release; use run_command with network: true.Three callers, one entry point
The sandbox has exactly three call paths, all calling
sandbox.Wrap(ctx, binary, args, workDir, opt) directly:run_commandandbackground_command— arbitrary user-issued commands, sharing onerunCommandpath (internal/tools/runCommand.go,runCommandBackground.go)toolAdapter/scriptexecute — script-tool extensions (script_*,ext_*)test_tool— trial runs of a script tool'sscript.py(60 s limit)
Since v1.3.0 the command tools and
test_tool start the wrapped process with sandbox.Run (go-pkg v0.13.17), which kills the process group after the command exits; the script adapter starts and waits on the process itself. There is no wrapper layer between callers and sandbox.Wrap. Adding behavior (e.g., new resource limits) means contributing to go-pkg/sandbox, not adding shims in agenvoy.Policy injection
filesystem.LoadRuntime() reads ~/.config/agenvoy/config.json at startup and hands the merged policy to go-pkg/sandbox and go-pkg/filesystem. Some policies ship embedded in the binary and merge with the user's entries; others exist only as user config.| Policy | Embedded default | config.json key | Purpose |
|---|---|---|---|
| Sensitive paths | configs/jsons/sensitive_path.json | sensitive_path | Paths holding credentials, key material, shell startup files or tool settings (since v1.3.7 also .git, .claude, .bashrc / .zshrc and the other shell rc files, .mcp.json), for reads and writes alike — reachable only after a per-session grant: a plain confirmation inside $HOME, the system password outside it. Four buckets: dirs, files, prefixes, extensions |
| Exclusions | configs/jsons/exclude_list.json | — | Paths excluded from listing / walking / searching |
| Read-only commands | configs/jsons/read_only_command.json (532 entries since v1.3.3, 409 after cd was dropped in v1.3.0) | read_only_command | Commands that skip the confirm gate. Since v1.0.25 a run_command call (since v1.3.1 also the background variant, now background_command) matches when the binary alone, the binary plus its first argument, or the binary plus its first two arguments is on the list (git global flags such as -C <dir> are skipped first); any argument that is a sensitive path, or network: true, cancels the match |
| Denied commands | — | denied_command | Binaries run_command refuses outright, inside sh -c too |
| Denied paths | — | denied_path | Paths permanently off limits for reads and writes; no prompt can approve them. Entries must be absolute or ~/-prefixed, the root is refused, and symlink targets are denied as well |
| SSRF exemptions | — | net_white_list | Hosts exempt from the http_request SSRF guard |
The command policy is a denylist. There is no binary allowlist:
white_list and path_white_list were removed, and sensitive_map was renamed to sensitive_path; a config still carrying any of these keys is ignored silently (the startup warning was dropped in v1.1.2). Path access outside $HOME is granted per session through the confirmation prompt, not through a config list.denied_path is handed to go-pkg/sandbox as its denied map; boundary.Resolve and boundary.WriteBinds check it on the agenvoy side before any file tool or write_paths bind proceeds.