Skip to content

cataggar/wamr

WAMR: WebAssembly Micro Runtime

A fork of bytecodealliance/wasm-micro-runtime ported from C to Zig and maintained with AI assistance. It passes the WebAssembly/spec test suite of 20k+ tests. It supports the Component Model. It has a very fast cold start, small engine binary size with no dependencies, and is easy to build & fork.

Wasmtime is currently about 3x faster in CoreMark benchmarks. It has years of production usage and use with a proven track record and security audits.

Install

Install pre-built binaries from GitHub Releases with ghr:

$ ghr install cataggar/wamr

See INSTALL.md for alternative installation methods (winget, uv, pip) and detailed instructions.

Tools

  • wamrc: AOT compiler — compile a .wasm module to a native .cwasm binary (wamrc compile foo.wasm)
  • wamr: run a WebAssembly module — either a .wasm file via the stack-based interpreter, or a precompiled .cwasm file produced by wamrc (wamr run foo.wasm)

Building

Requires Zig 0.16. No other dependencies.

$ git clone https://github.com/cataggar/wamr
$ cd wamr
$ zig build

For release builds:

$ zig build -Doptimize=ReleaseSafe

ReleaseSafe is the primary source-build recommendation. The 2026-05-29 optimize-mode comparison shows cold-start noop.cwasm at ×0.54 Safe/Fast and SIMD interpreter rows at ×0.99 median on the project VM, while also showing ReleaseSafe catching a CoreMark AOT compiler invariant before timing; see docs/bench/optimize-mode-comparison-2026-05-29.md.

Cross-compilation works out of the box:

$ zig build -Dtarget=aarch64-linux -Doptimize=ReleaseSafe
$ zig build -Dtarget=aarch64-macos -Doptimize=ReleaseSafe
$ zig build -Dtarget=x86_64-windows -Doptimize=ReleaseSafe

Running tests

Unit tests:

$ zig build test

Spec tests:

$ zig build
$ ./zig-out/bin/spec-test-runner tests/spec-json

WASI conformance (WebAssembly/wasi-testsuite):

$ git submodule update --init tests/wasi-testsuite
$ pip install -r tests/wasi-testsuite/test-runner/requirements.txt
$ zig build wasi-testsuite      # WASI Preview 1 + Preview 2 — passing
$ zig build wasi-p3-testsuite   # WASI Preview 3 (wasm32-wasip3) — 40 / 40 passing
$ zig build wasi-p3-parity      # Same fixtures via wamr + wasmtime, diff the reports

