https://github.com/AjaxZhan/AgentFense
Least-privilege filesystem sandbox & context guardrails for AI agents
https://github.com/AjaxZhan/AgentFense
agent bash bubblewrap bwrap context-engineering fuse permission sandbox
Last synced: about 5 hours ago
JSON representation
Least-privilege filesystem sandbox & context guardrails for AI agents
- Host: GitHub
- URL: https://github.com/AjaxZhan/AgentFense
- Owner: AjaxZhan
- License: mit
- Created: 2026-01-29T03:30:21.000Z (5 months ago)
- Default Branch: main
- Last Pushed: 2026-02-03T05:59:39.000Z (5 months ago)
- Last Synced: 2026-06-22T16:05:39.579Z (8 days ago)
- Topics: agent, bash, bubblewrap, bwrap, context-engineering, fuse, permission, sandbox
- Language: Go
- Homepage: https://ajaxzhan.github.io/AgentFense/
- Size: 636 KB
- Stars: 6
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Security: docs/security/best-practices.md
Awesome Lists containing this project
README
# AgentFense
[](LICENSE)
[](https://pypi.org/project/agentfense/)
[](https://github.com/AjaxZhan/AgentFense/releases)
[](https://github.com/libfuse/libfuse)
> 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
```python
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:
```python
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:
```python
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/`](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
```bash
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
```bash
pip install -e sdk/python/
```
## Usage
### High-Level API (Recommended)
```python
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:
```python
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 |
```python
from agentfense import list_presets, extend_preset
# Extend a preset
rules = extend_preset("agent-safe", additions=[
{"pattern": "/custom/**", "permission": "write"}
])
```
### Error Handling
```python
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:
```python
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
```bash
# 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
```bash
# 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/`](example/ticket-agent/) | Interactive AI agent with permission demo (write/read/view/none) |
## References
- [FUSE is All You Need](https://jakobemmerling.de/posts/fuse-is-all-you-need/)
- [Anthropic's Agent Skill](https://github.com/anthropics/skills)
- [Agfs](https://github.com/c4pt0r/agfs)
- [OpenViking](https://github.com/volcengine/OpenViking)
## License
MIT