5 exercises — Practice WebAssembly vocabulary in English: module, linear memory, WASI, WASM component model, wasm-pack, WAT format, instantiation, sandbox model, and near-native performance.
Core WebAssembly vocabulary clusters
Core concepts: module (.wasm binary), WAT (text format), linear memory, stack machine, import/export, instantiation
Security: sandbox, capability-based security, no syscalls by default, explicit imports only
WASI: WebAssembly System Interface — standard API for filesystem, sockets, clocks; WASI Preview 1 vs Preview 2
A frontend engineer explains WebAssembly to a colleague who asked why the image-editing tool loads so fast: "We ported the image processing library from C++ to WebAssembly. Instead of running JavaScript, which is interpreted or JIT-compiled at runtime, the browser executes WASM — a compact binary format that's compiled ahead of time. The browser's WASM engine compiles it to native machine code. Performance is near-native — within 10-20% of native in most cases. The browser still sandboxes it: WASM runs in the same JavaScript engine, can only access the memory it's given, and can't make OS calls. You compile C++ with Emscripten, or Rust with wasm-pack, and get a .wasm file." What is a WebAssembly module and what is the linear memory?
WASM module: the unit of deployment. Contains: type section (function signatures), import section (what the module needs from the host), function section (code), export section (what the module provides to the host), memory section (linear memory declaration), data section (initial memory data), table section (function pointers for indirect calls). Linear memory: a flat array of bytes that is the module's only memory space. No pointers to arbitrary host memory — the WASM code can only read/write within this buffer. Grows in 64KB pages. Host JavaScript can also read/write this memory via ArrayBuffer. This is the mechanism for passing data between JS and WASM: write bytes to the shared ArrayBuffer, call the WASM function, read result bytes. WAT (WebAssembly Text Format): the human-readable form of WASM binary. S-expression syntax. Useful for debugging. Example: (module (func $add (param i32 i32) (result i32) local.get 0 local.get 1 i32.add)). WASM types: i32, i64, f32, f64 (integers and floats, 32 and 64 bit). WASM is a stack machine: instructions push/pop values from a virtual stack. Instantiation vocabulary: Instantiation: creating a running instance of a module — allocating memory, linking imports, running the start function. Import object: JavaScript object providing the host functions and memory the module imports. In conversation: 'The key insight of linear memory: WASM has its own sandbox of bytes. You cannot accidentally access browser memory outside that buffer. It's memory-safe by construction.'
2 / 5
A backend engineer explains why they're running WASM on the server: "We use Wasmtime — a standalone WASM runtime — to run plugins submitted by our customers. We can't let customers run arbitrary native code; it would compromise our servers. But WASM's sandbox is perfect: each plugin runs in its own isolated memory space, can't access the filesystem unless we explicitly grant it, can't make network calls we haven't permitted. We use WASI — the WebAssembly System Interface — to give plugins controlled access to specific directories and capabilities. The 'nano-process' model: WASM gives you isolation as strong as a container but with microsecond startup time." What is WASI and what problem does it solve for server-side WASM?
WASI: without WASI, a WASM module outside the browser has no way to access the filesystem, network, or clocks — it's completely isolated. WASI defines a standard set of host functions the module can import, covering: fd_read/fd_write: file descriptor I/O. path_open: open files. poll_oneoff: async I/O. sock_accept: networking (WASI Preview 2). random_get: random bytes. clock_time_get: time. Capability-based security: WASI does not give filesystem access by default. The host runtime (Wasmtime, WasmEdge) preopens specific directories and grants them to the module. Module gets a capability token for that directory only — it can read/write that directory but cannot traverse up to the root. WASI versions: Preview 1 (wasi_snapshot_preview1): stable, widely supported. POSIX-inspired. Synchronous. Preview 2 (wasi 0.2): based on component model and WIT. Async, sockets (wasi:sockets), HTTP (wasi:http). Server-side WASM runtimes: Wasmtime: Bytecode Alliance, Cranelift JIT. Wasmer: multiple backends (LLVM, Cranelift, SinglePass). WasmEdge: CNCF project, optimized for edge/serverless. WAMR (WebAssembly Micro Runtime): IoT/embedded. Solomon Hykes (Docker founder) quote: "If WASM+WASI existed in 2008, Docker wouldn't exist." — highlighting WASM's potential for portable, secure compute. In conversation: 'WASI is to WASM what POSIX is to Unix programs — a portable system interface so the same binary runs everywhere.'
3 / 5
A Rust developer explains wasm-pack to a team member starting with WASM: "wasm-pack is the toolchain for compiling Rust to WebAssembly for the browser. It handles the whole pipeline: compile Rust to wasm32-unknown-unknown target, run wasm-bindgen to generate JavaScript glue code, optimize with wasm-opt, and package it as an npm module. wasm-bindgen is the magic — it generates TypeScript type definitions and JS wrappers that let you pass complex types (strings, structs, arrays) between JS and Rust. Without it, you can only pass integers across the WASM boundary. With wasm-bindgen, calling a Rust function from JS looks like a normal JS function call." What does wasm-bindgen do and why is it necessary?
WASM ABI boundary: WASM functions can only natively pass/return: i32, i64, f32, f64, v128 (SIMD). To pass a string, you must: (1) allocate memory in WASM linear memory, (2) write the string bytes there, (3) pass the pointer (i32) and length (i32) to the WASM function. The function reads it from linear memory. wasm-bindgen automates all of this: Rust side: #[wasm_bindgen] pub fn process_text(input: &str) -> String — annotate with macro. Generated JS side: handles writing the string to WASM memory, calling the function, reading the result string from memory, calling the deallocator. Generated TypeScript types: export function process_text(input: string): string — fully typed. wasm-pack workflow: wasm-pack build --target web produces: .wasm file (the compiled Rust), a .js glue file (imports/exports), .d.ts TypeScript declarations, package.json. C/C++ equivalent: Emscripten — much older, more mature for C/C++ codebases (SQLite, ffmpeg, game engines). WASM performance vocabulary: Near-native performance: WASM executes at 60-90% of equivalent native code speed in most benchmarks. The gap: WASM's stack machine model vs native register-based ISA, some overhead in WASM→native translation. SIMD: WASM fixed-width SIMD (128-bit) for vectorized compute. Threads: SharedArrayBuffer + Atomics enable multi-threaded WASM (requires correct HTTP headers). In conversation: 'wasm-bindgen is why Rust + WASM is so ergonomic. Without it, you'd be manually shuffling bytes across the memory boundary for every function call.'
4 / 5
A platform architect presents the WASM component model at a technical conference: "The component model is the next evolution of WebAssembly. Today, if you compile a Rust WASM module and a Go WASM module, they can't easily talk to each other — they have different memory models and ABIs. The component model defines a standard interface type system — WIT (WebAssembly Interface Types). You define interfaces in WIT: 'function greet(name: string) -> string'. Both the Rust component and the Go component speak this interface. You can compose them together — link a Rust image processor with a Go HTTP server with a Python ML model — all as WASM components. No shared memory, no unsafe FFI." What problem does the WASM component model solve?
WASM component model: defines a higher-level module system above core WebAssembly. Core problem: core WASM modules can only share linear memory and function pointers — no safe way for two modules (especially from different languages) to exchange complex types. Component model solution: WIT (WebAssembly Interface Types / WIT IDL): a language for defining typed interfaces. Types: string, list, record, variant, tuple, option, result, resource. Component: a WASM module wrapped with WIT-defined imports and exports. Composition: link component A's export to component B's import — the runtime handles the type translation. Canonical ABI: the standardized binary encoding for WIT types across component boundaries. WASM 2024 ecosystem: WASI 0.2: built on the component model. Interfaces: wasi:filesystem, wasi:sockets, wasi:http. wasi:http/proxy world: a WASM component that handles HTTP requests — deployable to Fastly, Cloudflare Workers, and any WASI 0.2-compatible runtime. cargo component: Rust toolchain for building WASM components. WASM use cases beyond browsers: Edge compute: Cloudflare Workers (V8 + WASM), Fastly Compute@Edge (Wasmtime). Plugin systems: Extism, wasm-plugin. Serverless: WasmEdge in APISIX, KubeWasm. Embedded/IoT: WAMR on microcontrollers. In conversation: 'The component model is what makes WASM a universal binary format rather than just a browser performance trick. Write in any language, compose safely with any other language.'
5 / 5
A security engineer explains the WASM sandbox model to a team evaluating WASM for running untrusted user code: "WASM's security model is capability-based. A module has no ambient authority — it can't access the filesystem, make network calls, read environment variables, or call any OS function unless the host explicitly grants it. When you instantiate a WASM module, you pass an import object: the exact set of functions the module is allowed to call. The module declares what it wants to import; the host decides whether to supply it. If you don't supply a 'read_file' import, the module simply can't read files, even if it tries. This is fundamentally different from running a native binary, which inherits all the process's OS permissions." What makes the WebAssembly sandbox a strong security boundary compared to native code execution?
WASM capability-based security: the Principle of Least Authority (POLA) built into the runtime. No syscalls: WASM has no syscall instruction. All OS access goes through host-provided imports. Verifier: before executing, the WASM runtime validates the module (type safety, control flow integrity, no out-of-bounds memory access in the instruction encoding). This is different from JIT safety — the module is structurally verified to be safe before a single instruction runs. Memory isolation: each WASM instance has its own linear memory. Instance A cannot read Instance B's memory. The host controls what memory each instance sees. Defense in depth vs containers: WASM is not a replacement for containers — containers isolate at the process level; WASM isolates at the instruction/memory level. Combining both (WasmEdge in Docker or Kubernetes) gives defense in depth. Escape vulnerabilities: despite the strong model, WASM sandbox escapes have been found in browser JIT engines. Spectre-style side channels can leak information across sandbox boundaries. The sandbox is strong but not perfect. WASM for plugin systems: Extism: universal plugin system using WASM. Any language → WASM component → plugin. OPA (Open Policy Agent) Wasm: compile Rego policies to WASM for embedding in any application. In conversation: 'Running untrusted user code natively requires containers, seccomp, AppArmor, and still carries risk. WASM gives you a strong sandboxed execution model in a single function call, with microsecond startup.'