What you will build
The same server source from the getting-started guide, compiled to a .wasm component and run by wasmtime. The guest owns the accept loop through wasi:sockets, so this is a real server inside the sandbox rather than a handler invoked by a host.
What you will need
- The WASI SDK (25.0 or newer), for
clangwith awasm32-wasip2target. - wasmtime 25 or newer.
- A CWIST checkout, to build the WASI archive.
Step 1: build the WASI archive
From the CWIST checkout, produce the wasip2 static library. The target is named after the artefact it builds:
make libcwist_wasip2_wasm32-wasip2.aThat leaves libcwist_wasip2_wasm32-wasip2.a in the repository root. It is an ordinary static archive; you link it the way you would link any other.
Step 2: compile your server against it
$WASI_SDK/bin/clang \
--target=wasm32-wasip2 -std=c17 -O2 \
-D_WASI_EMULATED_GETPID \
-I$CWIST/include -I$CWIST/lib/cjson \
-o server.wasm main.c \
$CWIST/libcwist_wasip2_wasm32-wasip2.a \
-lwasi-emulated-pthread -lwasi-emulated-getpid \
-Wl,--gc-sections -Wl,-z,stack-size=1048576Two flags are load-bearing. The emulated pthread and getpid libraries stand in for POSIX facilities the sandbox does not provide. The stack size matters because the default wasm stack is small and a template or TLS path will overflow it; a megabyte is a safe starting point.
Step 3: run it
wasmtime run -S preview2=y -S tcp=y -S inherit-network=y server.wasmEach flag grants one capability: preview2 selects the component model ABI, tcp allows socket use, and inherit-network lets the guest bind on the host's network. Without them the process starts and then fails to listen, which is the sandbox behaving correctly.
curl -i http://localhost:8080/Step 4: what changes inside the sandbox
- No filesystem unless you grant one. Pass
--dir .if you need one; otherwise embed what you need at build time. This site embeds its templates into the binary for exactly that reason. - Threads are limited. Prefer the reactor path; thread-per-connection has nowhere useful to go here.
- Environment variables are not inherited unless you pass
--env. - Time and randomness are host-provided capabilities and can be withheld.
Step 5: serving assets without a filesystem
CWIST's asset registry keeps content in memory and serves it at a content-hashed URL, which is what makes the no-filesystem case practical:
cwist_app_asset_add(app, "css/app.css", css, strlen(css),
"text/css; charset=utf-8");
const char *url = cwist_app_asset_url(app, "css/app.css");
/* -> /assets/css/app.<hash>.css, immutable for a year */Changing the bytes changes the URL, so a cache never serves a stale copy and an unchanged file keeps its URL across restarts. Register assets before cwist_app_listen().
Summary
A CWIST server compiles to a WASI 0.2 component with no source changes, keeps its own accept loop, and runs under a capability list you control. Anything it needs from the outside world (sockets, files, environment, clock) is something you granted on the command line.