# sandbox _(no submodules)_ ## Purpose Top-level entry point for the WASM sandbox host. `createWasmSandbox` compiles (or accepts a pre-compiled) WASM module, validates its WASI imports against the supported-version allowlist, builds the preopen table and outbound allowlist from the `PermissionPolicy`, wraps every capability import in the permission checker, and instantiates the guest under those constraints. The returned `WasmSandboxHandle` exposes the WebAssembly instance plus the policy bindings (`preopens`, `outbound`) for diagnostic consumers, and a `dispose()` method that is idempotent — calling it twice is a no-op. Memory is bound automatically when the guest exports a `memory` of type `WebAssembly.Memory`. ## Features - Accepts a pre-compiled `WebAssembly.Module` or raw bytes. - WASI version pre-check via `detectWasiVersion`. - Wraps `WebAssembly.compile` / `WebAssembly.instantiate` failures in `SAND_WASM_INSTANTIATION_FAILED`, preserving the underlying `cause` for operator triage. - Idempotent `dispose()` — the WebAssembly instance becomes eligible for GC once the handle is released. ## Usage: Basic ```ts const handle = await createWasmSandbox({ pluginName: "stripe-checkout", policy, capabilities: [], checker, wasmBytes: await readFile("./plugin.wasm"), }); try { (handle.instance.exports._start as () => void)(); } finally { handle.dispose(); } ``` ## Usage: Advanced ```ts // Hot-reload path: re-use the compiled module across reloads. const module = await WebAssembly.compile(bytes); let handle = await createWasmSandbox({ ..., wasmModule: module }); hotReloadBus.on("reload", async () => { handle.dispose(); handle = await createWasmSandbox({ ..., wasmModule: module }); }); ``` ## API Reference - `createWasmSandbox(options): Promise` - `CreateWasmSandboxOptions.{pluginName, policy, capabilities, checker, wasmModule | wasmBytes, wasiDeps?, instruments?}` - `WasmSandboxHandle.{instance, preopens, outbound, wasi, instruments, dispose(), disposed}` ## Implementation Notes The pre-compiled-vs-bytes split exists because hot-reload paths cache the `WebAssembly.Module` across reloads to avoid re-paying the validator cost on every cycle. Compilation is significantly more expensive than instantiation, and operators have explicitly asked for the option. `dispose()` does NOT call any `fd_close` on the preopens because the preopen layer in this minimal snapshot does not hold open file descriptors — each `path_open` is a fresh `stat` against the host filesystem. A richer WASI implementation that holds open fds would need to track them in the handle and close them in `dispose()`.