Fundamentals 10 min read

How to Build and Run a Simple WASI‑Enabled WebAssembly “cat” Program

This article explains why WebAssembly needs a standardized system interface, introduces the WebAssembly System Interface (WASI) for portable and secure execution, and provides a step‑by‑step tutorial for compiling a C "cat" program with wasi‑sdk and running it via Node.js.

Node Underground
Node Underground
Node Underground
How to Build and Run a Simple WASI‑Enabled WebAssembly “cat” Program

Since WebAssembly was first released in 2017, it has gradually been adopted beyond browsers, allowing C and C++ code to be reused across environments with near‑native performance while remaining portable and secure. However, WebAssembly alone is just a binary instruction set and requires a virtual operating system interface to access system resources.

Projects like Emscripten expose a simulated POSIX layer to WebAssembly modules via JavaScript glue code, enabling the use of standard C libraries. When running WebAssembly outside the browser, these glue layers must be re‑implemented, leading to fragmented and non‑standard interfaces.

To provide a stable, portable, and secure foundation, the WebAssembly System Interface (WASI) standardizes system calls, emphasizing portability and capability‑based security, allowing hosts to expose only the resources a program should access.

Getting Started with WASI

We begin with a simple C program that mimics the cat command:

#include <stdio.h>

int main(int argc, char **argv) {
  FILE* file = fopen(argv[0], "r");
  char c = fgetc(file);
  while (c != EOF) {
    int wrote = fputc(c, stdout);
    c = fgetc(file);
  }
  return 0;
}

Compile the source to a WebAssembly binary using the wasi-sdk toolchain:

$ /path/to/wasi-sdk/bin/clang -target wasm32-wasi --sysroot /path/to/wasi-sdk/share/wasi-sysroot main.c -o main.wasm
We use wasi‑sdk v8.0; the SDK and Node.js support are still evolving.

The -target wasm32-wasi flag selects the WebAssembly target, --sysroot points to the SDK’s system libraries, and -o specifies the output file.

Running

Execute the compiled module with Node.js’s WASI implementation, configuring a sandboxed directory:

'use strict';
const { readFile } = require('fs').promises;
const { resolve } = require('path');
const { WASI } = require('wasi');

(async () => {
  const wasi = new WASI({
    args: [resolve('/sandbox', process.argv[2])],
    preopens: { '/sandbox': __dirname }
  });
  const importObject = { wasi_unstable: wasi.wasiImport };
  const wasm = await WebAssembly.compile(await readFile('./main.wasm'));
  const instance = await WebAssembly.instantiate(wasm, importObject);
  wasi.start(instance);
})();

The program is given access only to the virtual /sandbox directory, which maps to the current folder. The WASI runtime calls the module’s exported _start function to begin execution.

Because WASI is still unstable, the Node.js flags --experimental-wasi-unstable-preview0 and --experimental-wasm-bigint are required for full functionality, especially when dealing with 64‑bit integers.

Running the example produces the expected output:

$ echo 'hello world!' > ./foo.txt
$ node --experimental-wasi-unstable-preview0 --experimental-wasm-bigint wasm.js ./foo.txt
hello world!

This demonstrates a working WebAssembly “cat” program using WASI.

References

CraneStation – wasi‑sdk: https://github.com/CraneStation/wasi-sdk

Lin Clark – Standardizing WASI: https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/

Node.js Foundation – WASI API: https://nodejs.org/api/wasi.html

WebAssemblyNode.jsC++sandboxWASI
Node Underground
Written by

Node Underground

No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.