agentsh Documentation

Secure, policy-enforced execution gateway for AI agents. Intercept file, network, and process activity at runtime with full audit logs.

What is agentsh#

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.

Drop-in Shell

Turns every command (and its subprocesses) into auditable events.

Policy Engine

Per-operation decisions: allow, deny, approve, redirect, or soft_delete.

Full Visibility

File I/O, network, DNS, process lifecycle, PTY activity, and LLM requests.

Two Output Modes

Human-friendly shell output or compact JSON for agents/tools.

Redirect & Steer

Swap commands or file paths to keep agents on the paved road.

LLM Proxy & DLP

Intercept API requests, redact PII, and track token usage.

Checkpoints

Snapshot workspace state and rollback destructive operations.

Cross-Platform

Linux, macOS, and Windows with native enforcement mechanisms.

Why agentsh?

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.

Quick Start#

The fastest way to understand agentsh 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

# 3) Run something that hits the network (JSON output + event summary)
agentsh exec --output json --events summary "$SID" -- curl -s https://example.com

# 4) Trigger a policy decision - try to delete something
agentsh exec "$SID" -- rm -rf ./tmp

# 5) See what happened (structured audit trail)
agentsh exec --output json --events full "$SID" -- ls

What you'll see in the JSON output:

Installation#

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)

# FUSE-T mode (standard, requires brew install fuse-t)
CGO_ENABLED=1 go build -o bin/agentsh ./cmd/agentsh

# ESF+NE enterprise mode (requires Xcode 15+, Apple entitlements)
make build-macos-enterprise

Platform Support#

agentsh supports Linux, macOS, and Windows with varying levels of enforcement capability.

Linux Native
100%
Windows WSL2
100%
macOS ESF+NE
90%
macOS + Lima
85%
Windows Native
85%
macOS FUSE-T
70%

Linux#

100% Score Full enforcement with all features.

Linux provides the most complete security coverage:

Requirements

Root or CAP_SYS_ADMIN for namespaces. eBPF requires kernel 5.x+ for full features.

macOS#

macOS supports three deployment options:

ESF+NE (Enterprise) 90%

Best for commercial security products. Uses Endpoint Security Framework for file monitoring and Network Extension for traffic interception.

FUSE-T (Standard) 70%

Easy setup for development and personal use. Install via brew install fuse-t.

Lima VM 85%

Full isolation via Linux VM. Best for production on macOS when you need full security features.

Windows#

Native 85%

Uses mini filter driver with AppContainer sandbox isolation.

WSL2 100%

Full Linux features in Windows Subsystem for Linux.

Platform Comparison#

Feature Linux macOS ESF macOS FUSE-T Windows
File blocking Yes Yes Yes Yes
Network blocking Yes Yes Yes Yes
Signal blocking Yes Audit Audit Audit
Namespace isolation Full None None Partial
Syscall filtering Yes Exec only No No
Resource limits Full None None Partial
Hosted sandbox environments

On hosted sandbox platforms (Sprites, Modal, E2B, etc.), some Linux features like namespace isolation, syscall filtering, and resource limits may be unavailable or restricted due to the underlying kernel configuration. File and network blocking typically work, but verify feature availability for your specific platform.

Running Inside Containers#

Containers isolate the host surface; agentsh adds in-container runtime visibility and policy.

Ready-to-use examples:

Shell Shim

The shell shim replaces /bin/sh and /bin/bash so that any shell invocation routes through agentsh—including subprocess calls from scripts, package managers, and build tools.

agentsh shim install-shell \
  --root / \
  --shim /usr/bin/agentsh-shell-shim \
  --bash \
  --i-understand-this-modifies-the-host

Point the shim at your server:

ENV AGENTSH_SERVER=http://127.0.0.1:18080

Now any /bin/sh -c ... or /bin/bash -lc ... in the container routes through agentsh.

Docker Setup

Example Dockerfile (Debian-based):

FROM debian:bookworm-slim

ARG AGENTSH_REPO=canyonroad/agentsh
ARG AGENTSH_TAG=v0.1.0
ARG DEB_ARCH=amd64

RUN set -eux; \
  apt-get update; \
  apt-get install -y --no-install-recommends ca-certificates curl bash; \
  rm -rf /var/lib/apt/lists/*

RUN set -eux; \
  version="${AGENTSH_TAG#v}"; \
  deb="agentsh_${version}_linux_${DEB_ARCH}.deb"; \
  url="https://github.com/${AGENTSH_REPO}/releases/download/${AGENTSH_TAG}/${deb}"; \
  curl -fsSL -L "${url}" -o /tmp/agentsh.deb; \
  dpkg -i /tmp/agentsh.deb; \
  rm -f /tmp/agentsh.deb; \
  agentsh shim install-shell \
    --root / \
    --shim /usr/bin/agentsh-shell-shim \
    --bash \
    --i-understand-this-modifies-the-host

CMD ["/bin/sh", "-lc", "echo hello from agentsh shim"]

Sidecar Pattern

Recommended: 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.

# docker-compose.yml
services:
  agentsh:
    image: agentsh:latest
    volumes:
      - workspace:/workspace
    ports:
      - "18080:18080"

  agent:
    image: your-agent:latest
    environment:
      - AGENTSH_SERVER=http://agentsh:18080
    volumes:
      - workspace:/workspace
    depends_on:
      - agentsh

volumes:
  workspace:

Local Development#

For local development outside containers:

# Start the server (optional if using autostart)
./bin/agentsh server --config configs/server-config.yaml

# Create a session and run a command
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
Autostart

You don't need to start agentsh server manually. The first agentsh exec automatically launches a local server using the config file. Set AGENTSH_NO_AUTO=1 to manage the server lifecycle yourself.

Protecting Dev Tools

AI coding assistants like Claude Code, Cursor, and Codex don't just run the commands you see—they also make internal implementation decisions, run subprocesses, and access files without explicit tool calls. Running the dev tool itself through agentsh gives you visibility and policy enforcement over all activity, not just the commands shown in the UI.

Why this matters

When an AI assistant decides to "check something quickly" or "read a file for context," those operations happen silently. Without agentsh wrapping the entire process, you only see what the tool chooses to show you.

Wrapping your dev tool

Instead of running your AI assistant directly, launch it through agentsh:

# Create a session for your workspace
SID=$(agentsh session create --workspace . | jq -r .id)

# Run Claude Code through agentsh
agentsh exec "$SID" -- claude

# Or run Cursor through agentsh
agentsh exec "$SID" -- cursor .

# Or any other dev tool
agentsh exec "$SID" -- code .

What you gain

Shell alias

Add an alias to make this seamless: alias claude='agentsh exec $(agentsh session create --workspace . | jq -r .id) -- claude'

AI Assistant Integration#

There are two approaches to integrating agentsh with AI coding assistants: running the entire agent under agentsh, or configuring the agent to use agentsh for commands.

Running Agents Under agentsh

For unsupervised or autonomous agents, run the entire agent process under agentsh. This ensures comprehensive coverage—every shell command, subprocess, and file operation is monitored and policy-enforced, even if the agent doesn't explicitly use agentsh.

# Create a session with your policy
SID=$(agentsh session create --workspace /path/to/project --policy my-policy | jq -r .id)

# Run Claude Code under agentsh
agentsh exec "$SID" -- claude --dangerously-skip-permissions -p "implement the feature"

# Or run other coding agents
agentsh exec "$SID" -- codex --full-auto "fix the failing tests"
agentsh exec "$SID" -- opencode -p "refactor the auth module"
agentsh exec "$SID" -- cursor-agent --print "add unit tests"

This approach is ideal for:

Combine with shell shim

In containerized environments, install the shell shim so that even nested shells and subprocess calls route through agentsh automatically.

Configuring Agents to Use agentsh

For interactive development, configure your AI assistant to route commands through agentsh. This works well when running inside a container isn't practical.

Note

For production or CI/CD environments, prefer running agents in containers with the shell shim installed.

Claude Code

Add to your project's CLAUDE.md:

# Shell access

Run commands via agentsh, not directly in bash/zsh.

Syntax:
agentsh exec SESSION_ID -- COMMAND [ARGS...]

Examples:
agentsh exec my-session -- ls -la
agentsh exec my-session -- npm install
agentsh exec my-session -- go build ./...

With environment variable:
export AGENTSH_SESSION_ID=my-session
agentsh exec -- ls -la

Cursor

Add to your Cursor rules:

All shell commands MUST be executed through agentsh.

Required syntax:
  agentsh exec SESSION_ID -- COMMAND [ARGS...]

The -- separator is required between session ID and command.

AGENTS.md

For tools that support the AGENTS.md convention, add the same instructions to your project's AGENTS.md file.

Flag Description
--timeout 30sCommand timeout
--output jsonJSON structured output
--streamStream output as produced
--ptyInteractive PTY mode
Environment Variable Description
AGENTSH_SESSION_IDDefault session ID
AGENTSH_SESSION_ROOTRoot for auto-creating sessions
AGENTSH_SERVERServer URL (default: http://127.0.0.1:18080)

Redirect & Steering#

Most systems can deny an action. agentsh can also redirect it.

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.

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"]

Redirect writes outside workspace

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"

Discourage unnecessary deletions

Instead of blocking destructive commands outright, steer the agent away with guidance:

command_rules:
  - name: discourage-rm
    commands: [rm, rmdir]
    args_patterns: ["(-rf?|--recursive)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Skipped: deletion not needed. Files can remain; they don't affect the build or tests."]
    message: "Cleanup is unnecessary—focus on the task instead of removing files."

The agent sees a "successful" cleanup, receives the guidance message, and moves on without wasting retries or attempting workarounds.

The agent sees a successful operation (not an error), but you control where things actually land.

Prevent bypassing merge workflow

Steer agents away from shortcuts that bypass proper code review and merge workflows:

command_rules:
  - name: no-direct-push-to-main
    commands: [git]
    args_patterns: ["push.*(origin\\s+)?(main|master)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Push declined. Create a branch and open a pull request instead."]
    message: "Direct pushes to main/master are not allowed. Use feature branches and pull requests."

  - name: no-force-push
    commands: [git]
    args_patterns: ["push.*(--force|-f)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Force push blocked. Resolve conflicts through proper merge."]
    message: "Force pushing is not allowed—merge changes through the standard workflow."

  - name: no-hard-reset
    commands: [git]
    args_patterns: ["reset.*(--hard)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["Hard reset blocked. Use git stash or git checkout to preserve changes."]
    message: "git reset --hard discards uncommitted work. Use non-destructive alternatives."

  - name: no-git-clean
    commands: [git]
    args_patterns: ["clean.*(-f|--force)"]
    decision: redirect
    redirect_to:
      command: echo
      args: ["git clean blocked. Untracked files may be needed—review before deleting."]
    message: "git clean -fd permanently deletes untracked files. Review them manually first."

The agent receives guidance to use the proper workflow instead of attempting workarounds.

LLM Proxy & DLP#

agentsh includes an embedded proxy that intercepts all LLM API requests from agents.

proxy:
  mode: embedded
  providers:
    anthropic: https://api.anthropic.com
    openai: https://api.openai.com

dlp:
  mode: redact
  patterns:
    email: true
    api_keys: true
  custom_patterns:
    - name: customer_id
      display: identifier
      regex: "CUST-[0-9]{8}"

Session Reports#

Generate markdown reports summarizing session activity for auditing, debugging, and compliance.

# Quick summary of latest session
agentsh report latest --level=summary

# Detailed investigation with full timeline
agentsh report <session-id> --level=detailed --output=report.md

# JSON output for programmatic processing
agentsh report latest --level=summary --format=json

Report Levels

LevelContents
summaryOverview, activity counts, security findings, decision summary
detailedEverything in summary plus command history, file access, network connections, resource usage, and full event timeline

Example Summary Report

# Session Report: sess-abc123

**Generated:** 2025-01-15T10:31:00Z
**Report Level:** Summary

## Session Overview

| Property | Value |
|----------|-------|
| Session ID | sess-abc123 |
| Duration | 25s |
| Workspace | /home/user/project |

## Activity Summary

| Metric | Count |
|--------|-------|
| Commands Executed | 6 |
| Files Accessed | 1 |
| Network Connections | 2 |
| Policy Denials | 2 |

## Security Findings

### Critical
- **Dangerous command blocked**: `rm -rf /` - rm -rf blocked for safety

### Warning
- **Network access denied**: Connection to `internal.corp.local:80` blocked

## Policy Decisions

| Decision | Count |
|----------|-------|
| Allow | 5 |
| Deny | 2 |
| Redirect | 0 |

Example Detailed Report (excerpt)

## Command History

| Time | Command | Decision | Exit Code | Duration |
|------|---------|----------|-----------|----------|
| 10:30:01 | `ls -la` | allow | 0 | 126ms |
| 10:30:05 | `git status` | allow | 0 | 149ms |
| 10:30:15 | `rm -rf /` | **deny** | - | - |
| 10:30:20 | `curl https://api.github.com` | allow | 0 | 499ms |

## Network Connections

| Time | Domain | Port | Decision | Rule |
|------|--------|------|----------|------|
| 10:30:20 | api.github.com | 443 | allow | github.com allowed |
| 10:30:25 | internal.corp.local | 80 | **deny** | internal networks blocked |

## Event Timeline

```
10:30:00.000 [session_created] Session started in /home/user/project
10:30:01.123 [command_policy] ls -la → allow
10:30:15.000 [command_policy] rm -rf / → DENY (rm -rf blocked)
10:30:20.010 [net_connect] api.github.com:443 → allow
10:30:25.010 [net_connect] internal.corp.local:80 → DENY
```

Workspace Checkpoints#

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

# 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
Auto-checkpoint

When enabled, agentsh automatically creates checkpoints before risky commands (rm, mv, git reset, etc.). Configure in sessions.checkpoints.auto_checkpoint.

Policy Generation#

The profile-then-lock workflow lets you generate restrictive policies from observed session behavior. Run your workload once in permissive mode, then lock down future runs to only what was observed.

The Profile-Then-Lock Workflow

1. Profile

Run your build/test/agent task with a permissive policy. agentsh records every operation.

2. Generate

Use the recorded session to generate a policy that allows only what was observed.

3. Lock

Apply the generated policy to future runs. Any deviation is blocked or flagged.

4. Iterate

If legitimate behavior is blocked, re-profile and regenerate.

Generating a Policy

# Step 1: Run your workload (profile phase)
SID=$(agentsh session create --workspace . --policy permissive | jq -r .id)
agentsh exec "$SID" -- npm install
agentsh exec "$SID" -- npm run build
agentsh exec "$SID" -- npm test

# Step 2: Generate policy from observed behavior
agentsh policy generate "$SID" --output=ci-locked.yaml --name=ci-build

# Step 3: Use the locked policy for future runs
SID=$(agentsh session create --workspace . --policy ci-build | jq -r .id)
agentsh exec "$SID" -- npm install  # Only allowed packages from profile

Generation Options

FlagDescription
--outputWrite policy to file (default: stdout)
--namePolicy name (default: generated-<session-id>)
--thresholdMinimum occurrences before including a path pattern (default: 1)
--include-blockedInclude blocked operations as commented-out rules for review

What Gets Generated

The generated policy:

Example Generated Policy

# Generated from session sess-abc123 on 2025-01-15
# Profile: npm install && npm run build && npm test

version: 1
name: ci-build

file_rules:
  - name: workspace-src
    paths: ["/workspace/src/**"]
    operations: [read, open, stat, list]
    decision: allow

  - name: workspace-dist
    paths: ["/workspace/dist/**"]
    operations: [read, write, create, mkdir]
    decision: allow

  - name: node-modules
    paths: ["/workspace/node_modules/**"]
    operations: ["*"]
    decision: allow

network_rules:
  - name: npm-registry
    domains: ["registry.npmjs.org", "*.npmjs.org"]
    ports: [443]
    decision: allow

  - name: default-deny
    domains: ["*"]
    decision: deny

Use Cases

Authentication#

agentsh supports multiple authentication methods:

Type Use Case
api_keySimple deployments with static keys
oidcEnterprise SSO (Okta, Azure AD, etc.)
hybridBoth methods accepted

Approval modes for human-in-the-loop verification:

Policy Model#

Decisions

DecisionDescription
allowPermit the operation
denyBlock the operation
approveRequire human approval
redirectSwap to a different target
auditAllow + log (explicit logging)
soft_deleteQuarantine with restore option

Scopes

Evaluation

First matching rule wins. Rules live in a named policy; sessions choose a policy at creation time.

File Rules#

Control file system operations by path and operation type.

file_rules:
  # Allow reading workspace
  - name: allow-workspace-read
    paths:
      - "/workspace"
      - "/workspace/**"
    operations: [read, open, stat, list, readlink]
    decision: allow

  # Require approval for deletes
  - name: approve-workspace-delete
    paths: ["/workspace/**"]
    operations: [delete, rmdir]
    decision: approve
    message: "Agent wants to delete: {{.Path}}"
    timeout: 5m

  # Block sensitive paths
  - name: deny-ssh-keys
    paths: ["/home/**/.ssh/**", "/root/.ssh/**"]
    operations: ["*"]
    decision: deny

Operations: read, open, stat, list, readlink, write, create, mkdir, chmod, rename, delete, rmdir, * (all)

Network Rules#

Control network connections by domain, CIDR, or port.

network_rules:
  # Allow package registries
  - name: allow-npm
    domains: ["registry.npmjs.org", "*.npmjs.org"]
    ports: [443, 80]
    decision: allow

  # Block private networks
  - name: block-private
    cidrs:
      - "10.0.0.0/8"
      - "172.16.0.0/12"
      - "192.168.0.0/16"
    decision: deny

  # Block cloud metadata
  - name: block-metadata
    cidrs: ["169.254.169.254/32"]
    decision: deny

  # Approve unknown HTTPS
  - name: approve-unknown
    ports: [443]
    decision: approve
    message: "Connect to {{.RemoteAddr}}:{{.RemotePort}}?"

Command Rules#

Pre-execution checks for commands.

command_rules:
  # Safe commands
  - name: allow-safe
    commands: [ls, cat, grep, find, pwd, echo, git, node, python]
    decision: allow

  # Approve package installs
  - name: approve-install
    commands: [npm, pip, cargo]
    args_patterns: ["install*", "add*"]
    decision: approve
    message: "Install packages: {{.Args}}"

  # Block dangerous patterns
  - name: block-rm-rf
    commands: [rm]
    args_patterns: ["*-rf*", "*-fr*"]
    decision: deny

  # Block system commands
  - name: block-system
    commands: [shutdown, reboot, systemctl, mount, dd, kill]
    decision: deny

Signal Rules#

Control which processes can send signals to which targets. Full blocking only on Linux; macOS and Windows provide audit only.

signal_rules:
  # Allow signals to self and children
  - name: allow-self
    signals: ["@all"]
    target:
      type: self
    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

Signal groups:

Target types: self, children, descendants, session, external, system

Registry Rules (Windows)#

Control Windows registry access. Requires mini filter driver.

registry_rules:
  # Block persistence locations
  - name: block-run-keys
    paths:
      - 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*'
      - 'HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run*'
    operations: [write, create, delete]
    decision: deny

  # Block security settings
  - name: block-defender
    paths: ['HKLM\SOFTWARE\Policies\Microsoft\Windows Defender*']
    operations: [write, create, delete]
    decision: deny

  # Allow reads everywhere
  - name: allow-read
    paths: ["*"]
    operations: [read]
    decision: allow

Resource Limits#

Constrain resource usage per session. Full enforcement on Linux only.

resource_limits:
  # Memory
  max_memory_mb: 2048
  memory_swap_max_mb: 0

  # CPU
  cpu_quota_percent: 80

  # Disk I/O
  disk_read_bps_max: 104857600   # 100 MB/s
  disk_write_bps_max: 52428800   # 50 MB/s

  # Network
  net_bandwidth_mbps: 100

  # Process limits
  pids_max: 100

  # Time limits
  command_timeout: 5m
  session_timeout: 4h
  idle_timeout: 30m

Environment Policy#

Control which environment variables processes can access.

Starter Policy Packs#

Pre-built policies for common scenarios:

dev-safe.yaml

Safe for local development.

ci-strict.yaml

Safe for CI runners.

agent-sandbox.yaml

"Agent runs unknown code" mode.

Need help?

See the GitHub repository for more examples, the full spec, and to report issues.