Skip to content

AjaxZhan/AgentFense

Repository files navigation

AgentFense

License: MIT PyPI Release FUSE

Least-privilege filesystem sandbox & context guardrails for AI agents

Run untrusted AI agent code against a real codebase while enforcing least-privilege access at the file level.

Motivation

The best agent interface remains simple: bash + filesystem. With FUSE, you can mount any world and make an agent productive with plain ls, cat, grep, and find.

But there's a gap: filesystems are usually all-or-nothing. Mount a real repo, and you often expose everything—including secrets.

AgentFense fills that gap with four permission levels:

Level What the agent can do
none Path is invisible (hidden from ls, behaves like it doesn't exist)
view Can list names (ls), but cannot read file content
read Can read file content
write Can read + modify / create files

Example policy: "You can edit /docs, see /metadata, read everything else, but /secrets does not exist."

Quick Start

from agentfense import Sandbox

# One-liner: create sandbox from local directory with "agent-safe" preset
with Sandbox.from_local("./my-project") as sandbox:
    result = sandbox.run("python main.py")
    print(result.stdout)

The agent-safe preset: read all files, write to /output and /tmp, hide secrets (.env, *.key, etc.).

For custom permissions:

sandbox = client.create_sandbox(
    codebase_id=codebase.id,
    permissions=[
        {"pattern": "**/*", "permission": "read"},           # Default: read-only
        {"pattern": "/docs/**", "permission": "write"},      # Writable
        {"pattern": "/metadata/**", "permission": "view"},   # List-only
        {"pattern": "/secrets/**", "permission": "none"},    # Hidden
    ]
)

AI Agent Example

Build secure AI agents that execute bash commands with permission control:

from anthropic import Anthropic
from agentfense import Sandbox

# Define what the agent can access
PERMISSIONS = [
    {"pattern": "**/*", "permission": "read"},      # Read all by default
    {"pattern": "output/*", "permission": "write"}, # Can write to output/
    {"pattern": ".env", "permission": "none"},      # Hide secrets
]

client = Anthropic()

with Sandbox.from_local("./project", permissions=PERMISSIONS) as sandbox:
    # Agent generates bash command
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        messages=[{"role": "user", "content": "List all Python files"}],
        system="Output bash commands in ```bash``` blocks."
    )
    
    # Execute safely in sandbox - permissions enforced at filesystem level
    cmd = extract_command(response)  # e.g., "find . -name '*.py'"
    result = sandbox.run(cmd)
    print(result.stdout)

The agent cannot access .env even if it tries - the file is invisible at the filesystem level.

See example/ticket-agent/ for a complete interactive demo.

Features

  • Fine-grained permissions: none / view / read / write with glob patterns
  • Lightweight isolation: bubblewrap (bwrap) for fast startup
  • Docker runtime: Full isolation with custom images and resource limits
  • Delta Layer (COW): Copy-On-Write isolation for multi-sandbox write safety
  • Stateful sessions: Persistent shell with working directory and environment
  • Async SDK: Full async/await support for high-concurrency scenarios
  • Permission presets: Built-in presets (agent-safe, read-only, full-access)

Installation

Server

git clone https://github.com/AjaxZhan/AgentFense.git
cd AgentFense

go mod tidy
go build -o bin/agentfense-server ./cmd/agentfense-server

# Start (gRPC :9000, REST :8080)
./bin/agentfense-server -config configs/agentfense-server.yaml

Prerequisites: Go 1.21+, bubblewrap (bwrap)

Python SDK

pip install -e sdk/python/

Usage

High-Level API (Recommended)

from agentfense import Sandbox, RuntimeType, ResourceLimits

# Basic usage
with Sandbox.from_local("./my-project") as sandbox:
    result = sandbox.run("python main.py")
    print(result.stdout)

# With Docker and resource limits
with Sandbox.from_local(
    "./my-project",
    preset="agent-safe",
    runtime=RuntimeType.DOCKER,
    image="python:3.11-slim",
    resources=ResourceLimits(memory_bytes=512 * 1024 * 1024, pids_limit=100),
) as sandbox:
    with sandbox.session() as session:
        session.exec("pip install -r requirements.txt")
        result = session.exec("pytest")
        print(result.stdout)

Async SDK

For high-concurrency scenarios, use the async API:

import asyncio
from agentfense import AsyncSandbox

async def main():
    async with await AsyncSandbox.from_local("./my-project") as sandbox:
        result = await sandbox.run("python main.py")
        print(result.stdout)
        
        # Async sessions
        async with sandbox.session() as session:
            await session.exec("cd /workspace")
            result = await session.exec("npm test")

asyncio.run(main())

The async SDK provides the same API as the sync version, with await for all operations.

Permission Presets

