Skip to content

cataggar/wamr

Repository files navigation

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 2.1x faster in CoreMark steady-state throughput. For cold start, this repo's in-process JIT mode is instead ~4.9x faster than wasmtime's default run on small modules — see docs/bench/jit-cold-start-comparison-2026-07-10.md for the full measured comparison (AOT, JIT .fast/.full presets, and wasmtime, across both cold-start and steady-state axes). Wasmtime 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 precompiled .cwasm file (wamr run foo.cwasm), or a plain .wasm module directly when built with -Djit=true — see JIT mode below

JIT mode

By default wamr is AOT-only: it links only the runtime, not the compiler, so it stays small (~17 MB) and requires a precompiled .cwasm artifact for every module. Building with -Djit=true instead links the compiler in and adds an opt-in in-process JIT: wamr run foo.wasm compiles the module in memory and executes it in the same process, one command, no .cwasm artifact ever hits disk — wasmtime run-style ergonomics, at the cost of a bigger binary (~25 MB) and per-invocation compile latency.

AOT-only (default) JIT (-Djit=true)
Build zig build -Doptimize=ReleaseSafe zig build -Doptimize=ReleaseSafe -Djit=true
Core wasm wamrc compile foo.wasmwamr run foo.cwasm wamr run foo.wasm
Component wamrc compile-component foo.wasmwamr run foo.wasm (auto-detects the sidecar manifest) wamr run foo.wasm (no manifest needed)
One-shot testing wamrc run foo.wasm (compiles, then spawns wamr as a subprocess) wamr run foo.wasm (single process, no subprocess)
Binary size / compiler Small; no compiler linked in Larger; compiler linked in

Both flows produce identical guest-observable behavior — the JIT path reuses the exact same compiler (src/compiler) and AOT loader/runtime (src/runtime/aot) as the two-step flow, just without the disk round-trip. wamr serve (the wasi:http server) supports the same JIT fallback for components with no sidecar manifest. See issue #863 for the full plan and design rationale.

By default the JIT compiles with a fast/baseline pass preset tuned for low compile latency rather than peak steady-state throughput (set WAMR_JIT_FULL_OPT=1 to opt into the same fully-optimized pipeline wamrc compile uses, at the cost of compile latency). See docs/bench/jit-cold-start-comparison-2026-07-10.md for measured cold-start and throughput numbers across every mode (two-step AOT, wamrc run, both JIT presets, and wasmtime).

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 — 70 / 72 passing
$ zig build wasi-p3-testsuite-unfiltered # WASI Preview 3 — 41 / 41 passing
$ zig build wasi-p3-testsuite-jit-unfiltered -Djit=true # Same 41-fixture contract with no sidecars
$ 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 expectations at tests/wasi-testsuite-expectations.toml (Preview 1 / 2) and tests/wasi-p3-testsuite-expectations.toml (Preview 3, currently empty). The unfiltered P3 contract requires all 41 fixtures to execute and pass. Every entry in either expectation file 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 v46.0.1, matching the updated upstream suite) 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 currently empty 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. Wasmtime v46.0.1 passes all 41 fixtures in the updated corpus. 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). The Preview 1 gate is green (zig build wasi-testsuite70 / 72 Preview 1 fixtures; the 2 skips are a narrow environ-inheritance behavioral mismatch and a flaky upstream fixture that compares clock reads without accounting for second rollover, not crashes). The Preview 3 gate (zig build wasi-p3-testsuite-unfiltered) executes all 41 fixtures: all 41 pass through both manifest AOT and no-sidecar JIT after #881 and #905. 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 — keep-alive, chunked Transfer-Encoding, response trailers, and 431 / 413 oversize limits are all in. HTTPS termination (#609) is live:

$ wamr serve --addr <addr> --tls-cert=<cert.pem> --tls-key=<key.pem> app.wasm
$ wamr serve --addr <addr> --tls-pem=<combined.pem> app.wasm   # cert + key in one file

The cert chain (std.crypto.Certificate.Bundle) and PEM-encoded private key (PKCS#8 / RSA / EC) are parsed + validated at startup, so a missing file, malformed PEM, or key/cert mismatch surfaces before bind(2). Each accepted connection is upgraded with a TLS 1.3 server-side handshake (RSA and ECDSA server certificates) before HTTP/1.1 is served over the encrypted stream. Server-side TLS comes from the pure-Zig cataggar/tls.zig dependency — Zig 0.16's std.crypto.tls ships only a client. When --tls-cert / --tls-pem is omitted the listener serves plaintext HTTP/1.1.

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

Code of conduct

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages