{"id":51289303,"url":"https://github.com/AjaxZhan/AgentFense","last_synced_at":"2026-06-30T09:00:41.227Z","repository":{"id":335259048,"uuid":"1144783425","full_name":"AjaxZhan/AgentFense","owner":"AjaxZhan","description":"Least-privilege filesystem sandbox \u0026 context guardrails for AI agents","archived":false,"fork":false,"pushed_at":"2026-02-03T05:59:39.000Z","size":651,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T16:05:39.579Z","etag":null,"topics":["agent","bash","bubblewrap","bwrap","context-engineering","fuse","permission","sandbox"],"latest_commit_sha":null,"homepage":"https://ajaxzhan.github.io/AgentFense/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AjaxZhan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security/best-practices.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-29T03:30:21.000Z","updated_at":"2026-02-03T05:59:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/AjaxZhan/AgentFense","commit_stats":null,"previous_names":["ajaxzhan/sandbox-rls","ajaxzhan/agentfense"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/AjaxZhan/AgentFense","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AjaxZhan%2FAgentFense","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AjaxZhan%2FAgentFense/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AjaxZhan%2FAgentFense/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AjaxZhan%2FAgentFense/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AjaxZhan","download_url":"https://codeload.github.com/AjaxZhan/AgentFense/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AjaxZhan%2FAgentFense/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34959509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","bash","bubblewrap","bwrap","context-engineering","fuse","permission","sandbox"],"created_at":"2026-06-30T09:00:40.051Z","updated_at":"2026-06-30T09:00:41.209Z","avatar_url":"https://github.com/AjaxZhan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AgentFense\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![PyPI](https://img.shields.io/pypi/v/agentfense?logo=pypi\u0026logoColor=white)](https://pypi.org/project/agentfense/)\n[![Release](https://img.shields.io/github/v/release/AjaxZhan/AgentFense?logo=github)](https://github.com/AjaxZhan/AgentFense/releases)\n[![FUSE](https://img.shields.io/badge/FUSE-filesystem-orange)](https://github.com/libfuse/libfuse)\n\n\u003e Least-privilege filesystem sandbox \u0026 context guardrails for AI agents\n\nRun untrusted AI agent code **against a real codebase** while enforcing **least-privilege access** at the file level.\n\n## Motivation\n\nThe 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`.\n\nBut there's a gap: filesystems are usually **all-or-nothing**. Mount a real repo, and you often expose *everything*—including secrets.\n\nAgentFense fills that gap with four permission levels:\n\n| Level | What the agent can do |\n|-------|------------------------|\n| `none`  | Path is **invisible** (hidden from `ls`, behaves like it doesn't exist) |\n| `view`  | Can **list** names (`ls`), but cannot read file content |\n| `read`  | Can read file content |\n| `write` | Can read + modify / create files |\n\n**Example policy**: *\"You can edit `/docs`, see `/metadata`, read everything else, but `/secrets` does not exist.\"*\n\n## Quick Start\n\n```python\nfrom agentfense import Sandbox\n\n# One-liner: create sandbox from local directory with \"agent-safe\" preset\nwith Sandbox.from_local(\"./my-project\") as sandbox:\n    result = sandbox.run(\"python main.py\")\n    print(result.stdout)\n```\n\nThe `agent-safe` preset: read all files, write to `/output` and `/tmp`, hide secrets (`.env`, `*.key`, etc.).\n\nFor custom permissions:\n\n```python\nsandbox = client.create_sandbox(\n    codebase_id=codebase.id,\n    permissions=[\n        {\"pattern\": \"**/*\", \"permission\": \"read\"},           # Default: read-only\n        {\"pattern\": \"/docs/**\", \"permission\": \"write\"},      # Writable\n        {\"pattern\": \"/metadata/**\", \"permission\": \"view\"},   # List-only\n        {\"pattern\": \"/secrets/**\", \"permission\": \"none\"},    # Hidden\n    ]\n)\n```\n\n## AI Agent Example\n\nBuild secure AI agents that execute bash commands with permission control:\n\n```python\nfrom anthropic import Anthropic\nfrom agentfense import Sandbox\n\n# Define what the agent can access\nPERMISSIONS = [\n    {\"pattern\": \"**/*\", \"permission\": \"read\"},      # Read all by default\n    {\"pattern\": \"output/*\", \"permission\": \"write\"}, # Can write to output/\n    {\"pattern\": \".env\", \"permission\": \"none\"},      # Hide secrets\n]\n\nclient = Anthropic()\n\nwith Sandbox.from_local(\"./project\", permissions=PERMISSIONS) as sandbox:\n    # Agent generates bash command\n    response = client.messages.create(\n        model=\"claude-sonnet-4-20250514\",\n        messages=[{\"role\": \"user\", \"content\": \"List all Python files\"}],\n        system=\"Output bash commands in ```bash``` blocks.\"\n    )\n    \n    # Execute safely in sandbox - permissions enforced at filesystem level\n    cmd = extract_command(response)  # e.g., \"find . -name '*.py'\"\n    result = sandbox.run(cmd)\n    print(result.stdout)\n```\n\nThe agent cannot access `.env` even if it tries - the file is invisible at the filesystem level.\n\nSee [`example/ticket-agent/`](example/ticket-agent/) for a complete interactive demo.\n\n## Features\n\n- **Fine-grained permissions**: `none` / `view` / `read` / `write` with glob patterns\n- **Lightweight isolation**: bubblewrap (`bwrap`) for fast startup\n- **Docker runtime**: Full isolation with custom images and resource limits\n- **Delta Layer (COW)**: Copy-On-Write isolation for multi-sandbox write safety\n- **Stateful sessions**: Persistent shell with working directory and environment\n- **Async SDK**: Full async/await support for high-concurrency scenarios\n- **Permission presets**: Built-in presets (`agent-safe`, `read-only`, `full-access`)\n\n## Installation\n\n### Server\n\n```bash\ngit clone https://github.com/AjaxZhan/AgentFense.git\ncd AgentFense\n\ngo mod tidy\ngo build -o bin/agentfense-server ./cmd/agentfense-server\n\n# Start (gRPC :9000, REST :8080)\n./bin/agentfense-server -config configs/agentfense-server.yaml\n```\n\n**Prerequisites**: Go 1.21+, bubblewrap (`bwrap`)\n\n### Python SDK\n\n```bash\npip install -e sdk/python/\n```\n\n## Usage\n\n### High-Level API (Recommended)\n\n```python\nfrom agentfense import Sandbox, RuntimeType, ResourceLimits\n\n# Basic usage\nwith Sandbox.from_local(\"./my-project\") as sandbox:\n    result = sandbox.run(\"python main.py\")\n    print(result.stdout)\n\n# With Docker and resource limits\nwith Sandbox.from_local(\n    \"./my-project\",\n    preset=\"agent-safe\",\n    runtime=RuntimeType.DOCKER,\n    image=\"python:3.11-slim\",\n    resources=ResourceLimits(memory_bytes=512 * 1024 * 1024, pids_limit=100),\n) as sandbox:\n    with sandbox.session() as session:\n        session.exec(\"pip install -r requirements.txt\")\n        result = session.exec(\"pytest\")\n        print(result.stdout)\n```\n\n### Async SDK\n\nFor high-concurrency scenarios, use the async API:\n\n```python\nimport asyncio\nfrom agentfense import AsyncSandbox\n\nasync def main():\n    async with await AsyncSandbox.from_local(\"./my-project\") as sandbox:\n        result = await sandbox.run(\"python main.py\")\n        print(result.stdout)\n        \n        # Async sessions\n        async with sandbox.session() as session:\n            await session.exec(\"cd /workspace\")\n            result = await session.exec(\"npm test\")\n\nasyncio.run(main())\n```\n\nThe async SDK provides the same API as the sync version, with `await` for all operations.\n\n### Permission Presets\n\n| Preset | Description |\n|--------|-------------|\n| `agent-safe` | Read all, write to `/output` \u0026 `/tmp`, hide secrets |\n| `read-only` | Read all files, no write access |\n| `full-access` | Full read/write access |\n| `development` | Full access except secrets |\n\n```python\nfrom agentfense import list_presets, extend_preset\n\n# Extend a preset\nrules = extend_preset(\"agent-safe\", additions=[\n    {\"pattern\": \"/custom/**\", \"permission\": \"write\"}\n])\n```\n\n### Error Handling\n\n```python\nfrom agentfense import Sandbox, CommandTimeoutError, CommandExecutionError\n\ntry:\n    with Sandbox.from_local(\"./project\") as sandbox:\n        result = sandbox.run(\"python main.py\", timeout=30, raise_on_error=True)\nexcept CommandTimeoutError:\n    print(\"Command timed out\")\nexcept CommandExecutionError as e:\n    print(f\"Failed (exit {e.exit_code}): {e.stderr}\")\n```\n\n### Low-Level API\n\nFor full control, use `SandboxClient` directly:\n\n```python\nfrom agentfense import SandboxClient\n\nclient = SandboxClient(endpoint=\"localhost:9000\")\n\n# Create codebase → upload files → create sandbox → start → exec → cleanup\ncodebase = client.create_codebase(name=\"my-project\", owner_id=\"user_001\")\nclient.upload_file(codebase.id, \"main.py\", b\"print('hello')\")\n\nsandbox = client.create_sandbox(\n    codebase_id=codebase.id,\n    permissions=[{\"pattern\": \"**/*\", \"permission\": \"read\"}],\n)\nclient.start_sandbox(sandbox.id)\n\nresult = client.exec(sandbox.id, command=\"python /workspace/main.py\")\nprint(result.stdout)\n\nclient.destroy_sandbox(sandbox.id)\nclient.delete_codebase(codebase.id)\n```\n\n### REST API\n\n```bash\n# Create codebase\ncurl -X POST http://localhost:8080/v1/codebases \\\n  -d '{\"name\": \"my-project\", \"owner_id\": \"user_001\"}'\n\n# Create sandbox\ncurl -X POST http://localhost:8080/v1/sandboxes \\\n  -d '{\"codebase_id\": \"cb_xxx\", \"permissions\": [{\"pattern\": \"**/*\", \"permission\": \"PERMISSION_READ\"}]}'\n\n# Start → exec → cleanup\ncurl -X POST http://localhost:8080/v1/sandboxes/sb_xxx/start\ncurl -X POST http://localhost:8080/v1/sandboxes/sb_xxx/exec -d '{\"command\": \"ls /workspace\"}'\ncurl -X DELETE http://localhost:8080/v1/sandboxes/sb_xxx\n```\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                      Client Layer                           │\n│              Go SDK / Python SDK / REST API                 │\n└─────────────────────────┬───────────────────────────────────┘\n                          │\n┌─────────────────────────▼───────────────────────────────────┐\n│                     Service Layer                           │\n│          gRPC Server + REST Gateway (grpc-gateway)          │\n└─────────────────────────┬───────────────────────────────────┘\n                          │\n┌─────────────────────────▼───────────────────────────────────┐\n│                     Runtime Layer                           │\n│    Sandbox Manager │ Permission Engine │ Executor           │\n└─────────────────────────┬───────────────────────────────────┘\n                          │\n┌─────────────────────────▼───────────────────────────────────┐\n│                   Isolation Layer                           │\n│  bwrap Runtime │ Docker Runtime │ FUSE FS │ Delta Layer     │\n└─────────────────────────────────────────────────────────────┘\n```\n\n**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).\n\n## Performance\n\nThe architecture is designed to be lightweight. Each sandbox consumes minimal resources:\n\n| Component | Per-Sandbox Overhead |\n|-----------|---------------------|\n| Memory | ~5 MB |\n| Processes | ~2 |\n| FUSE mount | 1 |\n| Docker container | 1 (Docker runtime only) |\n\n**Stress test results** on a 2-core / 4GB RAM server (Docker runtime):\n\n| Metric | Result |\n|--------|--------|\n| Max concurrent sandboxes | **100+** (tested up to 120) |\n| Memory at 100 sandboxes | ~67% usage |\n| Stability | No crashes, clean resource cleanup |\n\n**Recommended capacity** (conservative):\n\n| Server Spec | Suggested Max Sandboxes |\n|-------------|------------------------|\n| 2 cores / 4 GB | 50–80 |\n| 4 cores / 8 GB | 150–200 |\n| 8 cores / 16 GB | 400+ |\n\nThe bottleneck is typically memory, not CPU or FUSE. For higher concurrency, consider sandbox pooling or on-demand creation.\n\n## Comparison\n\n| Capability | AgentFense | E2B | Docker | Others |\n|------------|-------------|-----|--------|--------|\n| Path-based least privilege | ✅ (glob + priority) | ❌ | ⚠️ coarse | ⚠️ varies |\n| Hidden paths (`none`) | ✅ invisible | ❌ | ❌ | ⚠️ varies |\n| List-only paths (`view`) | ✅ | ❌ | ❌ | ❌ |\n| Multi-sandbox codebase sharing | ✅ | ⚠️ | ⚠️ | ⚠️ varies |\n\n## Roadmap\n\n**Completed**: Session support, Docker runtime, resource limits, Delta Layer (COW), one-liner API, permission presets, semantic exceptions, async SDK.\n\n**Next**: CLI tool, Go SDK, configuration files, file locking, agent communication.\n\n**Out of scope**: MicroVM isolation, hibernate/wake (CRIU), million-scale concurrency.\n\n\n\n## Development\n\n```bash\n# Run tests\ngo test ./...\n\n# With coverage\ngo test -coverprofile=coverage.out ./...\ngo tool cover -html=coverage.out\n```\n\n## Known Limitations\n\n### macOS + Docker Desktop\n\nThe `view` permission level may not work correctly on macOS with Docker Desktop due to VirtioFS limitations. Files appear as \"No such file\" inside containers.\n\n**Workarounds**: Use Linux, use `read` instead of `view`, or use `bwrap` runtime.\n\n| Permission | Linux | macOS (Docker Desktop) |\n|------------|-------|------------------------|\n| `none` | ✅ | ✅ |\n| `view` | ✅ | ❌ |\n| `read` | ✅ | ✅ |\n| `write` | ✅ | ✅ |\n\n## Examples\n\n| Example | Description |\n|---------|-------------|\n| [`example/ticket-agent/`](example/ticket-agent/) | Interactive AI agent with permission demo (write/read/view/none) |\n\n## References\n\n- [FUSE is All You Need](https://jakobemmerling.de/posts/fuse-is-all-you-need/)\n- [Anthropic's Agent Skill](https://github.com/anthropics/skills)\n- [Agfs](https://github.com/c4pt0r/agfs)\n- [OpenViking](https://github.com/volcengine/OpenViking)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAjaxZhan%2FAgentFense","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAjaxZhan%2FAgentFense","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAjaxZhan%2FAgentFense/lists"}