From ab38b96a7f8b068609397adb6e277dd3abe4dca5 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Wed, 11 Mar 2026 09:42:09 +0100 Subject: [PATCH] docs: document Wasmtime integration plan for weft-runtime Specifies the concrete steps needed to replace the current READY stub with real Wasm module execution: - Optional cargo feature gate (wasmtime-runtime) to keep default builds fast; production service unit uses the feature-enabled binary. - Engine + Module setup from wasm file path. - WasiCtxBuilder with inherited stdout/stderr for READY signal passthrough. - READY signal timing: Option A (print before _start) vs Option B (explicit weft_ready() export); Option A is the initial implementation. - Entry point: _start (standard wasm32-wasi target output). - Error handling: module load failures, trap handling, missing _start. - Explicit non-scope: host imports beyond WASI, memory caps, component model, Wasm threads, fuel metering. - Prerequisite: confirmed wasm32-wasi app SDK before integration. --- .../runtime-wasmtime-integration.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 docs/architecture/runtime-wasmtime-integration.md diff --git a/docs/architecture/runtime-wasmtime-integration.md b/docs/architecture/runtime-wasmtime-integration.md new file mode 100644 index 0000000..313a1ee --- /dev/null +++ b/docs/architecture/runtime-wasmtime-integration.md @@ -0,0 +1,115 @@ +# weft-runtime: Wasmtime Integration Plan + +**Status:** Not yet implemented. weft-runtime currently stubs execution with `println!("READY")`. + +--- + +## Current State + +`crates/weft-runtime/src/main.rs` resolves the package directory, validates `app.wasm` +exists, then prints `READY\n` and exits. No Wasm code is executed. + +The stub satisfies the supervisor contract (described in `docs/architecture/app-package-format.md`) +and allows the full session lifecycle to be tested end-to-end. + +--- + +## Required Changes + +### 1. Optional feature gate + +Add a `wasmtime` cargo feature to `weft-runtime/Cargo.toml` so the default build remains +fast: + +```toml +[features] +default = [] +wasmtime-runtime = ["dep:wasmtime"] + +[dependencies] +wasmtime = { version = "30", optional = true, features = ["wasi"] } +``` + +`cfg(feature = "wasmtime-runtime")` guards the real implementation. +`cfg(not(feature = "wasmtime-runtime"))` keeps the stub for tests and development. + +The production service unit (`infra/systemd/weft-appd.service`) would set +`WEFT_RUNTIME_BIN` to a binary built with `--features wasmtime-runtime`. + +### 2. Engine and module setup + +```rust +use wasmtime::{Config, Engine, Module, Store}; +use wasmtime_wasi::{WasiCtxBuilder, preview1}; + +let engine = Engine::new(Config::default())?; +let module = Module::from_file(&engine, &wasm_path)?; +``` + +### 3. WASI context + +```rust +let wasi = WasiCtxBuilder::new() + .inherit_stdout() + .inherit_stderr() + .build(); +let mut store = Store::new(&engine, wasi); +``` + +stdout is inherited so the Wasm module can write `READY\n` directly via WASI +`fd_write` (standard output fd=1). + +### 4. READY signal timing + +The READY signal to weft-appd must be sent after module initialization but before +the main event loop blocks. Two options: + +**Option A (simpler):** weft-runtime prints `READY\n` before calling the Wasm entry +point. The Wasm module is responsible for being ready to handle requests when it +starts running. + +**Option B (accurate):** The Wasm module exports a `weft_ready()` function that +weft-runtime calls explicitly before starting the main event loop. The ready signal +is sent after `weft_ready()` returns. + +Initial implementation should use Option A. Option B requires a convention on the +Wasm module's exported interface. + +### 5. Entry point call + +```rust +let linker = wasmtime_wasi::preview1::add_to_linker_sync(&engine)?; +let instance = linker.instantiate(&mut store, &module)?; +println!("READY"); +let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; +start.call(&mut store, ())?; +``` + +`_start` is the standard WASI entry point generated by Rust's `wasm32-wasi` target. + +### 6. Error handling + +- `Module::from_file` failure: exit with non-zero code (weft-appd supervisor logs it). +- `_start` trap: log the trap message, exit non-zero. +- No `_start` export: log and exit non-zero. The package checker (`weft-pack`) should + eventually validate this at install time. + +--- + +## What Is Not Designed + +- **Host function imports**: Wasm modules that import host functions beyond WASI are not + supported in the initial implementation. Custom host bindings are deferred. +- **Memory limits**: no per-session memory cap is enforced. Deferred to capability model. +- **Wasm component model**: not used. Wasm 2.0 module format only. +- **Multi-threading (WASM threads)**: not enabled. +- **Fuel metering**: not enabled. + +--- + +## Prerequisite + +Before implementing: confirm the target Wasm module compilation pipeline. The WEFT +app SDK must produce a valid `wasm32-wasi` binary that exports `_start`. Until an +example app exists that can be tested, the Wasmtime integration cannot be verified +end-to-end.