Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

wasm-agent

A wasm-only coding agent: file tools plus exactly one execution tool, wasmtime_exec. There is no shell — the only way to observe program behaviour is to write Rust and compile to WebAssembly.

Execution is super fast because there is no external process. wasmtime_exec embeds the wasmtime/wasmtime-wasi crates directly.

Sandboxing

  • No ambient authority. By default a compiled guest only gets WASI stdio (println!/eprintln!, captured into in-memory pipes, not inherited from the host process) and argv. No filesystem, no network, no clock/random beyond what WASI exposes, unless the agent explicitly asks for it on that call.
  • Filesystem access is per-call and explicit. wasmtime_exec's dirs= preopens exactly the host directories named, at the guest paths named (src/tools/wasm_exec.rs). Nothing outside those directories is reachable — there's no general filesystem root.
  • Network access is host-bounded. This embeds a custom host stdlib (wasm_agent_host: http_request/http_read, src/host_stdlib.rs) exposing whitelist-gated http_get/http_post to the guest (assets/wasm_agent_std.rs). The whitelist is enforced twice: the import module is only linked into the Linker when --http-allow is non-empty, so a module built without a whitelist configured fails to instantiate (unresolved import); and every request's host is checked against the allowlist before any HTTP call is made, so a request to a non-whitelisted host never reaches the network (-2, zero requests — see the tests in src/tools/wasm_exec.rs).
  • Deterministic execution bounds. Instead of a wall-clock timeout, wasmtime_exec runs with fuel metering (Config::consume_fuel, store.set_fuel, default 20,000,000,000 instructions, --fuel / $WASM_AGENT_FUEL). An exhausted budget traps at an instruction boundary — the same clean trap as any other fault, nothing half-applied, nothing left running.

Limitations

The build step is not sandboxed — this is the biggest misalignment risk in this design. compile_rust_to_wasm runs rustc directly on the host (src/tools/rustc.rs): a plain Command::new(rustc), same user, same filesystem, same environment as the wasm-agent process itself, bounded only by a 300s wall-clock timeout — no fuel, no WASI, none of the guarantees described above. Everything in Sandboxing applies to the compiled .wasm, not to the compiler that produces it.

Concretely, at compile time the agent-authored Rust source can:

  • read arbitrary host files/env vars via include!, include_str!, env!, option_env!, etc., and have their contents show up in compiler diagnostics or get baked into the .wasm binary — a way to leak host information into the sandbox loop that bypasses dirs= and --http-allow entirely, since those only gate the guest, not the compiler;
  • in principle, run arbitrary unsandboxed code at compile time via build.rs build scripts or proc-macro crates, which execute as part of compilation, not inside wasmtime. This build currently has no cargo/build.rs path at all — compile_rust_to_wasm's optional deps= only adds -L dependency=... search paths for source pulled in via #[path] (following the original wasm_agent.py's "no cargo, no build scripts" approach), and there's no cargo_install tool in this build to vendor a crate in the first place — but that's a property of what's wired up today, not a guarantee enforced by the sandbox. Adding a Cargo-driven build path later would reopen this.

None of this is mitigated by wasmtime, fuel, or the HTTP whitelist — those only constrain the guest module. Treat rustc invocations the way you'd treat any other untrusted-input-to-a-trusted-compiler pipeline: it currently runs with the same privileges as the agent process, on whatever host it runs on.

Origin

This is a Rust rewrite and extension of wasm_agent.py There is more info at https://www.monperrus.net/martin/wasm-agent.

Build

cargo build --release

Run

cargo run -- "write a Rust program, compile it to wasm, and run it"
cargo run -- --demo                              # self-contained demo, no API key
cargo run -- --demo --http-allow example.com     # demo including the http_get step

The model defaults to glm-5.2 on the z.ai coding endpoint. The API key is resolved from --api-key, $WASM_AGENT_API_KEY, or the OS keyring (service z.ai, username api_key).

Pass --http-allow domain[,domain...] to let the compiled guest module call http_get/http_post (see assets/wasm_agent_std.rs) against those domains; without it the guest has no network access at all.

About

Wasm-only coding agent: file tools + compile_rust_to_wasm + wasmtime_exec, wasmtime embedded in-process (no shell, no CLI subprocess).

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages