Open-source project
wasmi-labs/wasmi avatar
wasmi-labs/wasmi

Wasmi: a WebAssembly interpreter that treats determinism and fuel as features

WebAssembly (Wasm) Interpreter - An efficient, lightweight, and deterministic WebAssembly interpreter for embedded systems and plug-in runtimes.

2,313 stars370 forksRustApache-2.0

At a glance

What is it?
A Rust interpreter for running untrusted WebAssembly on embedded targets and in plugin systems. The interesting parts are the proposal support table, two security audits, and a 2.0 rewrite that changed the fuel accounting.
Who is it for?
Wasmi is the interpreter to look at when you are running third-party WebAssembly somewhere unusual: a microcontroller, a plugin host, a chain node, a terminal multiplexer. It is written in Rust, works without the standard library, aims at full spec testsuite compliance, and has been audited twice by outside firms rather than once.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An interpreter aimed at places a JIT cannot go

Wasmi is a WebAssembly interpreter written in Rust, and its stated focus is constrained and embedded systems. That single sentence explains most of its design decisions, including the ones that would otherwise look like limitations.

An interpreter rather than a JIT compiles Wasm to an internal representation and walks it. That means no executable memory at runtime, no dependence on host instruction set support, and a much smaller and more predictable attack surface, at the cost of throughput. On a microcontroller or inside a plugin boundary that trade is usually correct.

The feature list in the README makes the priorities explicit. Execution is described as simple, correct and deterministic. It works in no_std embedded environments. It resists compiler and JIT bombs during translation. It loosely mirrors the Wasmtime API to act as a drop-in replacement. It claims 100 percent WebAssembly spec testsuite compliance. Fuel metering is built in. And it supports the official Wasm C-API.

Two of those deserve expansion. Determinism means the same module produces the same result on every host, which is a requirement for consensus systems and a nice property everywhere else, since it removes floating point and address layout as sources of surprise.

Resistance to compiler and JIT bombs refers to a specific attack. A translation step that runs before execution can be made arbitrarily expensive by a small crafted module, so a host that compiles untrusted Wasm can be denied service before a single instruction is interpreted. A runtime that compiles untrusted code needs a bound on translation effort as well as on execution.

The Wasmtime API mirror is a strategic choice. Developers frequently prototype on Wasmtime and move to a smaller runtime for production, and a similar API surface makes that move a dependency change rather than a rewrite.

What is supported, and the four features that are not

The README publishes a full table of WebAssembly proposals with the version each one landed in, which is more informative than most project pages give you.

Supported, with versions: mutable globals, saturating float to int conversions, sign extension and multi-value, all at 0.14.0 or later. Bulk memory and reference types at 0.24.0. Tail calls at 0.28.0. Extended const at 0.29.0. Multi-memory at 0.37.0. Custom page sizes and memory64 at 0.41.0. Wide arithmetic at 0.42.0. SIMD at 0.43.0, and relaxed SIMD at 0.44.0.

The four that are not supported are all still tracking issues in the repository: function references, garbage collection, threads and exception handling. This is the single most important thing to check before committing to this runtime. If your toolchain emits a module using any of those proposals, it will fail to load, and no amount of configuration will help.

There is a coherent reason for the grouping. Function references and garbage collection belong to the same proposal family, and threads need shared memory and an execution model that an interpreter does not naturally provide. Exception handling is the one that surprises people, since it is now shipped in several engines, but the table is honest about where the project stands.

The two embedding paths are listed in a smaller table. WASI support for wasip1 is provided through the wasmi_wasi crate, and the official Wasm C-API is provided through the wasmi_c_api_impl crate. Having both means a Rust host and a C host can both embed the same engine, and a module built for WASI can run without the host reimplementing the standard library surface.

Two audits, and who paid for them

The README claims Wasmi is suitable for safety critical use cases and has been audited twice, then gives a table naming the auditors, the contractors and the report files. That level of attribution is rare and worth reading carefully, because the contractor column tells you something the auditor column does not.

Versions 0.36.0 through 0.38.0 were audited by Runtime Verification Inc., contracted by the Stellar Development Foundation, with the report published as a PDF in the repository. Version 0.31.0 was audited by SRLabs, contracted by Parity Technologies, also with a PDF in the repository.

Two different auditors on two different version ranges is better than one audit of one version, and having the reports committed rather than linked to a marketing page means they will still be readable if the project changes hands.

The practical caveat is the version range. Both audits cover pre-1.0 releases, and the current version is 2.0.0. An audit of 0.36 to 0.38 tells you the team responds to findings and that the code was reviewed by people who find real bugs for a living. It does not tell you that today's interpreter core has been audited, and the 2.0 release replaced that core wholesale. If you need an audit for the version you are shipping, ask rather than assume continuity.

Fuel metering that survives a version upgrade

Built-in fuel metering is listed as a distinct feature, and the 2.0 beta notes explain what changed and why it matters more than it first appears.

The old fuel metering was tied to Wasmi's internal bytecode. That is a problem: internal bytecode is an implementation detail that changes between releases, so the fuel cost of the same module could shift under you when you upgraded the runtime. A chain node or a paid API using fuel as a metering unit would see its economics change on a dependency bump.

The new stable fuel metering is tied to the input WebAssembly instead. The release notes are straightforward that it is less precise, and equally straightforward about why that is the right trade: it stays relatively stable across versions, and it is the same technique Wasmtime uses.

The 2.0.0-beta.10 release also added a way to customize dynamic fuel metering and a batch of execution optimisations with a clear theme. Improving branch prediction, reusing the stack pointer across Wasm calls, adding an instance pointer to the function entity, and adding specialised call_indirect variants for table 0 all reduce work in the hot path. The specialised variants are a good example of the kind of micro-optimisation that only matters in an interpreter, where every instruction pays interpretation overhead.

Fuel is the mechanism that makes untrusted execution safe to bound. Wasm itself has no instruction limit, so a host that runs third-party modules needs either fuel, a wall clock timeout, or both. Having it in the engine, stable across versions, is the reason to prefer Wasmi over a hand-rolled loop with a counter.

Version 2.0 replaced the interpreter core

Wasmi 2.0.0 was published on 2026-09-01, and the release notes are candid that the headline is a rewrite. Wasmi has an entirely new internal IR and executor. The benchmark claim is a geometric mean of roughly 2.2 times faster than version 1.0.

The architectural detail that explains the gain is that IR operators now store their results in accumulator registers, using the same interpreter architecture that other fast interpreters use. Combined with the host call and indirect call improvements from the beta series, that is a coherent redesign rather than a collection of patches.

The release notes also handle the awkward part well. Instead of attempting to list every change across eleven beta releases, the entry points at the beta entries for the complete record and links a migration guide for anyone coming from 1.x, describing the changes that require action from users. That is the right structure for a release with this much churn, and it is worth noting that the maintainer chose to acknowledge the size of the beta series rather than presenting a tidy summary.

There is also a companion article published alongside the release, titled as engineering of the fastest WebAssembly interpreters, which suggests the rewrite was accompanied by published reasoning about the design.

The workspace layout says more than the README

The manifest is a Cargo workspace with eleven members, and the split explains the architecture. Alongside the main crates/wasmi package there are separate crates for the interpreter core, the IR, collections, WASI, the C-API implementation, the C-API artifact and macros, a CLI, a WAST test harness, a fuzzing crate, and the top-level fuzz target.

Splitting the IR into its own crate is the sign of a real architectural boundary. The IR can be reasoned about, tested and versioned independently of the executor, which is what makes the 2.0 rewrite tractable rather than a single sprawling change.

The dependency versions pin down the surrounding ecosystem. The wasm-tools family is used throughout: wat, wast, wasmparser, wasm-smith and wasmprinter, all at the 0.228 generation. wasm-smith is a WebAssembly fuzzer that generates random modules, which tells you the project treats fuzzing as part of correctness rather than an afterthought. For WASI, the workspace pulls in wasi-common at 36.0.0 and wiggle from the Wasmtime project, meaning the WASI implementation reuses the same interface generation machinery as the JIT runtime rather than hand-rolling it.

The workspace package metadata is where the license story gets interesting. The manifest declares license as MIT/Apache-2.0, and the tree contains both LICENSE-MIT and LICENSE-APACHE files. GitHub's metadata records only Apache-2.0 for the repository. The manifest is the accurate description, and this is the standard Rust dual-license arrangement where you may use either license. Note the rust-version field: 1.86, with edition 2024. That is a recent floor, and it will exclude older toolchains even though the crate itself supports no_std.

The tree also has a fuzz directory, a docs directory holding the usage guide, the development guide and the migration guide, a resources directory containing the audit PDFs and the logo, plus SECURITY.md, CODE_OF_CONDUCT.md and CONTRIBUTING.md. A security policy and a fuzzing harness in the same repository is a reasonable signal about how the maintainer treats the threat model.

Adoption, and choosing between the interpreters

The README maintains a list of projects using Wasmi, with an invitation to be added by email. The list is a useful cross-section of the interpreter's actual niche: Soroban, the Stellar smart contract platform; Typst, the typesetting system; Zellij, the terminal workspace; Josh, a shell built on Rust; Ripple; Firefly Zero, a game for the Tandy Pocket Computer; icu4x, the Unicode component library; orbitinghail; smoldot, a Polkadot client; Munal OS; Ayaka; and Project Oak, Google's confidential computing work.

That list explains the design better than any feature list can. Typst and Zellij want a small, portable, dependable interpreter for user-supplied extensions. Soroban and Project Oak want determinism and auditability on machines they do not control. Firefly Zero wants something small enough for a 1980s handheld. Nobody on that list is trying to serve the highest possible requests per second, which is exactly the position Wasmi occupies.

The alternatives come to mind quickly. Wasmtime and Wasmer are JITs that win on throughput and lose on footprint and attack surface. Wasm3 is a C interpreter with a different performance profile. WAMR is an embedded runtime with a strong industrial footprint. The choice comes down to your constraint: if you have memory and CPU to spare and want speed, use a JIT. If you are on a microcontroller, inside a plugin boundary, or somewhere you need identical behaviour on every host, Wasmi is the more honest fit.

The project has 2,313 stars and 370 forks, with 38 open issues. The last push was 2026-09-21. The fork count relative to stars is high, which is what you would expect from a library that gets vendored into other projects, including some that will never be reflected in the README's list. Thirty-eight open issues on a project of this scope is a manageable number, and the presence of tracking issues for the four unsupported proposals suggests they are used deliberately rather than left to rot.

Editorial conclusion

Wasmi is the interpreter to look at when you are running third-party WebAssembly somewhere unusual: a microcontroller, a plugin host, a chain node, a terminal multiplexer. It is written in Rust, works without the standard library, aims at full spec testsuite compliance, and has been audited twice by outside firms rather than once. The 2.0 release replaced the interpreter core entirely and, as a side effect, made fuel metering stable across versions by tying it to the input WebAssembly instead of internal bytecode, which is the kind of change that removes a real source of production bugs and also breaks anyone who depended on the old numbers. The proposal table is the honest part of the README: function references, garbage collection, threads and exception handling are all still tracking issues, so a module compiled with those features will not load. Read that table before you choose it. If your target is a normal server with room to spare, Wasmtime is the more capable engine, and Wasmi's own README says as much by mirroring the Wasmtime API to make migration possible.

Frequently asked questions

What is Wasmi?

Wasmi is a WebAssembly interpreter written in Rust, focused on constrained and embedded systems. It runs untrusted Wasm modules without compiling them to native code, works in no_std environments, mirrors the Wasmtime API, claims full WebAssembly spec testsuite compliance, and includes fuel metering for bounding execution.

Why use an interpreter instead of a JIT like Wasmtime?

An interpreter needs no executable memory at runtime and does not depend on host instruction set support, which makes it smaller, more predictable and easier to sandbox. The cost is throughput. Wasmi also reports resistance to compiler and JIT bombs during translation, and its 2.0 release closed much of the performance gap with the previous version.

Which WebAssembly proposals does Wasmi support?

Supported proposals include mutable globals, saturating float to int, sign extension, multi-value, bulk memory, reference types, tail calls, extended const, multi-memory, custom page sizes, memory64, wide arithmetic, SIMD and relaxed SIMD. Function references, garbage collection, threads and exception handling are still tracking issues, so modules using them will not load.

Has Wasmi been security audited?

Yes, twice. Versions 0.36.0 to 0.38.0 were audited by Runtime Verification Inc. on behalf of the Stellar Development Foundation, and version 0.31.0 by SRLabs on behalf of Parity Technologies. Both reports are committed as PDFs in the repository. Note that both audits cover pre-1.0 versions while the current release is 2.0.0, whose interpreter core is new.

What does fuel metering do in Wasmi, and what changed in 2.0?

Fuel metering gives a host a way to bound how much work a Wasm module can do, since WebAssembly has no instruction limit. In 2.0 the accounting became stable, tied to the input WebAssembly rather than Wasmi's internal bytecode, so the cost of a module no longer shifts between runtime versions. The release notes call it less precise but stable, and the same technique Wasmtime uses.

What changed in Wasmi 2.0 and how do I upgrade from 1.x?

Version 2.0.0, published 2026-09-01, replaced the interpreter core with an entirely new internal IR and executor, with operators storing results in accumulator registers, and benchmarks report a geometric mean of roughly 2.2 times faster than 1.0. The repository includes a migration guide at docs/migration-v1-to-v2.md listing the changes that require action from users.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. wasmi-labs/wasmi on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wasmi-labs-wasmi.svg)](https://hysenlabs.com/projects/wasmi-labs-wasmi)