The upstream do_wait timeout is 5s; that matches GitHub Actions runner timings but is tight on slow developer VMs (e.g. http-fields takes ~11s on the project Azure dev VM). Set WAMR_TESTSUITE_TIMEOUT=<seconds> to override it — see tests/wasi-testsuite-runner-patch/ (#583 A7).

The suite drives the freshly-built wamr CLI through the in-tree adapter at tests/wasi-testsuite-adapter/wamr-zig.py and applies the curated skiplists at tests/wasi-testsuite-skip.json (Preview 1 / 2) and tests/wasi-p3-testsuite-skip.json (Preview 3 — currently empty: every wasm32-wasip3 fixture passes). Every entry in either skiplist must carry a one-line rationale and a follow-up issue number. When a previously-skipped test starts passing, delete the entry — the suite is the gate against regressions in already-shipped WASI host functions.

zig build wasi-p3-parity is the cross-runtime gate (#583 C1): it runs the same wasm32-wasip3 corpus through wamr and upstream Wasmtime (CI pin v44.0.1, the first stable release with -Sp3 support) and diffs the JSON reports via scripts/diff-testsuite-reports.py. The classifier exits non-zero on:

  • Regressions — wamr fails a fixture that Wasmtime still passes (a true wamr regression).
  • Stale skip-list entries — a fixture listed in tests/wasi-p3-parity-skip.json is no longer in the wamr-pass / Wasmtime-fail shape (e.g. the upstream Wasmtime / wasi-testsuite fix has landed and the entry must be retired).
  • Undocumented Wasmtime-side bugs under --strict — a fixture wamr passes but Wasmtime fails for which the skip-list has no tracking entry.

The skip-list at tests/wasi-p3-parity-skip.json maps each known Wasmtime-side or fixture-side bug to an upstream tracking issue. Entries listed there are documented deltas — they are reported on stderr but never fail the parity gate, so a Wasmtime v44.0.1 fixture failure (currently http-service, sockets-tcp-connect, sockets-tcp-listen, sockets-udp-send) does not masquerade as a wamr bug. To document a new Wasmtime delta, file an upstream issue at bytecodealliance/wasmtime (or WebAssembly/wasi-testsuite for fixture bugs) and add an entry keyed by fixture name with the tracking URL as the value. The CI workflow at .github/workflows/wasi-p3-parity.yml runs the gate on push to main and nightly and is required — a new wamr regression or new undocumented Wasmtime delta blocks the merge queue.

WASI

wamr ships the WASI 0.2.x and 0.3.0 interface surface (wasi:cli, wasi:clocks, wasi:filesystem, wasi:http, wasi:io, wasi:random, wasi:sockets); both gates are green (zig build wasi-testsuite72 / 72 Preview 1 fixtures; zig build wasi-p3-testsuite40 / 40 Preview 3 fixtures). Outbound HTTP and HTTPS issue real requests via std.http.Client and Zig 0.16's std.crypto.tls.

See docs/wasi.md for the full feature matrix — interface → version → method count → fixture pass-rate → known limitations — and #583 for post-Preview-3 hardening items.

To exercise the real outbound HTTPS path in unit tests (off by default so CI stays hermetic):

$ zig build test -Dnetwork_tests=true

wasi:http

The wasi:http/handler@0.3.0 incoming-handler server (PRs #580 + #595) accepts plaintext HTTP/1.1 today — keep-alive, chunked Transfer-Encoding, response trailers, and 431 / 413 oversize limits are all in. HTTPS termination ships in two halves:

  1. CLI plumbing + cert / key loader (in this build, opt-in). wamr run --listen=<addr> --tls-cert=<cert.pem> --tls-key=<key.pem> parses + validates the cert chain (std.crypto.Certificate.Bundle) and PEM-encoded private key (PKCS#8 / RSA / EC) at startup, so a missing file or malformed PEM surfaces before bind(2). The convenience flag --tls-pem=<combined.pem> accepts a single file containing both the cert chain and the key.

  2. Server-side handshake (upstream-blocked). Zig 0.16's std.crypto.tls ships only Client.zig — there is no std.crypto.tls.Server yet. Until upstream lands the server-side API (#609 tracks the wiring), the listener logs a single startup warning on stderr and continues to serve plaintext HTTP/1.1. The CLI surface (--tls-cert / --tls-key / --tls-pem) is stable — when handshake support lands it goes live without a flag-shape change.

Configuration

wasi:config@0.2.0-rc.1 (store.get / store.get-all) is wired through a layered host adapter (#583 B6). Guest components import wasi:config/store@0.2.0-rc.1 and read string → string pairs from two sources:

  1. Environment variables prefixed with WAMR_CONFIG_. The prefix is stripped and the remainder lower-cased ASCII (WAMR_CONFIG_API_KEY=secretapi_key=secret).
  2. JSON file passed via --config-store=PATH. The file must be a flat object of string values ({"key":"value", …}); nested objects, arrays, or non-string scalars are rejected at startup.

Precedence: file overrides env. When the same lower-cased key appears in both layers, the env entry is dropped and the file value wins. Surviving env-only entries are appended after the file entries.

$ cat config.json
{ "host": "api.example.com", "timeout_ms": "5000" }

$ WAMR_CONFIG_HOST=localhost WAMR_CONFIG_DEBUG=1 \
    wamr run --config-store=config.json my-component.wasm
# Guest sees: host=api.example.com (file wins), timeout_ms=5000 (file),
#             debug=1 (env-only).

The in-memory store never surfaces the error arms (upstream / io) defined by wasi:config@0.2.0-rc.1 — every lookup returns Ok(Some) or Ok(None). Those arms are reserved for future Vault / Kubernetes ConfigMaps / etc. back-ends.

License

Apache 2.0

About

WebAssembly Micro Runtime

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages