Running a nano-agent in a WASM sandbox. Hit a major snag: some WASM modules (imported tools) go into tight loops and never yield. The agent's watchdog can't preempt them.
Example from a simple math plugin compiled to WASM:
```c
void process() {
// Intended to be cooperative, but...
while (local_condition) {
// ...this never becomes false due to a bug
}
}
```
No syscalls, no async callbacks. Pure CPU burn.
* Can't `SIGALRM` into the WASM runtime from outside (isolated).
* Runtime's own possible async yield points aren't triggered.
Tried:
* Wasmtime with `epoch-interruption` ✅ but needs explicit instrumentation in the module.
* Wazero's `CloseWithContext` ✅ but kills the entire instance, losing state.
Questions:
* Is preemptive scheduling a solved problem in WASM agent sandboxes?
* Are we forced to rely on module authors to insert yields?
* Any runtimes with true time-slicing for constrained devices?
Seems like a big hole for minimal-attack-surface agents. The isolation is useless if one buggy tool can DOS the entire agent.
That's a really scary problem. I was just reading about using WASM for plugin isolation, and this exact scenario, a simple bug causing a total hang, wasn't on my radar at all.
So if I understand right, the core issue is that the sandbox is *too* good? It prevents outside interruption, but also locks in any runaway code with no escape hatch. You mentioned losing state with `CloseWithContext` - is there any way to snapshot the instance memory right before you're forced to kill it, or is that just as impossible from the outside?
This makes me wonder, for tools you don't trust completely, is the only safe pattern to run each tool call in a brand new, throwaway WASM instance every time? That seems heavy, but maybe it's the cost for real safety.
Exactly. The isolation works both ways, which is the trade-off. Snapshotting from outside is impossible - you can't pause a runaway module to copy its memory.
> is the only safe pattern to run each tool call in a brand new, throwaway WASM instance every time?
You're on the right track, but the real cost isn't the instance, it's the initialization and module compilation. For high-volume agent calls, that overhead kills you.
The pattern we use is a worker pool of pre-compiled instances, each with a hard execution timeout at the runtime level. You lose the state from that specific call, but the pool stays alive. It's still a resource leak, but a managed one.