Runtime security governance for AI agents using agentsh v0.16.9 with Cloudflare Containers (Firecracker VMs).
Cloudflare provides isolation. agentsh provides governance.
Cloudflare Containers give AI agents a secure, isolated Firecracker VM environment on the edge. But isolation alone doesn't prevent an agent from:
- Exfiltrating data to unauthorized endpoints
- Accessing cloud metadata (AWS/GCP/Azure credentials at 169.254.169.254)
- Leaking secrets in outputs (API keys, tokens, PII)
- Running dangerous commands (sudo, ssh, kill, nc)
- Reaching internal networks (10.x, 172.16.x, 192.168.x)
- Deleting workspace files permanently
The Cloudflare Worker runs in a V8 isolate — it handles HTTP requests, renders the UI, and manages the sandbox lifecycle. When commands need to execute, the Worker calls sandbox.exec() which runs inside a Firecracker microVM with a full Linux kernel.
+---------------------------+ +---------------------------------------------+
| Cloudflare Worker | | Firecracker VM (Container) |
| (V8 isolate) | | |
| | exec | +---------------------------------------+ |
| - HTTP routing | ------> | | agentsh (Governance) | |
| - HTML/JSON responses | | | - ptrace (syscall-level enforcement) | |
| - getSandbox() API | | | - seccomp (file enforcement) | |
| - Rate limiting | | | - Network proxy (domain filtering) | |
| - Turnstile verification | | | - DLP (secret redaction) | |
| | | | - Audit logging | |
+---------------------------+ | +---------------------------------------+ |
+---------------------------------------------+
agentsh adds the governance layer that controls what agents can do inside the sandbox, providing defense-in-depth:
+---------------------------------------------------------+
| Cloudflare Container (Isolation) |
| +---------------------------------------------------+ |
| | agentsh (Governance) | |
| | +---------------------------------------------+ | |
| | | AI Agent | | |
| | | - Commands are policy-checked | | |
| | | - Network requests are filtered | | |
| | | - File I/O is policy-enforced | | |
| | | - Secrets are redacted from output | | |
| | | - All actions are audited | | |
| | +---------------------------------------------+ | |
| +---------------------------------------------------+ |
+---------------------------------------------------------+
| Cloudflare Provides | agentsh Adds |
|---|---|
| Compute isolation (Firecracker) | Command blocking (ptrace execve interception) |
| Process sandboxing | File I/O policy (ptrace file syscall enforcement) |
| API access to sandbox | Domain allowlist/blocklist |
| Persistent environment | Cloud metadata blocking |
| Network syscall interception (connect/bind) | |
| Secret detection and redaction (DLP) | |
| seccomp file enforcement | |
| LLM request auditing | |
| Complete audit logging |
- Node.js 18+
- Docker
- Cloudflare account with Workers Paid plan
- Wrangler CLI
git clone https://github.com/canyonroad/agentsh-cloudflare
cd agentsh-cloudflare
npm install
# Local development (starts wrangler dev with container)
npm run dev
# Run the full test suite (71 tests)
npm testThe Cloudflare Worker wraps every command with agentsh exec before passing it to the container's sandbox.exec() API:
Worker: sandbox.exec("agentsh exec --root=/workspace demo -- /bin/bash -c 'sudo whoami'")
|
v
+-------------------+
| agentsh exec | CLI sends to agentsh server
| (intercepts) |
+--------+----------+
|
v
+-------------------+
| agentsh server | Policy evaluation + ptrace
| (pre-warmed) | + seccomp file enforcement
+--------+----------+
|
+------+------+
v v
+----------+ +----------+
| ALLOW | | BLOCK |
| exit: 0 | | exit: 1 |
+----------+ +----------+
The agentsh server is pre-warmed via an /internal/start-agentsh endpoint during test setup, and via systemd/rc.local on container boot. This avoids the cold-start penalty on first exec.
| Capability | Status | Notes |
|---|---|---|
| ptrace | Working | Syscall-level enforcement: execve, file, network, signal interception |
| seccomp | Working | File enforcement via seccomp_user_notify (enforce_without_fuse: true) |
| seccomp prefilter | Working | BPF pre-filter reduces ptrace overhead (only 7 syscalls trap) |
| Network proxy | Working | Domain/IP/port filtering via agentsh proxy |
| DLP | Working | Secret detection and redaction in LLM traffic |
| Audit logging | Working | All operations logged |
| Landlock | Not available | Firecracker kernel reports landlock_abi: v0 |
| FUSE | Not available | Firecracker seccomp blocks mount() syscall |
| cgroups | Not available | Read-only in Firecracker containers |
| PID namespace | Not available | Not available in Firecracker config |
Note: All core security enforcement works today using ptrace + seccomp. ptrace intercepts syscalls (execve, file I/O, network, signals) and enforces policy rules. seccomp's file_monitor with enforce_without_fuse: true provides a second layer of file enforcement via seccomp_user_notify. Together they achieve 100% Protection Score with no Cloudflare-side changes needed.
The features below are optional enhancements, not requirements.
Current state: The Firecracker VM has CAP_SYS_ADMIN and fusermount3 is installed, but the Firecracker seccomp profile blocks the mount() syscall. agentsh's FUSE overlay cannot mount.
What it would add (beyond what ptrace + seccomp already enforce):
- Soft-delete quarantine --
rmmoves files to a quarantine directory instead of deleting. Files can be restored withagentsh trash restore. Without FUSE, deletes are blocked or permanent — there is no undo. - VFS-level overlay -- Interception at the filesystem layer rather than the syscall layer. More resilient against edge cases like direct file descriptor manipulation.
Not needed for: File read/write blocking (ptrace + seccomp handle this), credential file protection, symlink restrictions.
How to enable: Allow mount() in the Firecracker seccomp profile, or expose /dev/fuse (character device 10,229). Standard Firecracker configuration — other platforms (E2B, etc.) expose it by default.
Current state: PID namespace creation is not available.
What it unlocks:
- Process isolation -- agentsh can create sessions in isolated PID namespaces, preventing agents from seeing or signaling other processes.
How to enable: Allow CLONE_NEWPID in the Firecracker seccomp filter.
| Feature | Impact | Current | What's Needed |
|---|---|---|---|
| FUSE | Nice to have — adds soft-delete quarantine, VFS overlay | Blocked (mount() denied) |
Allow mount() in Firecracker seccomp |
| PID namespace | Low — process isolation | Not available | Allow CLONE_NEWPID |
Security policy is defined in two files:
config/agentsh.yaml-- Server configuration: ptrace enforcement, network interception, DLP patterns, LLM proxy, seccomp file enforcementpolicies/default.yaml-- Policy rules: command rules, network rules, file rules
See the agentsh documentation for the full policy reference.
agentsh-cloudflare/
├── src/index.ts # Cloudflare Worker (API routes, agentsh exec wrapping)
├── Dockerfile # Container image with agentsh v0.16.9
├── config/agentsh.yaml # Server config (ptrace, seccomp, DLP, network)
├── policies/default.yaml # Security policy (commands, network, files)
├── systemd/agentsh.service # Systemd service for agentsh server
├── scripts/rc.local # Fallback startup script
├── wrangler.toml # Cloudflare Workers + Containers config
├── vitest.config.ts # Test configuration
└── test/ # Integration tests (71 tests, 11 categories)
├── global-setup.ts # Test warmup (container + server + exec path)
├── helpers/ # Test utilities (fetchDemo, findResult)
├── agentsh-installation.test.ts
├── agentsh-status.test.ts
├── allowed-commands.test.ts
├── blocked-commands.test.ts
├── cloud-metadata.test.ts
├── command-blocking.test.ts
├── devtools.test.ts
├── dlp-redaction.test.ts
├── filesystem.test.ts
├── privilege-escalation.test.ts
└── ssrf-prevention.test.ts
The test suite creates a Cloudflare Container and runs 71 security tests across 11 categories:
- Installation -- agentsh binary, version, config directory, security capabilities
- Status -- agentsh detect, kernel version, policy files, seccomp status
- Allowed commands -- whoami, pwd, ls, echo through agentsh enforcement
- Blocked commands -- nc, nmap, cloud metadata curl
- Command blocking -- sudo, su, ssh, scp, shutdown, mount, nc, nmap, killall, pkill
- Privilege escalation -- sudo id, sudo cat shadow, su root, pkexec, shadow read, sudoers write
- Filesystem protection -- workspace writes allowed; /etc/passwd, /etc/shadow, /usr/bin, /etc/agentsh config overwrite blocked
- Cloud metadata -- AWS, GCP, Azure, DigitalOcean, Alibaba, Oracle metadata endpoints blocked
- SSRF prevention -- All RFC 1918 ranges, link-local addresses blocked; external HTTPS allowed
- Dev tools -- Python, Node.js, Bun, git, curl, pip3, pipes all working
- DLP redaction -- Fake OpenAI key, AWS key, GitHub PAT, email/phone detection
npm test # 71 tests across 11 files (~38s with warm sandbox)| Method | Path | Tests | Description |
|---|---|---|---|
| GET | /demo/allowed |
4 | Safe commands: whoami, pwd, ls, echo |
| GET | /demo/blocked |
3 | Policy-blocked: nc, nmap, metadata curl |
| GET | /demo/commands |
10 | Full command blocking: sudo, su, ssh, scp, shutdown, mount, nc, nmap, killall, pkill |
| GET | /demo/privilege-escalation |
6 | Privilege escalation prevention |
| GET | /demo/filesystem |
8 | Filesystem protection (ptrace file enforcement) |
| GET | /demo/cloud-metadata |
6 | AWS, GCP, Azure, DigitalOcean, Alibaba, Oracle |
| GET | /demo/ssrf |
9 | RFC 1918 ranges, link-local, external allowed |
| GET | /demo/devtools |
10 | Python, Node.js, Bun, git, curl, pip3, pipes |
| GET | /demo/dlp |
4 | Fake secrets: OpenAI key, AWS key, GitHub PAT |
| GET | /demo/network |
- | Network blocking overview |
| GET | /demo/status |
7 | agentsh installation and security status |
| GET | /health |
- | Health check (no container needed) |
| POST | /execute |
- | Execute command (requires Turnstile token) |
npm run deployImportant: Cloudflare Containers persist across deploys and don't automatically pick up new images. To force a fresh container:
npx wrangler containers list
npx wrangler containers delete <app-id>
npm run deployUpdate the CACHE_BUST ARG in Dockerfile when config files change, since Docker layer caching may serve stale config.
| Property | Value |
|---|---|
| Base Image | cloudflare/sandbox:0.7.2-python |
| VM Type | Firecracker (standard-1: 1 vCPU, 2GB RAM, 8GB disk) |
| Python | 3.11 |
| Node.js | 20 |
| Bun | Available |
| agentsh | v0.16.9 (.deb package) |
| Enforcement | ptrace (execve, file, network, signal) + seccomp (file enforcement) |
| Workspace | /workspace |
- agentsh -- Runtime security for AI agents (docs)
- agentsh + E2B -- agentsh integration with E2B sandboxes
- agentsh + Daytona -- agentsh integration with Daytona sandboxes
- agentsh + Vercel -- agentsh integration with Vercel Sandbox
- Cloudflare Containers -- Cloudflare's container platform
MIT