Posted by souvik1997 6 hours ago
Agents get a bash-like shell and can only call tools you provide, with constraints you define. No Docker, no subprocess, no SaaS — just pip install amla-sandbox
I'm sad about this bit though:
> Python code is MIT. The WASM binary is proprietary—you can use it with this package but can't extract or redistribute it separately.
On the licensing: totally fair point. Our intention is to open source the WASM too. The binary is closed for now only because we need to clean up the source code before releasing it as open-source. The Python SDK and capability layer are MIT. We wanted to ship something usable now rather than wait. Since the wasm binary runs in wasmtime within an open source harness, it is possible to audit everything going in and out of the wasm blob for security.
Genuinely open to feedback on this. If the split license is a blocker for your use cases, that's useful signal for us.
[1]: https://github.com/sd2k/eryx/
Any chance you could add SQLite?
% uv run --with pyeryx python
Installed 1 package in 1ms
Python 3.14.0 (main, Oct 7 2025, 16:07:00) [Clang 20.1.4 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import eryx
>>> sandbox = eryx.Sandbox()
>>> result = sandbox.execute('''
... print("Hello from the sandbox!")
... x = 2 + 2
... print(f"2 + 2 = {x}")
... ''')
>>> result
ExecuteResult(stdout="Hello from the sandbox!\n2 + 2 = 4", duration_ms=6.83, callback_invocations=0, peak_memory_bytes=Some(16384000))
>>> sandbox.execute('''
... import sqlite3
... print(sqlite3.connect(":memory:").execute("select sqlite_version()").fetchall())
... ''').stdout
Traceback (most recent call last):
File "<python-input-6>", line 1, in <module>
sandbox.execute('''
~~~~~~~~~~~~~~~^^^^
import sqlite3
^^^^^^^^^^^^^^
print(sqlite3.connect(":memory:").execute("select sqlite_version()").fetchall())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
''').stdout
^^^^
eryx.ExecutionError: Traceback (most recent call last):
File "<string>", line 1, in <module>
File "<string>", line 125, in _eryx_exec
File "<user>", line 2, in <module>
File "/python-stdlib/sqlite3/__init__.py", line 57, in <module>
from sqlite3.dbapi2 import *
File "/python-stdlib/sqlite3/dbapi2.py", line 27, in <module>
from _sqlite3 import *
ModuleNotFoundError: No module named '_sqlite3'
Filed a feature request here: https://github.com/eryx-org/eryx/issues/28 uv run --with localsandbox python -c '
from localsandbox import LocalSandbox
with LocalSandbox() as sandbox:
result = sandbox.bash("echo hi")
print(result.stdout)
'
Gave me: Traceback (most recent call last):
File "<string>", line 5, in <module>
result = sandbox.bash("echo hi")
File "/Users/simon/.cache/uv/archive-v0/spFCEHagkq3VTpTyStT-Z/lib/python3.14/site-packages/localsandbox/core.py", line 492, in bash
raise SubprocessCrashed(
...<2 lines>...
)
localsandbox.exceptions.SubprocessCrashed: Node subprocess crashed: error: Failed reading lockfile at '/Users/simon/.cache/uv/archive-v0/spFCEHagkq3VTpTyStT-Z/lib/python3.14/site-packages/localsandbox/shim/deno.lock'
Caused by:
Unsupported lockfile version '5'. Try upgrading Deno or recreating the lockfile
Actually that was with Deno 2.2.10 - I ran "brew upgrade deno" and got Deno 2.6.7 and now it works!Will checkout asterai, thanks for sharing!
I'm pretty happy with Typescript.
For example, say you have a shell script to make a bank transfer. This just makes an API call to your bank.
You can't trust the AI to reliably make a call to your traceability tool, and then to your OTP confirmation gate, and only then to proceed with the bank transfer. This will eventually fail and be compromised.
If you're running your agent on a "composable tool runtime", rather than raw shell for tool calls, you can easily make it so the "transfer $500 to Alice" call always goes through the route trace -> confirm OTP -> validate action. This is configured at build time.
Your alternative with raw shell would be to program the tool itself to follow this workflow, but then you'd end up with a lot of duplicate source code if you have the same workflow for different tool calls.
Of course, any AI agent SDK will let you configure these workflows. But they are locked to their own ecosystems, it's not a global ecosystem like you can achieve with WASM, allowing for interop between components written in any language.
While I think that with their current choice for the runtime will hit some limitations (aka: not really full Python support, partial JS support), I strongly believe using Wasm for sandboxing is the way for the future of containers.
At Wasmer we are working hard to make this model work. I'm incredibly happy to see more people joining on the quest!
https://labs.leaningtech.com/blog/browserpod-beta-announceme...
The main issue that I see with Browserpod is very similar to Emscripten: it's designed to work mainly in the browser, and not outside.
In my view, where Wasm really shines, is for enabling containers that work seamlessly in any of this environments: browsers, servers, or even embedded in apps :)
Wasmer is doing great work—we're using wasmtime on the host side currently but have been following your progress. Excited to see WASM sandboxing become more mainstream for this use case.
That's true, but you'll likely need sockets, pydantic or SQLAlchemy (all of of them require heavy support on the Wasm layer!)
We implemented all the system calls necessary to make networking work (within Wasm), and dynamic linking (so you could import and run pydantic, numpy, gevent and more!)
One nice thing about using AgentFS as the VFS is that it's backed by sqlite so it's very portable - making it easy to fork and resume agent workflows across machines / time.
I really like Amla Sandbox addition of injecting tool calls into the sandbox, which lets the agent generated code interact with the harness provided tools. Very interesting!
I've been working on a couple of things which take a very similar approach, with what seem to be some different tradeoffs:
- eryx [1], which uses a WASI build of CPython to provide a true Python sandbox (similar to componentize-py but supports some form of 'dynamic linking' with either pure Python packages or WASI-compiled native wheels) - conch [2], which embeds the `brush` Rust reimplementation of Bash to provide a similar bash sandbox. This is where I've been struggling with figuring out the best way to do subcommands, right now they just have to be rewritten and compiled in but I'd like to find a way to dynamically link them in similar to the Python package approach...
One other note, WASI's VFS support has been great, I just wish there was more progress on `wasi-tls`, it's tricky to get network access working otherwise...
[1] https://github.com/eryx-org/eryx [2] https://github.com/sd2k/conch
For `jq` specifically we use the excellent `jaq_interpret` crate: https://crates.io/crates/jaq-interpret
curl is interesting. We don't include it currently but we could do it without too much additional effort.
Networking isn't done within the wasm sandbox; we "yield" back to the the caller using what we call "host operations" in order to perform any IO. This keeps the Wasm sandbox minimal and as close to "pure compute" as possible. In fact, the only capabilities we give the WASI runtime is a method to get the current time and to generate random numbers. Since we intercept all external IO, random number generation, time, and the Wasm runtime is just for pure computation, we also get perfect reproducibility. We can replay anything within the sandbox exactly.
Your approach with brush is interesting. Having actual bash semantics rather than "bash-like" is a real advantage for complex scripts. The dynamic linking problem for subcommands is a tough one; have you looked at WASI components for this? Feels like that's where it'll eventually land but the tooling isn't there yet.
Will check out eryx and conch. Thanks for sharing!
I did something very similar to you for networking in eryx too (no networking in conch yet); defined an `eryx:net` interface in WIT and reimplemented the `urllib` module using host networking, which most downstream packages (httpx, requests, etc) use far enough down the stack. It's a tradeoff but I think it's pretty much good enough for most use cases like this, and gives the host full control which is great.
Oh full transparency, the vast majority of conch and eryx were written by Opus 4.5. Being backed by wasmtime and the rather strict Rust compiler is definitely a boon here!
Good to see that you chose a similar path for networking in eryx!
Would love to see your MCP approach if you've published it anywhere.