Preset Description
agent-safe Read all, write to /output & /tmp, hide secrets
read-only Read all files, no write access
full-access Full read/write access
development Full access except secrets
from agentfense import list_presets, extend_preset

# Extend a preset
rules = extend_preset("agent-safe", additions=[
    {"pattern": "/custom/**", "permission": "write"}
])

Error Handling

from agentfense import Sandbox, CommandTimeoutError, CommandExecutionError

try:
    with Sandbox.from_local("./project") as sandbox:
        result = sandbox.run("python main.py", timeout=30, raise_on_error=True)
except CommandTimeoutError:
    print("Command timed out")
except CommandExecutionError as e:
    print(f"Failed (exit {e.exit_code}): {e.stderr}")

Low-Level API

For full control, use SandboxClient directly:

from agentfense import SandboxClient

client = SandboxClient(endpoint="localhost:9000")

# Create codebase → upload files → create sandbox → start → exec → cleanup
codebase = client.create_codebase(name="my-project", owner_id="user_001")
client.upload_file(codebase.id, "main.py", b"print('hello')")

sandbox = client.create_sandbox(
    codebase_id=codebase.id,
    permissions=[{"pattern": "**/*", "permission": "read"}],
)
client.start_sandbox(sandbox.id)

result = client.exec(sandbox.id, command="python /workspace/main.py")
print(result.stdout)

client.destroy_sandbox(sandbox.id)
client.delete_codebase(codebase.id)

REST API

# Create codebase
curl -X POST http://localhost:8080/v1/codebases \
  -d '{"name": "my-project", "owner_id": "user_001"}'

# Create sandbox
curl -X POST http://localhost:8080/v1/sandboxes \
  -d '{"codebase_id": "cb_xxx", "permissions": [{"pattern": "**/*", "permission": "PERMISSION_READ"}]}'

# Start → exec → cleanup
curl -X POST http://localhost:8080/v1/sandboxes/sb_xxx/start
curl -X POST http://localhost:8080/v1/sandboxes/sb_xxx/exec -d '{"command": "ls /workspace"}'
curl -X DELETE http://localhost:8080/v1/sandboxes/sb_xxx

Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Client Layer                           │
│              Go SDK / Python SDK / REST API                 │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                     Service Layer                           │
│          gRPC Server + REST Gateway (grpc-gateway)          │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                     Runtime Layer                           │
│    Sandbox Manager │ Permission Engine │ Executor           │
└─────────────────────────┬───────────────────────────────────┘
                          │
┌─────────────────────────▼───────────────────────────────────┐
│                   Isolation Layer                           │
│  bwrap Runtime │ Docker Runtime │ FUSE FS │ Delta Layer     │
└─────────────────────────────────────────────────────────────┘

Delta Layer (COW): Multiple sandboxes can share the same codebase with isolated writes. Each sandbox writes to its own delta directory; changes sync to source on completion (Last-Writer-Wins).

Performance

The architecture is designed to be lightweight. Each sandbox consumes minimal resources:

Component Per-Sandbox Overhead
Memory ~5 MB
Processes ~2
FUSE mount 1
Docker container 1 (Docker runtime only)

Stress test results on a 2-core / 4GB RAM server (Docker runtime):

Metric Result
Max concurrent sandboxes 100+ (tested up to 120)
Memory at 100 sandboxes ~67% usage
Stability No crashes, clean resource cleanup

Recommended capacity (conservative):

Server Spec Suggested Max Sandboxes
2 cores / 4 GB 50–80
4 cores / 8 GB 150–200
8 cores / 16 GB 400+

The bottleneck is typically memory, not CPU or FUSE. For higher concurrency, consider sandbox pooling or on-demand creation.

Comparison

Capability AgentFense E2B Docker Others
Path-based least privilege ✅ (glob + priority) ⚠️ coarse ⚠️ varies
Hidden paths (none) ✅ invisible ⚠️ varies
List-only paths (view)
Multi-sandbox codebase sharing ⚠️ ⚠️ ⚠️ varies

Roadmap

Completed: Session support, Docker runtime, resource limits, Delta Layer (COW), one-liner API, permission presets, semantic exceptions, async SDK.

Next: CLI tool, Go SDK, configuration files, file locking, agent communication.

Out of scope: MicroVM isolation, hibernate/wake (CRIU), million-scale concurrency.

Development

# Run tests
go test ./...

# With coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

Known Limitations

macOS + Docker Desktop

The view permission level may not work correctly on macOS with Docker Desktop due to VirtioFS limitations. Files appear as "No such file" inside containers.

Workarounds: Use Linux, use read instead of view, or use bwrap runtime.

Permission Linux macOS (Docker Desktop)
none
view
read
write

Examples

Example Description
example/ticket-agent/ Interactive AI agent with permission demo (write/read/view/none)

References

License

MIT

About

Least-privilege filesystem sandbox & context guardrails for AI agents

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors