Wasi
WebAssembly System Interface (WASI) for portable system-level access including filesystem, networking, and clocks
You are an expert in the WebAssembly System Interface (WASI) for building WebAssembly applications. ## Key Points - **Use `wasm32-wasip1` for maximum compatibility** — Preview 1 is supported by all major runtimes. Use Preview 2 only when you need its specific features (component model, typed interfaces). - **Test with multiple runtimes** — Wasmtime, Wasmer, and WasmEdge may have subtly different WASI implementations. Test on your deployment target. - **Prefer standard library I/O** — in Rust and C, standard `read`/`write`/`open` calls are automatically mapped to WASI. Avoid calling WASI functions directly unless necessary. - **Handle missing capabilities gracefully** — if a pre-opened directory is not provided, file operations will fail. Check for errors rather than assuming access. - **Forgetting `--dir` pre-opens** — WASI modules cannot access any filesystem path unless the host explicitly pre-opens it. Missing pre-opens cause `ENOTCAPABLE` or `ENOENT` errors. - **Assuming network access** — WASI Preview 1 has no networking support. TCP/UDP requires Preview 2's `wasi:sockets` or runtime-specific extensions. - **Preview 1 vs Preview 2 target mismatch** — modules compiled for `wasm32-wasip1` will not work with a Preview 2-only host configuration, and vice versa. Check your runtime's documentation. - **No threads in WASI Preview 1** — `wasm32-wasip1` does not support `std::thread`. Code that spawns threads will fail to compile or panic at runtime. - **Clock resolution differences** — `clock_time_get` resolution varies by host. Do not depend on nanosecond precision being accurate across all runtimes. - **Assuming network access in Preview 1** — WASI Preview 1 has no networking APIs; TCP/UDP requires Preview 2's `wasi:sockets` or runtime-specific extensions like WasmEdge's HTTP plugin. - **Spawning threads in Preview 1 targets** — `wasm32-wasip1` does not support `std::thread` or multi-threading; code that depends on thread spawning will fail to compile or trap at runtime. ## Quick Example ```bash rustup target add wasm32-wasip1 ``` ```bash cargo build --target wasm32-wasip1 --release # Run with Wasmtime, granting access to /data directory wasmtime run --dir /data::./local_data --env USER=alice target/wasm32-wasip1/release/app.wasm ```
skilldb get webassembly-skills/WasiFull skill: 222 linesWASI — WebAssembly
You are an expert in the WebAssembly System Interface (WASI) for building WebAssembly applications.
Overview
WASI (WebAssembly System Interface) is a standardized set of APIs that give WebAssembly modules portable access to operating system functionality such as filesystems, clocks, random number generation, and networking. WASI enables Wasm to run outside the browser as a secure, sandboxed application runtime. All capabilities are explicitly granted by the host, following a capability-based security model.
Core Concepts
WASI Versioning
- WASI Preview 1 (wasip1) — the stable, widely-supported snapshot. Provides
fd_read,fd_write,path_open,clock_time_get,random_get, and related syscall-like functions. Supported by Wasmtime, Wasmer, Node.js, and others. - WASI Preview 2 (wasip2) — the component-model-based redesign. Uses WIT (Wasm Interface Types) to define typed interfaces. Introduces
wasi:filesystem,wasi:sockets,wasi:http, andwasi:cliworlds. Under active development with growing runtime support.
Capability-Based Security
WASI does not grant blanket access to the host system. Every capability (directory access, network socket, environment variable) must be explicitly provided by the host at instantiation time. A module cannot access anything it was not given.
Key Interfaces (Preview 1)
| Module | Purpose |
|---|---|
wasi_snapshot_preview1 | Filesystem, stdio, clocks, random, args, environ |
Key Interfaces (Preview 2 / WIT)
| World | Purpose |
|---|---|
wasi:cli/run | Command-line application entry |
wasi:filesystem | File and directory operations |
wasi:sockets | TCP/UDP networking |
wasi:http | Incoming and outgoing HTTP |
wasi:random | Cryptographic random bytes |
wasi:clocks | Monotonic and wall clocks |
Implementation Patterns
Rust with WASI Preview 1
rustup target add wasm32-wasip1
// src/main.rs — standard Rust I/O works under WASI
use std::fs;
use std::io::{self, BufRead};
fn main() {
// Read environment variable
let name = std::env::var("USER").unwrap_or_else(|_| "world".into());
println!("Hello, {}!", name);
// Read a file (host must pre-open the directory)
match fs::read_to_string("/data/config.txt") {
Ok(contents) => println!("Config: {}", contents),
Err(e) => eprintln!("Cannot read config: {}", e),
}
// Read from stdin
let stdin = io::stdin();
for line in stdin.lock().lines() {
let line = line.unwrap();
if line.is_empty() { break; }
println!("Echo: {}", line);
}
}
cargo build --target wasm32-wasip1 --release
# Run with Wasmtime, granting access to /data directory
wasmtime run --dir /data::./local_data --env USER=alice target/wasm32-wasip1/release/app.wasm
C with WASI (using wasi-sdk)
// main.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("Arguments:\n");
for (int i = 0; i < argc; i++) {
printf(" [%d] %s\n", i, argv[i]);
}
const char *val = getenv("MY_VAR");
if (val) {
printf("MY_VAR = %s\n", val);
}
FILE *f = fopen("/data/input.txt", "r");
if (f) {
char buf[256];
while (fgets(buf, sizeof(buf), f)) {
printf("Line: %s", buf);
}
fclose(f);
}
return 0;
}
# Compile with wasi-sdk
/opt/wasi-sdk/bin/clang --sysroot=/opt/wasi-sdk/share/wasi-sysroot \
-o app.wasm main.c
# Run
wasmtime run --dir /data::./data --env MY_VAR=hello app.wasm -- arg1 arg2
Running WASI Modules Programmatically (Wasmtime Rust API)
use wasmtime::*;
use wasmtime_wasi::preview1::WasiP1Ctx;
use wasmtime_wasi::WasiCtxBuilder;
fn main() -> anyhow::Result<()> {
let engine = Engine::default();
let mut linker = Linker::<WasiP1Ctx>::new(&engine);
wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |t| t)?;
let wasi = WasiCtxBuilder::new()
.inherit_stdio()
.preopened_dir("./data", "/data", DirPerms::all(), FilePerms::all())?
.env("USER", "alice")
.args(&["app", "--verbose"])
.build_p1();
let mut store = Store::new(&engine, wasi);
let module = Module::from_file(&engine, "app.wasm")?;
linker.module(&mut store, "", &module)?;
let func = linker.get_default(&mut store, "")?;
func.call(&mut store, &[], &mut [])?;
Ok(())
}
WASI Preview 2 with WIT
// wit/world.wit
package my:app;
world my-command {
import wasi:filesystem/types@0.2.0;
import wasi:cli/stdin@0.2.0;
import wasi:cli/stdout@0.2.0;
export wasi:cli/run@0.2.0;
}
Node.js WASI Support
import { readFile } from "node:fs/promises";
import { WASI } from "node:wasi";
const wasi = new WASI({
version: "preview1",
args: ["app", "--verbose"],
env: { USER: "alice" },
preopens: { "/data": "./local_data" },
});
const module = await WebAssembly.compile(await readFile("app.wasm"));
const instance = await WebAssembly.instantiate(module, wasi.getImportObject());
wasi.start(instance);
Best Practices
- Grant minimal capabilities — only pre-open directories and provide environment variables that the module actually needs. WASI's security model is only effective if capabilities are scoped tightly.
- Use
wasm32-wasip1for maximum compatibility — Preview 1 is supported by all major runtimes. Use Preview 2 only when you need its specific features (component model, typed interfaces). - Test with multiple runtimes — Wasmtime, Wasmer, and WasmEdge may have subtly different WASI implementations. Test on your deployment target.
- Prefer standard library I/O — in Rust and C, standard
read/write/opencalls are automatically mapped to WASI. Avoid calling WASI functions directly unless necessary. - Handle missing capabilities gracefully — if a pre-opened directory is not provided, file operations will fail. Check for errors rather than assuming access.
Common Pitfalls
- Forgetting
--dirpre-opens — WASI modules cannot access any filesystem path unless the host explicitly pre-opens it. Missing pre-opens causeENOTCAPABLEorENOENTerrors. - Path mapping confusion — the
--dir host_path::guest_pathsyntax maps a host directory to a guest-visible path. Getting these reversed causes "file not found" errors even when the file exists on the host. - Assuming network access — WASI Preview 1 has no networking support. TCP/UDP requires Preview 2's
wasi:socketsor runtime-specific extensions. - Preview 1 vs Preview 2 target mismatch — modules compiled for
wasm32-wasip1will not work with a Preview 2-only host configuration, and vice versa. Check your runtime's documentation. - No threads in WASI Preview 1 —
wasm32-wasip1does not supportstd::thread. Code that spawns threads will fail to compile or panic at runtime. - Clock resolution differences —
clock_time_getresolution varies by host. Do not depend on nanosecond precision being accurate across all runtimes.
Core Philosophy
WASI's capability-based security model is its defining feature and its greatest strength. Unlike traditional operating systems where processes have ambient authority to access any file or network resource, WASI modules have no capabilities by default. Every directory, environment variable, and network socket must be explicitly granted by the host. This is not a limitation to work around — it is a security architecture to embrace. Design modules that request minimal capabilities and handle missing ones gracefully.
WASI makes WebAssembly a universal binary format for server-side code. A module compiled to wasm32-wasip1 runs on Wasmtime, Wasmer, WasmEdge, and Node.js without recompilation. This portability is valuable for plugin systems, serverless functions, and edge computing, where the execution environment varies. Write standard library I/O (not runtime-specific extensions) to maximize the number of hosts that can run your module.
Preview 1 is stable and widely supported. Preview 2 is the future, introducing the Component Model and typed WIT interfaces, but runtime support is still maturing. For production workloads today, target wasm32-wasip1. For forward-looking designs, experiment with Preview 2 and WIT to define typed interfaces that will compose cleanly across language boundaries.
Anti-Patterns
-
Forgetting
--dirpre-opens — WASI modules cannot access any filesystem path unless the host explicitly pre-opens it; running without pre-opens causes crypticENOTCAPABLEorENOENTerrors on file operations. -
Assuming network access in Preview 1 — WASI Preview 1 has no networking APIs; TCP/UDP requires Preview 2's
wasi:socketsor runtime-specific extensions like WasmEdge's HTTP plugin. -
Granting more capabilities than needed — pre-opening the root directory or passing the entire environment defeats WASI's security model; grant only the specific directories and variables the module requires.
-
Using WASI-specific APIs when standard library I/O works — calling
wasi_snapshot_preview1functions directly whenstd::fs::read_to_stringorprintfachieve the same result reduces portability; prefer standard library I/O. -
Spawning threads in Preview 1 targets —
wasm32-wasip1does not supportstd::threador multi-threading; code that depends on thread spawning will fail to compile or trap at runtime.
Install this skill directly: skilldb add webassembly-skills
Related Skills
Assemblyscript
Writing WebAssembly modules using AssemblyScript, a TypeScript-like language that compiles to Wasm
Js Interop
JavaScript and WebAssembly interop patterns including memory sharing, type marshaling, and binding generation
Performance
Optimizing WebAssembly performance including binary size, execution speed, memory usage, and profiling techniques
Rust WASM
Compiling Rust to WebAssembly using wasm-pack, wasm-bindgen, and the Rust Wasm ecosystem
WASM Basics
WebAssembly fundamentals including module structure, types, memory model, and binary/text formats
WASM in Browser
Using WebAssembly in the browser for canvas rendering, audio processing, Web Workers, and DOM integration