Secure, policy-enforced execution gateway for AI agents.
agentsh sits under your agent/tooling—intercepting file, network, and process activity (including subprocess trees), enforcing the policy you define, and emitting structured audit events.
Platform note: Linux today (native or containers). macOS and Windows builds are planned; until then, run agentsh inside a Linux container/VM on those platforms.
- Drop-in shell/exec endpoint that turns every command (and its subprocesses) into auditable events.
- Per-operation policy engine:
allow,deny,approve(human OK),soft_delete, orredirect. - Full I/O visibility:
- file open/read/write/delete
- network connect + DNS
- process start/exit
- PTY activity
- Two output modes:
- human-friendly shell output
- compact JSON responses for agents/tools
Agent workflows eventually run arbitrary code (pip install, make test, python script.py). Traditional "ask for approval before running a command" controls stop at the tool boundary and can't see what happens inside that command.
agentsh enforces policy at runtime, so hidden work done by subprocesses is still governed, logged, and (when required) approved.
Most systems can deny an action. agentsh can also redirect it.
That means when an agent tries the wrong approach (or brute-force workarounds), policy can steer it to the right path by swapping the command and returning guidance—keeping the agent on the paved road and reducing wasted retries.
Example: redirect curl to an audited wrapper
command_rules:
- name: redirect-curl
commands: [curl, wget]
decision: redirect
message: "Downloads routed through audited fetch"
redirect_to:
command: agentsh-fetch
args: ["--audit"]Example: redirect writes outside workspace back inside
file_rules:
- name: redirect-outside-writes
paths: ["/home/**", "/tmp/**"]
operations: [write, create]
decision: redirect
redirect_to: "/workspace/.scratch"
message: "Writes outside workspace redirected to /workspace/.scratch"The agent sees a successful operation (not an error), but you control where things actually land.
Containers isolate the host surface; agentsh adds in-container runtime visibility and policy.
- Per-operation audit (files, network, commands) shows what happened during installs/builds/tests.
- Approvals and rules persist across long-lived shells and subprocess trees—not just the first command.
- Path-level controls on mounted workspaces/caches/creds; containers don't natively give that granularity.
- Same behavior on host and in containers, so CI and local dev see the same policy outcomes.
From a GitHub Release
Download the .deb, .rpm, or .apk for your platform from the releases page.
# Example for Debian/Ubuntu
sudo dpkg -i agentsh_<VERSION>_linux_amd64.debFrom source
make build
sudo install -m 0755 bin/agentsh bin/agentsh-shell-shim /usr/local/bin# Start the server (optional if using autostart)
./bin/agentsh server --config configs/server-config.yaml
# Create a session and run a command (shell output)
SID=$(./bin/agentsh session create --workspace . | jq -r .id)
./bin/agentsh exec "$SID" -- ls -la
# Structured output for agents
./bin/agentsh exec --output json --events summary "$SID" -- curl https://example.com## Shell access
- Run commands via agentsh, not directly in bash/zsh.
- Use: `agentsh exec $SID -- <your-command-here>`
- For structured output: `agentsh exec --output json --events summary $SID -- <your-command-here>`
- Get session ID first: `SID=$(agentsh session create --workspace . | jq -r .id)`You do not need to start agentsh server yourself.
- The first
agentsh exec(or any shimmed/bin/sh//bin/bash) will automatically launch a local server usingconfigs/server-config.yaml(orAGENTSH_CONFIGif set). - That server keeps the FUSE layer and policy engine alive for the session lifetime; subsequent commands reuse it.
- Set
AGENTSH_NO_AUTO=1if you want to manage the server lifecycle manually.
See Dockerfile.example for a minimal Debian-based image.
Inside the image, install a release package (or copy your build), then activate the shim:
agentsh shim install-shell \
--root / \
--shim /usr/bin/agentsh-shell-shim \
--bash \
--i-understand-this-modifies-the-hostPoint the shim at your server (sidecar or host):
ENV AGENTSH_SERVER=http://127.0.0.1:8080Now any /bin/sh -c ... or /bin/bash -lc ... in the container routes through agentsh.
Recommended pattern: run agentsh as a sidecar (or PID 1) in the same pod/service and share a workspace volume; the shim ensures every shell hop stays under policy.
allowdenyapprove(human OK)redirect(swap a command)audit(allow + log)soft_delete(quarantine deletes with restore)
- file operations
- commands
- environment vars
- network (DNS/connect)
- PTY/session settings
- first matching rule wins
Rules live in a named policy; sessions choose a policy.
Defaults:
- sample config:
configs/server-config.yaml - default policy:
configs/policies/default.yaml - env override: set
AGENTSH_POLICY_NAMEto an allowed policy name (no suffix). If unset/invalid/disallowed, the default is used. - env policy: configure
policies.env_policy(allow/deny, max_bytes, max_keys, block_iteration) and per-commandenv_*overrides in policy files. Empty allowlist defaults to minimal PATH/LANG/TERM/HOME with built-in secret deny list; setblock_iterationto hide env iteration (requires env shim). - allowlist: configure
policies.allowedinconfig.yml; empty means only the default is permitted. - optional integrity: set
policies.manifest_pathto a SHA256 manifest to verify policy files at load time.
- Defaults: With no
env_allow, agentsh builds a minimal env (PATH/LANG/TERM/HOME) and strips built-in secret keys. - Overrides: Per-command
env_allow/env_denyplusenv_max_keys/env_max_bytescap and filter the child env at exec time. - Block iteration:
env_block_iteration: true(global or per rule) hides env enumeration; setpolicies.env_shim_pathtolibenvshim.soso agentsh injectsLD_PRELOAD+AGENTSH_ENV_BLOCK_ITERATION=1. - Limits: Errors if limits are exceeded; env builder is applied before exec for every command.
- Examples: See
config.ymland policy samples underconfigs/.
version: 1
name: default
file_rules:
- name: allow-workspace
paths: ["/workspace", "/workspace/**"]
operations: [read, open, stat, list, write, create, mkdir, chmod, rename]
decision: allow
- name: approve-workspace-delete
paths: ["/workspace", "/workspace/**"]
operations: [delete, rmdir]
decision: approve
message: "Delete {{.Path}}?"
timeout: 5m
- name: deny-ssh-keys
paths: ["/home/**/.ssh/**", "/root/.ssh/**"]
operations: ["*"]
decision: deny
network_rules:
- name: allow-api
domains: ["api.example.com"]
ports: [443]
decision: allow
command_rules:
- name: block-dangerous
commands: ["rm", "shutdown", "reboot"]
decision: deny# Start the server with your policy
./bin/agentsh server --config configs/server-config.yaml
# Create a session pinned to a policy
SID=$(./bin/agentsh session create --workspace /workspace --policy default | jq -r .id)
# Exec commands; responses include decision + guidance when blocked/approved
./bin/agentsh exec "$SID" -- rm -rf /workspace/tmpThe fastest way to "get it" is to run something that spawns subprocesses and touches the filesystem/network.
# 1) Create a session in your repo/workspace
SID=$(agentsh session create --workspace . | jq -r .id)
# 2) Run something simple (human-friendly output)
agentsh exec "$SID" -- uname -a
# → prints system info, just like normal
# 3) Run something that hits the network (JSON output + event summary)
agentsh exec --output json --events summary "$SID" -- curl -s https://example.com
# → JSON response includes: exit_code, stdout, and events[] showing dns_query + net_connect
# 4) Trigger a policy decision - try to delete something
agentsh exec "$SID" -- rm -rf ./tmp
# → With default policy: prompts for approval or denies based on your rules
# 5) See what happened (structured audit trail)
agentsh exec --output json --events full "$SID" -- ls
# → events[] shows every file operation, even from subprocessesWhat you'll see in the JSON output:
exit_code: the command's exit statusstdout/stderr: captured outputevents[]: every file/network/process operation with policy decisionspolicy.decision:allow,deny,approve, orredirect
Tip: keep a terminal with --output json open when testing policies—it makes it obvious what's being touched.
You already have a default policy (configs/policies/default.yaml). These opinionated packs are available as separate files so teams can pick one:
-
policies/dev-safe.yaml: safe for local development- allow workspace read/write
- approve deletes in workspace
- deny
~/.ssh/**,/root/.ssh/** - restrict network to allowlisted domains/ports
-
policies/ci-strict.yaml: safe for CI runners- deny anything outside workspace
- deny outbound network except artifact registries
- deny interactive shells unless explicitly allowed
- audit everything (summary events)
-
policies/agent-sandbox.yaml: "agent runs unknown code" mode- default deny + explicit allowlist
- approve any credential/path access
- redirect network tool usage to internal proxies/mirrors
- soft-delete destructive operations for easy recovery
- Config template:
configs/server-config.yaml - Default policy:
configs/policies/default.yaml - Example Dockerfile (with shim):
Dockerfile.example - Environment variables (all
AGENTSH_*overrides, auto-start toggles, transport selection):docs/spec.md§15.3 "Environment Variables" - Architecture & data flow (FUSE + policy engine + API): inline comments in
configs/server-config.yamlandinternal/netmonitor - CLI help:
agentsh --help,agentsh exec --help,agentsh shim --help
Created with the help of agents for agents.