{"schemaVersion":1,"id":"https://lucioduran.com/projects/agent-sandbox-boundaries","url":"https://lucioduran.com/projects/agent-sandbox-boundaries","title":"Agent Sandbox Boundaries","description":"An ephemeral Wasmtime runner that makes CPU, wall-time, guest-memory, filesystem and network capability limits observable.","language":"en","datePublished":"2026-11-09","dateModified":"2026-11-09","contentFormat":"text/markdown","content":"[Projects](<https://lucioduran.com/projects>) / Agent Sandbox Boundaries\n\n# Agent Sandbox Boundaries\n\nAn ephemeral Wasmtime runner that makes CPU, wall-time, guest-memory, filesystem and network capability limits observable.\n\nAuthor: [Lucio Durán](<https://lucioduran.com/>) · Python 3.11.9 and Wasmtime 50.0.1 · License: [MIT](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE>)\n\nPage published: 2026-11-09 · Page updated: 2026-11-09\n\n[Repository](<https://github.com/lucioduran/agent-sandbox-boundaries>) · [Original evidence](<https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json>) · [Release v0.1.0](<https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0>)\n\n## Problem and relevance to agents\n\nA timeout on a subprocess does not prevent filesystem access, network egress or memory growth by generated code. The actual isolation mechanism must be explicit.\n\nThe runner executes bounded binary core WebAssembly with a narrow WASI interface and rejects unsupported policy requests instead of ignoring them.\n\n## Architecture\n\n1. **Validate the policy.** Accept only bounded binary Wasm, zero guest disk writes, denied network access and supported guest-memory limits.\n2. **Enforce with Wasmtime.** Fresh worker and store, POSIX CPU limits, instruction fuel, guest memory and table bounds, and a parent wall deadline.\n3. **Withhold ambient capabilities.** WASI Preview 1 receives no inherited environment, arguments, files, directories or sockets. Workers and temporary directories are cleaned up.\n\n- [Design and trust boundaries](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md>)\n\n## What this demonstrates\n\n- Controlled traversal and resource-limit probes against owned synthetic canaries.\n- Explicit failure for unsupported positive disk quotas, network access and process-memory caps.\n- Recorded policy, module hash and observed outcome for every boundary probe.\n\n## Published results\n\nNineteen boundary probes and three unsupported-profile checks ran once on an Intel Xeon W-3223, 48 GiB RAM, Python 3.11.9 and Wasmtime 50.0.1.\n\nThe matrix checks expected statuses and values, canary integrity and absence of created files. CPU and wall probes ran under the shared exclusive benchmark lock.\n\n### Observed isolation boundaries\n\n| Boundary | Observed result |\n| --- | --- |\n| CPU and execution | Fuel exhaustion and OS CPU termination |\n| Wall time | Blocking WASI poll stopped at parent deadline |\n| Guest memory | Growth denied; oversized memory rejected; invalid access trapped |\n| Filesystem | Traversal, canary access and file creation returned bad descriptor |\n| Network | Socket send denied; socket creation import rejected |\n| Lifecycle | Empty arguments and environment; fresh instance state |\n| All recorded outcomes | 19/19 matched |\n| Unsupported profiles | 3/3 rejected |\n\nWasmtime provides the isolation mechanism; the subprocess provides lifecycle and deadline control. These observations are synthetic regression evidence, not a security certification or escape-proof guarantee.\n\n## Reproduce\n\nPython 3.11.9 on macOS or Linux, with pinned Wasmtime 50.0.1. Install the repository requirements before running checks.\n\nThe stress command uses the original shared workspace lock. On another installation, supply an equivalent exclusive lock around the same experiment module. Do not run concurrent stress workloads; use a fresh output path. The non-stress regression command is separate.\n\n### Check the supported boundaries\n\n```\ngit clone https://github.com/lucioduran/agent-sandbox-boundaries.git\ncd agent-sandbox-boundaries\ngit checkout f67487bb807ca84bb1801eaee7cc81f072f71ac4\npython3.11 -m venv .venv\n. .venv/bin/activate\npython -m pip install -r requirements-dev.txt\npython -m pytest -q -m 'not stress'\n```\n\n### Repeat the local experiment\n\n```\nOMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 python3 /Users/luchoduran/Sites/open-source-projects/.coordination/benchmark_lock.py .venv/bin/python -m experiments.reproduce --output build/new-boundaries.json\n```\n\n## Scope and limitations\n\n- Memory bounds cover guest linear memory, not total worker RSS, compiler allocations or all host memory.\n- The runtime and operating system are trusted. A compromised engine is outside this boundary.\n- No writable filesystem mounts, positive disk quotas, output capture or network allowlists are supported. Network evidence checks denied capabilities, not packet-level firewall behavior.\n- Only core Wasm with WASI Preview 1 on macOS and Linux is supported. Windows and Preview 2 components fail closed. Callers must bound concurrency.\n\n- [Related architecture article](<https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless>)\n\n## Sources and revision\n\nThe report records execution time, implementation revision and source/module hashes. The reviewed release contains that evidence. Absolute-canary probe hashes depend on the original temporary canary path.\n\n- Reviewed revision: [` f67487bb807ca84bb1801eaee7cc81f072f71ac4 `](<https://github.com/lucioduran/agent-sandbox-boundaries/commit/f67487bb807ca84bb1801eaee7cc81f072f71ac4>)\n- Revision recorded by the experiment: [` bdffaf85ff10f9e28682592af0c9d041c04862f7 `](<https://github.com/lucioduran/agent-sandbox-boundaries/commit/bdffaf85ff10f9e28682592af0c9d041c04862f7>)\n- Report timestamp (UTC): 2026-11-09T18:32:32.705508+00:00\n- Release published: [v0.1.0](<https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0>) · 2026-11-09T18:34:04Z\n\n- [README and usage](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/README.md>)\n- [Original evidence](<https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json>)\n- [MIT license](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE>)\n- [Design and trust boundaries](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md>)\n- [Results summary](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.md>)\n- [Boundary probes](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/experiments/probes.py>)","headings":[{"level":1,"text":"Agent Sandbox Boundaries","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#title"},{"level":2,"text":"Problem and relevance to agents","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#problem"},{"level":2,"text":"Architecture","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#architecture"},{"level":2,"text":"What this demonstrates","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#demonstrates"},{"level":2,"text":"Published results","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#results"},{"level":3,"text":"Observed isolation boundaries","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#results-Observed%20isolation%20boundaries"},{"level":2,"text":"Reproduce","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce"},{"level":3,"text":"Check the supported boundaries","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce-checks"},{"level":3,"text":"Repeat the local experiment","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce-experiment"},{"level":2,"text":"Scope and limitations","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#scope"},{"level":2,"text":"Sources and revision","url":"https://lucioduran.com/projects/agent-sandbox-boundaries#sources"}],"sections":[{"heading":"Agent Sandbox Boundaries","level":1,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries","content":"[Projects](<https://lucioduran.com/projects>) / Agent Sandbox Boundaries"},{"heading":"Agent Sandbox Boundaries","level":1,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#title","content":"An ephemeral Wasmtime runner that makes CPU, wall-time, guest-memory, filesystem and network capability limits observable.\n\nAuthor: [Lucio Durán](<https://lucioduran.com/>) · Python 3.11.9 and Wasmtime 50.0.1 · License: [MIT](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE>)\n\nPage published: 2026-11-09 · Page updated: 2026-11-09\n\n[Repository](<https://github.com/lucioduran/agent-sandbox-boundaries>) · [Original evidence](<https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json>) · [Release v0.1.0](<https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0>)"},{"heading":"Problem and relevance to agents","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#problem","content":"A timeout on a subprocess does not prevent filesystem access, network egress or memory growth by generated code. The actual isolation mechanism must be explicit.\n\nThe runner executes bounded binary core WebAssembly with a narrow WASI interface and rejects unsupported policy requests instead of ignoring them."},{"heading":"Architecture","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#architecture","content":"1. **Validate the policy.** Accept only bounded binary Wasm, zero guest disk writes, denied network access and supported guest-memory limits.\n2. **Enforce with Wasmtime.** Fresh worker and store, POSIX CPU limits, instruction fuel, guest memory and table bounds, and a parent wall deadline.\n3. **Withhold ambient capabilities.** WASI Preview 1 receives no inherited environment, arguments, files, directories or sockets. Workers and temporary directories are cleaned up.\n\n- [Design and trust boundaries](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md>)"},{"heading":"What this demonstrates","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#demonstrates","content":"- Controlled traversal and resource-limit probes against owned synthetic canaries.\n- Explicit failure for unsupported positive disk quotas, network access and process-memory caps.\n- Recorded policy, module hash and observed outcome for every boundary probe."},{"heading":"Published results","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#results","content":"Nineteen boundary probes and three unsupported-profile checks ran once on an Intel Xeon W-3223, 48 GiB RAM, Python 3.11.9 and Wasmtime 50.0.1.\n\nThe matrix checks expected statuses and values, canary integrity and absence of created files. CPU and wall probes ran under the shared exclusive benchmark lock."},{"heading":"Observed isolation boundaries","level":3,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#results-Observed%20isolation%20boundaries","content":"| Boundary | Observed result |\n| --- | --- |\n| CPU and execution | Fuel exhaustion and OS CPU termination |\n| Wall time | Blocking WASI poll stopped at parent deadline |\n| Guest memory | Growth denied; oversized memory rejected; invalid access trapped |\n| Filesystem | Traversal, canary access and file creation returned bad descriptor |\n| Network | Socket send denied; socket creation import rejected |\n| Lifecycle | Empty arguments and environment; fresh instance state |\n| All recorded outcomes | 19/19 matched |\n| Unsupported profiles | 3/3 rejected |\n\nWasmtime provides the isolation mechanism; the subprocess provides lifecycle and deadline control. These observations are synthetic regression evidence, not a security certification or escape-proof guarantee."},{"heading":"Reproduce","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce","content":"Python 3.11.9 on macOS or Linux, with pinned Wasmtime 50.0.1. Install the repository requirements before running checks.\n\nThe stress command uses the original shared workspace lock. On another installation, supply an equivalent exclusive lock around the same experiment module. Do not run concurrent stress workloads; use a fresh output path. The non-stress regression command is separate."},{"heading":"Check the supported boundaries","level":3,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce-checks","content":"```\ngit clone https://github.com/lucioduran/agent-sandbox-boundaries.git\ncd agent-sandbox-boundaries\ngit checkout f67487bb807ca84bb1801eaee7cc81f072f71ac4\npython3.11 -m venv .venv\n. .venv/bin/activate\npython -m pip install -r requirements-dev.txt\npython -m pytest -q -m 'not stress'\n```"},{"heading":"Repeat the local experiment","level":3,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#reproduce-experiment","content":"```\nOMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 python3 /Users/luchoduran/Sites/open-source-projects/.coordination/benchmark_lock.py .venv/bin/python -m experiments.reproduce --output build/new-boundaries.json\n```"},{"heading":"Scope and limitations","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#scope","content":"- Memory bounds cover guest linear memory, not total worker RSS, compiler allocations or all host memory.\n- The runtime and operating system are trusted. A compromised engine is outside this boundary.\n- No writable filesystem mounts, positive disk quotas, output capture or network allowlists are supported. Network evidence checks denied capabilities, not packet-level firewall behavior.\n- Only core Wasm with WASI Preview 1 on macOS and Linux is supported. Windows and Preview 2 components fail closed. Callers must bound concurrency.\n\n- [Related architecture article](<https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless>)"},{"heading":"Sources and revision","level":2,"url":"https://lucioduran.com/projects/agent-sandbox-boundaries#sources","content":"The report records execution time, implementation revision and source/module hashes. The reviewed release contains that evidence. Absolute-canary probe hashes depend on the original temporary canary path.\n\n- Reviewed revision: [` f67487bb807ca84bb1801eaee7cc81f072f71ac4 `](<https://github.com/lucioduran/agent-sandbox-boundaries/commit/f67487bb807ca84bb1801eaee7cc81f072f71ac4>)\n- Revision recorded by the experiment: [` bdffaf85ff10f9e28682592af0c9d041c04862f7 `](<https://github.com/lucioduran/agent-sandbox-boundaries/commit/bdffaf85ff10f9e28682592af0c9d041c04862f7>)\n- Report timestamp (UTC): 2026-11-09T18:32:32.705508+00:00\n- Release published: [v0.1.0](<https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0>) · 2026-11-09T18:34:04Z\n\n- [README and usage](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/README.md>)\n- [Original evidence](<https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json>)\n- [MIT license](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE>)\n- [Design and trust boundaries](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md>)\n- [Results summary](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.md>)\n- [Boundary probes](<https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/experiments/probes.py>)"}],"links":[{"text":"Projects","url":"https://lucioduran.com/projects"},{"text":"Lucio Durán","url":"https://lucioduran.com/"},{"text":"MIT","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE"},{"text":"Repository","url":"https://github.com/lucioduran/agent-sandbox-boundaries"},{"text":"Original evidence","url":"https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json"},{"text":"Release v0.1.0","url":"https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0"},{"text":"Design and trust boundaries","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md"},{"text":"Results summary","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.md"},{"text":"Related architecture article","url":"https://lucioduran.com/blog/unikernels-firecracker-microvm-serverless"},{"text":"f67487bb807ca84bb1801eaee7cc81f072f71ac4","url":"https://github.com/lucioduran/agent-sandbox-boundaries/commit/f67487bb807ca84bb1801eaee7cc81f072f71ac4"},{"text":"bdffaf85ff10f9e28682592af0c9d041c04862f7","url":"https://github.com/lucioduran/agent-sandbox-boundaries/commit/bdffaf85ff10f9e28682592af0c9d041c04862f7"},{"text":"v0.1.0","url":"https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0"},{"text":"README and usage","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/README.md"},{"text":"MIT license","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE"},{"text":"Boundary probes","url":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/experiments/probes.py"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com/"},{"@type":"WebSite","@id":"https://lucioduran.com/#website","url":"https://lucioduran.com/","name":"Lucio Durán","inLanguage":["en","es"],"publisher":{"@id":"https://lucioduran.com/#person"}},{"@type":"WebPage","@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#page","url":"https://lucioduran.com/projects/agent-sandbox-boundaries","name":"Agent Sandbox Boundaries","description":"An ephemeral Wasmtime runner that makes CPU, wall-time, guest-memory, filesystem and network capability limits observable.","inLanguage":"en","author":{"@id":"https://lucioduran.com/#person"},"publisher":{"@id":"https://lucioduran.com/#person"},"isPartOf":{"@id":"https://lucioduran.com/#website"},"isAccessibleForFree":true,"mainEntity":{"@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#software"},"datePublished":"2026-11-09","dateModified":"2026-11-09","breadcrumb":{"@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#breadcrumb"}},{"@type":"SoftwareSourceCode","@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#software","name":"Agent Sandbox Boundaries","description":"An ephemeral Wasmtime runner that makes CPU, wall-time, guest-memory, filesystem and network capability limits observable.","inLanguage":"en","url":"https://lucioduran.com/projects/agent-sandbox-boundaries","codeRepository":"https://github.com/lucioduran/agent-sandbox-boundaries","programmingLanguage":"Python","runtimePlatform":"Python 3.11.9 and Wasmtime 50.0.1","license":"https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/LICENSE","version":"v0.1.0","identifier":{"@type":"PropertyValue","propertyID":"git-commit","value":"f67487bb807ca84bb1801eaee7cc81f072f71ac4"},"author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"isAccessibleForFree":true,"citation":["https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/README.md","https://raw.githubusercontent.com/lucioduran/agent-sandbox-boundaries/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.json","https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/docs/design.md","https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/results/boundaries.md","https://github.com/lucioduran/agent-sandbox-boundaries/blob/f67487bb807ca84bb1801eaee7cc81f072f71ac4/experiments/probes.py","https://github.com/lucioduran/agent-sandbox-boundaries/releases/tag/v0.1.0"],"publisher":{"@id":"https://lucioduran.com/#person"},"mainEntityOfPage":{"@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#page"}},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Projects","item":"https://lucioduran.com/projects"},{"@type":"ListItem","position":2,"name":"Agent Sandbox Boundaries","item":"https://lucioduran.com/projects/agent-sandbox-boundaries"}],"@id":"https://lucioduran.com/projects/agent-sandbox-boundaries#breadcrumb"}]}],"contentHash":"cc540b7f962865cf0609dbf4cf1dc0400d664d73f74434f8cce2ed982a8768e6","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/projects/agent-sandbox-boundaries","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":43775,"contentBytes":6553}}
