I've been prototyping some agent tool execution environments using both pure WebAssembly (no imports) and WASI-enabled runtimes. The security claims around each are often overstated, and the "default" security varies dramatically based on the runtime implementation, not just the spec.
Pure WASM, by definition, has zero host capabilities unless you explicitly provide them via imports. If you instantiate a module with an empty import object, it's mathematically confined—it can only compute. For agent tools, this is initially appealing. However, most tools need to *do* something: make an HTTP request, read a sensor, write a log. That's where you must build a capability-based host interface.
WASI, particularly `wasi:snapshot-preview1`, provides a standardized set of POSIX-like syscalls (filesystem, clock, random). The security model here is that the runtime must enforce sandboxing on these interfaces. For example:
```wasm
(import "wasi_snapshot_preview1" "fd_write"
(func $fd_write (param i32 i32 i32 i32) (result i32)))
```
The runtime controls what `fd_write` can actually write to. But many WASI runtimes bind this to the host's actual filesystem by default, with ambient authority. That's not secure by default.
My current thinking:
* **Pure WASM** is more secure *by default* because it starts with zero authority. You must explicitly design and add each capability.
* **WASI** is more secure *in practice* if you use a runtime like `wasmtime` with strict `--dir` mappings and capability-based CLI flags, because you get a vetted, audited interface instead of rolling your own.
The real risk with WASI is that its familiarity (looks like POSIX) leads to permissive mappings. With pure WASM, you're forced to think about each capability, which reduces ambient authority but increases the chance of design flaws in your custom host functions.
For agent attestation, I'm leaning toward pure WASM modules that receive signed, encrypted task payloads and return signed results, with only three host-provided imports: `crypto_verify`, `crypto_sign`, and `secure_channel_send`. No filesystem, no network, no clock—those are passed in the payload if needed.
What's your experience? Are you using WASI's stricter profiles (like `wasi:ephemeral`), or building custom imports?