Documentation v0.30.0

Sandbox

Every run_command, script tool, and scheduler script execution is wrapped by go-pkg/sandbox:

Platform Mechanism
Linux bubblewrap (bwrap)
macOS sandbox-exec

Sandbox restrictions: no privileged execution, restricted filesystem write scope, configurable network access, configurable CPU/memory limits.

Three callers, one entry point

The sandbox has exactly three callers, all calling sandbox.Wrap(ctx, binary, args, workDir, opt) directly:

  1. run_command — arbitrary user-issued commands (internal/tools/runCommand.go)
  2. toolAdapter/script/execute — script-tool extensions (script_*, ext_*)
  3. test_tool — trial runs of a script tool's script.py

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

Policy files are embedded in the binary and merged with the matching keys in ~/.config/agenvoy/config.json at startup (user entries add to the defaults, never replace them):

File config.json key Purpose
configs/jsons/denied_map.json denied_map Paths the sandbox refuses to expose
configs/jsons/exclude_list.json Paths excluded from listing / walking / searching
configs/jsons/white_list.json white_list Binaries run_command may execute
configs/jsons/read_only_command.json read_only_command Commands that skip the confirm gate
configs/jsons/net_white_list.json net_white_list Hosts exempt from the SSRF guard

filesystem.LoadRuntime() injects the merged policy once:

sandbox.New(DeniedMapBytes)
filesystem.New(Policy{DeniedMap: DeniedMapBytes, ExcludeList: configs.ExcludeList})

Both go-pkg/sandbox and go-pkg/filesystem enforce IsDenied automatically — no caller-side checking needed.

Filesystem write guard

go-pkg/filesystem write APIs (WriteFile, WriteJSON, AppendText, CheckDir) all enforce IsDenied internally. Any agenvoy code that bypasses go-pkg/filesystem and writes via os.WriteFile directly escapes the policy — this is forbidden.

The internal/filesystem package retains only path computation and domain wrappers (e.g., MCPPath, MCPSessionPath). It does not duplicate read/write logic.

Subprocess argv-only schema

run_command accepts only argv: string[] (minItems 1). It does not accept a command: string with auto-tokenization. This zero-parsing approach removes shell-injection surface in the agent layer.

Shell features (pipes, redirects) require an explicit ["sh", "-c", "cmd | pipe"]. That script is parsed with mvdan.cc/sh rather than string-matched, and every command node inside it is checked:

Rule Effect
Bare command names only /usr/bin/curl is rejected — the binary must be written as curl and appear on the allowlist
No dynamic commands A command built from a variable or command substitution is rejected outright
rm is never allowed inside sh -c Rejected regardless of allowlist state
Shell builtins A fixed set (cd, echo, test, export, …) passes without an allowlist entry
Nested sh -c Recursively validated with the same rules

Timeouts

Every tool carries its own timeout, defaulting to one minute and overridden per tool at registration (fetch_page and search_web 90 s, transcribe_media 5 min, download_file 10 min). Subagent invocations, including slot-wait time, are capped by MaxSubagentTimeoutMin (30 min).

These are package-level values — the environment-variable overrides that used to control them were removed.

中文