first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
# sandbox
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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<WasmSandboxHandle>`
- `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()`.