WASM Tools Runtime
May 20, 2026 ยท View on GitHub
Lemon supports Ironclaw-compatible WASM tools through a per-session Rust sidecar runtime.
The Foundry/cast command tools under native/wasm-tools/ share a support crate at native/wasm-tools/common. That crate owns common JSON param parsing, Ethereum address validation, secret placeholder injection, and host command execution/error formatting so credential-handling logic is reviewed in one place.
Scope
- ABI: strict
tool.witparity (copied from Ironclaw). - Sidecar lifecycle: one runtime process per
CodingAgent.Session. - Tool registration: one Lemon tool per discovered WASM module.
- Capabilities schema: Ironclaw-style top-level
http,secrets,tool_invoke,workspace,auth. - Precedence: built-in > WASM > extension.
- Output trust: all WASM tool results are marked
trust: :untrusted.
Enablement
WASM tools are opt-in and disabled by default.
[runtime.tools.wasm]
enabled = true
auto_build = true
runtime_path = ""
tool_paths = []
default_memory_limit = 10485760
default_timeout_ms = 60000
default_fuel_limit = 10000000
cache_compiled = true
cache_dir = ""
max_tool_invoke_depth = 4
Discovery roots:
<cwd>/.lemon/wasm-tools~/.lemon/agent/wasm-toolsruntime.tools.wasm.tool_paths
Each module is discovered as:
<name>.wasm- Optional
<name>.capabilities.json
Runtime Build
If runtime_path is unset, Lemon expects the runtime binary at:
<repo>/_build/lemon-wasm-runtime/release/lemon-wasm-runtime
With auto_build = true, Lemon runs:
CARGO_TARGET_DIR=<repo>/_build/lemon-wasm-runtime cargo build --release --manifest-path <repo>/native/lemon-wasm-runtime/Cargo.toml
Manual build example:
CARGO_TARGET_DIR=_build/lemon-wasm-runtime cargo build --release --manifest-path native/lemon-wasm-runtime/Cargo.toml
Troubleshooting
extensions_statusincludes WASM runtime state (enabled,running, discovered tools, warnings/errors).- If runtime build fails, Lemon logs a warning and disables WASM tools for that session.
- If discovery fails, non-WASM tools still load and the session continues.
- If a WASM tool name conflicts:
- Built-in tools always win.
- WASM tools shadow extension tools.
- Conflicts are logged and included in
extensions_status.
- If host tool callbacks fail (
tool-invoketo non-WASM tools), the runtime returns an error to the caller tool.
Lifecycle Proof
Run the focused sidecar lifecycle proof with:
MIX_ENV=test mix run scripts/live_wasm_lifecycle_smoke.exs
The proof writes .lemon/proofs/wasm-lifecycle-latest.json and verifies
redacted discover/invoke start-stop telemetry, running sidecar status, explicit
stop termination, and omission of raw cwd, session id, tool name, and params
from lifecycle telemetry. extensions.status, Web /ops, and
mix lemon.doctor --verbose surface it as extensions.wasm_lifecycle.
Security Model (v1)
tool_authreads WASMauthmetadata and helps set up credentials.secret-existschecks Lemon secret store first, then environment fallback.workspace-readis path-normalized and restricted by allowed prefixes.http-requestis allowlist/rate-limited by capabilities.tool-invokeis alias-based and depth/rate-limited.- Direct workspace write imports are intentionally not exposed; write/edit/patch behavior must go through host tool aliases.