8.4 The WebAssembly service contract

When you'd rather write Rust (or another language that compiles to a WASI component) than JavaScript, RS2 runs WebAssembly components through the Wasmtime engine. They satisfy the same host contract as JS services, so grants, limits, and capabilities behave identically.

This is the more advanced of the two engines. If you're coming from front-end JavaScript, the JS contract (8.2) is the easier path — reach for Wasm when you want Rust's performance or type safety, or a non-JS language.

Scaffolding

rs2 new my-svc            # Rust/Wasm scaffold against the published WIT

This generates a Rust project wired to the published WIT world (rs2:service@0.1.0). Importantly, the scaffold compiles to a working component as-is — you start from something that runs, then fill in your logic.

The contract (the WIT world)

A component exports:

  • init(config) — one-time initialization from the mount config.
  • handle(message, config) -> result<message, string> — the request handler; returns a response message or an error string.

…and imports the host capabilities:

  • host.request(...) — the sandbox exit (the Wasm equivalent of ctx.request).
  • host.log(...) — structured logging.
  • host.state-get(...) / host.state-put(...) — durable per-version state.

The message and capability shapes mirror the JS contract; the same default-deny grants (8.5) apply.

Building

rustup target add wasm32-wasip2          # once
cargo build --target wasm32-wasip2 --release

The output *.wasm component is what you deploy (8.6).

Bodies materialize at the boundary

In v1, message bodies materialize at the component boundary — there's no streaming through the Wasm sandbox (an open question pending component-model streams). For typical request/response handlers this is invisible; just be aware very large bodies are buffered (and subject to the materialization cap) when they cross into a Wasm service.

Validating before deploy

rs2 test my-svc            # checks manifest + the built component

rs2 test validates the manifest (name, engine, effect classes, capabilities) and the component (wasm header; a full engine compile-check when the CLI is built with --features wasm). The server also compile-checks at deploy time (8.6).

JS or Wasm?

JavaScript WebAssembly
Language JS/TS Rust (or any → WASI)
Best for SDKs, quick handlers, web devs Performance, type safety, non-JS
Streaming through sandbox n/a (synchronous) not in v1 (materialized)
Toolchain esbuild (--bundle) cargo + wasm32-wasip2

Both deploy the same way and share grants and limits. Next: capability grants — how a service is allowed to reach anything at all.


Previous: 8.3 The supported API surface · Manual home · Next: 8.5 Capability grants →