v1.3.8

Sandbox

Last updated


Every run_command / background_command, script tool, and test_tool execution is wrapped by go-pkg/sandbox:
PlatformMechanism
Linuxbubblewrap (bwrap)
macOSsandbox-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:
  1. run_command and background_command — arbitrary user-issued commands, sharing one runCommand path (internal/tools/runCommand.go, runCommandBackground.go)
  2. toolAdapter/script execute — script-tool extensions (script_*, ext_*)
  3. test_tool — trial runs of a script tool's script.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.
PolicyEmbedded defaultconfig.json keyPurpose
Sensitive pathsconfigs/jsons/sensitive_path.jsonsensitive_pathPaths 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
Exclusionsconfigs/jsons/exclude_list.json—Paths excluded from listing / walking / searching
Read-only commandsconfigs/jsons/read_only_command.json (532 entries since v1.3.3, 409 after cd was dropped in v1.3.0)read_only_commandCommands 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_commandBinaries run_command refuses outright, inside sh -c too
Denied paths—denied_pathPaths 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_listHosts 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.
中文