
Execution-Layer Security (ELS) for AI agents — policy-enforced shell with audit.
macOS note: Native macOS enforcement via ESF (Endpoint Security Framework) + NE (Network Extension) is in Alpha. It works end-to-end — file, process, and network events flow through the system extension to the Go policy engine — but expect rough edges and breaking changes between releases. For production use today, we recommend Linux.
Windows note: We are working to get the minifilter drivers signed. Until then, only Windows WSL2 mode is fully supported for production use.
Secure, policy-enforced execution gateway for AI agents.
agentsh sits under your agent/tooling—intercepting file, network, process, and signal activity (including subprocess trees), enforcing the policy you define, and emitting structured audit events.
Platform note: Linux provides full enforcement (100% security score). macOS ESF+NE (90% score) is in Alpha — functional but not production-ready. Windows WSL2 provides full Linux-equivalent enforcement (100% score); native Windows via minifilter driver + AppContainer (85% score) is pending driver signing. See the Platform Comparison Matrix for details.
allow, deny, approve (human OK), soft_delete, or redirect.db_servicesAgent 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.
macOS (Homebrew)
brew tap canyonroad/tap
brew install --cask agentsh
This installs the AgentSH app bundle with the ESF+NE system extension. After installation you'll be prompted to approve the system extension in System Settings > General > Login Items & Extensions.
Linux (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.deb
From source (Linux)
make build
sudo install -m 0755 bin/agentsh bin/agentsh-shell-shim /usr/local/bin
From source (macOS)
# ESF+NE mode (full enforcement — Alpha, requires Xcode 15+)
make build-macos-enterprise
See macOS Build Guide for detailed macOS build instructions.
# 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 . --json | 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
agentsh detect probes the host and reports which enforcement primitives are actually available — seccomp, Landlock, FUSE, eBPF, ptrace, cgroups — grouped into per-domain protection scores plus the selected security mode. On restricted hosts (Daytona, E2B, Firecracker-class) where the seccomp user-notify listener can't install, it reports the mode that will actually enforce rather than what the kernel merely supports.
agentsh detect # human-readable protection report
agentsh detect config # emit a config tuned for this host
See Security Modes for the mode matrix and tuning knobs.
## 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 . --json | jq -r .id)`
You do not need to start agentsh server yourself.
agentsh exec (or any shimmed /bin/sh//bin/bash) will automatically launch a local server using configs/server-config.yaml (or AGENTSH_CONFIG if set).AGENTSH_NO_AUTO=1 if 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-host
Point the shim at your server (sidecar or host):
ENV AGENTSH_SERVER=http://127.0.0.1:18080
Now any /bin/sh -c ... or /bin/bash -lc ... in the container routes through agentsh.
By default, the shim bypasses policy when stdin is not a TTY (preserving binary data for piped commands). On platforms where commands are always non-interactive but still need enforcement (e.g., exe.dev, sandbox APIs), add --force:
agentsh shim install-shell \
--root / \
--shim /usr/bin/agentsh-shell-shim \
--bash \
--force \
--i-understand-this-modifies-the-host
This writes /etc/agentsh/shim.conf with force=true, which the shim reads at startup. The config file works regardless of how the shell is spawned (unlike env vars or profile scripts). AGENTSH_SHIM_FORCE=1 in the process environment achieves the same effect per-process.
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)Rules live in a named policy; sessions choose a policy.
Defaults:
configs/server-config.yamlconfigs/policies/default.yamlAGENTSH_POLICY_NAME to an allowed policy name (no suffix). If unset/invalid/disallowed, the default is used.policies.env_policy (allow/deny, max_bytes, max_keys, block_iteration) and per-command env_* overrides in policy files. Empty allowlist defaults to minimal PATH/LANG/TERM/HOME with built-in secret deny list; set block_iteration to hide env iteration (requires env shim).policies.allowed in config.yml; empty means only the default is permitted.policies.manifest_path to a SHA256 manifest to verify policy files at load time.env_allow, agentsh builds a minimal env (PATH/LANG/TERM/HOME) and strips built-in secret keys.env_allow/env_deny plus env_max_keys/env_max_bytes cap and filter the child env at exec time.env_block_iteration: true (global or per rule) hides env enumeration; set policies.env_shim_path to libenvshim.so so agentsh injects LD_PRELOAD + AGENTSH_ENV_BLOCK_ITERATION=1.BASH_ENV to disable shell builtins that bypass seccomp. Configure in sandbox.env_inject (global) or policy-level (overrides global).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 --json | jq -r .id)
# Exec commands; responses include decision + guidance when blocked/approved
./bin/agentsh exec "$SID" -- rm -rf /workspace/tmp
agentsh supports multiple authentication methods:
| Type | Use Case |
|---|---|
api_key | Simple deployments with static keys |
oidc | Enterprise SSO (Okta, Azure AD, etc.) |
hybrid | Both methods accepted |
Approval modes for human-in-the-loop verification:
local_tty - Terminal prompt (default)totp - Authenticator app codeswebauthn - Hardware security keys (YubiKey)api - Remote approval via RESTSee SECURITY.md for configuration details.
See SECURITY.md for full configuration options, or run the MCP Protection Demo to see these detections in action.
The 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 . --json | 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 all "$SID" -- ls
# → events[] shows every file operation, even from subprocesses
What 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, or redirectTip: keep a terminal with --output json open when testing policies—it makes it obvious what's being touched.
Generate markdown reports summarizing session activity:
# Quick summary
agentsh report latest --level=summary
# Detailed investigation
agentsh report <session-id> --level=detailed --output=report.md
Reports include:
See CI/CD Integration Guide for pipeline examples.
Create snapshots of workspace state for recovery from destructive operations:
# Create a checkpoint before risky operations
agentsh checkpoint create --session $SID --workspace /workspace --reason "before cleanup"
# List checkpoints for a session
agentsh checkpoint list --session $SID
# Show what changed since a checkpoint
agentsh checkpoint show <cp-id> --session $SID --workspace /workspace --diff
# Preview what rollback would restore (dry-run)
agentsh checkpoint rollback <cp-id> --session $SID --workspace /workspace --dry-run
# Restore workspace to checkpoint state
agentsh checkpoint rollback <cp-id> --session $SID --workspace /workspace
# Clean up old checkpoints
agentsh checkpoint purge --session $SID --older-than 24h --keep 5
Auto-checkpoint: When enabled, agentsh automatically creates checkpoints before risky commands (rm, mv, git reset, git checkout, etc.). Configure in sessions.checkpoints.auto_checkpoint.
See SECURITY.md for full configuration options.
agentsh includes an embedded proxy that intercepts all LLM API requests from agents:
# Check proxy status for a session
agentsh proxy status <session-id>
# View LLM-specific events
agentsh session logs <session-id> --type=llm
Features:
ANTHROPIC_BASE_URL and OPENAI_BASE_URL so agent SDKs route through the proxyProvider configuration:
proxy:
mode: embedded
providers:
anthropic: https://api.anthropic.com # Default Anthropic API
openai: https://api.openai.com # Default OpenAI API
# Or use alternative providers:
# openai: http://localhost:8000 # LiteLLM / vLLM
# openai: https://your-resource.openai.azure.com # Azure OpenAI
# anthropic: https://llm.corp.example.com # Corporate gateway
DLP configuration:
dlp:
mode: redact
patterns:
email: true
api_keys: true
custom_patterns:
- name: customer_id
display: identifier
regex: "CUST-[0-9]{8}"
See LLM Proxy Documentation for full configuration options.
The same proxy also dispatches declared http_services entries — named API upstreams with per-method, per-path rules. See Declared HTTP Services and the HTTP Services Cookbook for details.
agentsh can enforce policy on declared database services through db_services, database_connection_rules, and database_rules. The current implementation is Postgres-family only: PostgreSQL is the supported target, with Aurora Postgres using the same path and Redshift/CockroachDB treated as Postgres-compatible dialects with beta coverage. MySQL, MongoDB, Snowflake, BigQuery, Databricks, ClickHouse, MSSQL, Cassandra, Redis, and Oracle are roadmap items, not current runtime support.
Current Postgres support includes:
redirect for read-only Postgres relation replacement.The Postgres proxy runtime is Linux-only in-process code today. Use native Linux, WSL2, or a Linux VM environment for database enforcement.
See Database Access Control and Policy documentation.
Generate restrictive policies from observed session behavior ("profile-then-lock" workflow):
# Generate policy from latest session
agentsh policy generate latest --output=ci-policy.yaml
# Generate with custom name and threshold
agentsh policy generate abc123 --name=production-build --threshold=10
# Quick preview to stdout
agentsh policy generate latest
The generated policy:
*.github.com)Use cases:
agentsh includes an embedded PostgreSQL proxy that makes database access agent-aware and policy-governed. It speaks the Postgres wire protocol, classifies every statement into a list of effects (reads, writes, DDL, DCL, transaction/session control, bulk COPY/export, …), and evaluates each effect against database_rules before forwarding upstream — so an UPDATE, DROP, or unscoped DELETE is governed the same way a file write or network connect is.
allow, deny, approve (human OK), audit, and statement-level redirect.require_where guard — refuse top-level UPDATE/DELETE that lack a WHERE clause.database_connection_rules) gate which sessions may reach which declared db_service.policies.db.log_statements: none | parameters_redacted | full).Phase 1 covers the PostgreSQL v3 wire protocol (dialects: postgres, aurora_postgres; redshift / cockroachdb in beta). Replication and GSSAPI-encrypted connections default-deny.
database_rules:
# normal reads + updates on the declared service
- name: app-read-and-update
db_service: appdb
operations: [READ, UPDATE]
decision: allow
# allow UPDATE/DELETE only when scoped by a WHERE clause
- name: app-guard-unscoped-dml
db_service: appdb
operations: [UPDATE, DELETE]
require_where: true
decision: allow
# block schema/DDL mutations; terminate the transaction on violation
- name: app-deny-ddl
db_service: appdb
operations: [CREATE, DROP, ALTER, EXPORT]
decision: deny
deny_mode_in_tx: terminate
message: "appdb is read+update only. Requested: {{.Operation}}"
See the Database Access Control spec for the full operation taxonomy, effects model, connection rules, and unavoidability threat model.
agentsh can transparently redirect DNS and TCP connections, enabling use cases like routing API calls through corporate proxies or switching AI providers without code changes.
Intercept DNS resolution and return configured IP addresses:
dns_redirect:
- match: "api.anthropic.com"
redirect_ip: "10.0.0.50"
visibility: audit_only
on_failure: fail_closed
- match: ".*\\.openai\\.com" # Regex pattern
redirect_ip: "10.0.0.51"
visibility: warn
Redirect TCP connections to different destinations with optional TLS handling:
connect_redirect:
- match: "api.anthropic.com:443"
redirect_to: "vertex-proxy.internal:8443"
tls_mode: passthrough # Forward encrypted traffic unchanged
visibility: silent
- match: "api.openai.com:443"
redirect_to: "azure-proxy.internal:443"
tls_mode: rewrite_sni # Modify SNI in TLS ClientHello
rewrite_sni: "azure-openai.example.com"
visibility: audit_only
agentsh intercepts signals (kill, SIGTERM, etc.) sent between processes, providing policy-based control over which signals can reach which targets.
signal_rules:
# Allow signals to self and children
- name: allow-self
signals: ["@all"]
target:
type: self
decision: allow
- name: allow-children
signals: ["@all"]
target:
type: children
decision: allow
# Redirect SIGKILL to graceful SIGTERM
- name: graceful-kill
signals: ["SIGKILL"]
target:
type: children
decision: redirect
redirect_to: SIGTERM
# Block fatal signals to external processes
- name: deny-external-fatal
signals: ["@fatal"]
target:
type: external
decision: deny
@all - All signals (1-31)@fatal - SIGKILL, SIGTERM, SIGQUIT, SIGABRT@job - SIGSTOP, SIGCONT, SIGTSTP, SIGTTIN, SIGTTOU@reload - SIGHUP, SIGUSR1, SIGUSR2self - Process signaling itselfchildren - Direct child processesdescendants - All descendant processessession - Any process in the agentsh sessionexternal - PIDs outside the sessionsystem - PID 1 and kernel threadsSee Policy Documentation for full configuration options.
On macOS, agentsh monitors file I/O using the Endpoint Security Framework (ESF), subscribing to both AUTH and NOTIFY events. Tracked operations include file open, create, delete, rename, write (detected via close-modified), and on macOS 26+, chmod and chown via attribute change events. Every file event is attributed to the originating session and command through PID-based resolution, providing full audit trails across subprocess trees.
ESF provides kernel-level allow/deny enforcement but does not support transparent file interception like Linux FUSE. Policy actions that require interception -- such as redirect (path rewriting) and soft_delete (quarantine) -- are implemented as deny + guidance: the operation is blocked at the ESF level and the agent receives instructions to retry with the correct path or to acknowledge that the file is protected. See the macOS ESF+NE architecture doc for event stream details and the policy documentation for per-action behavior.
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
~/.ssh/**, /root/.ssh/**policies/ci-strict.yaml: safe for CI runners
policies/agent-sandbox.yaml: "agent runs unknown code" mode
Ready-to-use snippets for configuring AI coding assistants to use agentsh:
Note: These examples are for local development scenarios where running the AI agent inside a container isn't practical. For production or CI/CD environments, prefer running agents in containers with the shell shim installed—see Use in Docker.
agentsh-mcp-protection-demo - live demo of cross-server exfiltration detection, rug pull blocking, and policy generationSECURITY.md - what agentsh protects against, known limitations, operator checklistSECURITY.md#external-kms-integration - AWS KMS, Azure Key Vault, HashiCorp Vault, GCP Cloud KMS for audit integrity keysconfigs/server-config.yamlconfigs/policies/default.yamlDockerfile.exampledocs/operations/policies.md - policy variables, signal rules, network redirectCreated with the help of agents for agents.
env_injectconfig.yml and policy samples under configs/.| Field | Values | Description |
|---|
visibility | silent, audit_only, warn | How redirects are logged/shown |
on_failure | fail_closed, fail_open, retry_original | What happens if redirect fails |
tls_mode | passthrough, rewrite_sni | TLS handling for connect redirect |
| Feature | Linux | macOS | Windows |
|---|
| DNS Redirect | ✅ eBPF | ✅ pf/proxy | ✅ WinDivert |
| Connect Redirect | ✅ eBPF | ✅ pf/proxy | ✅ WinDivert |
| SNI Rewrite | ✅ | ✅ | ✅ |
| Platform | Blocking | Redirect | Audit |
|---|
| Linux | Yes (seccomp user-notify) | Yes | Yes |
| macOS | No | No | Yes (ES) |
| Windows | Partial | No | Yes (ETW) |
docs/agentsh-db-access-spec.md - Postgres-only database enforcement scope, policy semantics, redirect behavior, and roadmapdocs/cookbook/command-policies.md - how to allow a new binary, when to use wrap instead of exec, and how to debug a denialdocs/cookbook/http-services.md - recipes for routing outbound HTTP API calls through declared services with rules and approval gatingdocs/cookbook/sandbox-sdk-integrations.md - shim_install config for Tensorlake / E2B / Modal / Daytona where commands run as siblings of the agentsh serverskills/ - AI-assistant skills for creating and editing policies in Claude Code, NanoClaw, etc.docs/platform-comparison.md - feature support, security scores, performance by platformdocs/bubblewrap-vs-agentsh-comparison.md - comparison with Bubblewrap for Linux container sandboxingdocs/agentsh-db-access-spec.md - PostgreSQL proxy taxonomy, effects model, database_rules, connection rules, threat modeldetect: docs/security-modes.md - enforcement modes, protection score, and what agentsh detect reportsdocs/seccomp.md - syscall filtering, execve interception, and socket-family blockingdocs/ptrace-support.md - PTRACE_SEIZE enforcement for restricted containers (attach_mode, seccomp prefilter)docs/ebpf.md - eBPF network tracing & policy enforcementdocs/llm-proxy.md - embedded proxy configuration, DLP patterns, usage trackingdocs/macos-build.md - ESF+NE build instructionsdocs/macos-esf-ne-architecture.md - System Extension, XPC, and deployment detailsdocs/macos-xpc-sandbox.md - XPC/Mach IPC control for sandboxed processesAGENTSH_* overrides, auto-start toggles, transport selection): docs/spec.md §15.3 "Environment Variables"configs/server-config.yaml and internal/netmonitoragentsh --help, agentsh exec --help, agentsh shim --help