Open-source project
nasa/spacewasm avatar
nasa/spacewasm

SpaceWasm: NASA JPL's flight-compliant WebAssembly interpreter

A flight-compliant WebAssembly interpreter.

1,577 stars60 forksRustApache-2.0

At a glance

What is it?
SpaceWasm is a Rust implementation of the Wasm 1.0 specification built to interpret WebAssembly binaries on-board spacecraft, with a streaming decoder and a fixed-block allocation model. It is aimed at flight-software teams, not at general Wasm workloads.
Who is it for?
Adopt SpaceWasm if you are embedding an interpreter into flight software that must decode a Wasm binary in bounded chunks and cannot tolerate panicking allocation failures. Do not adopt it if you need the full Wasm specification, threads, or the standard Rust allocator, because the interpreter deliberately constrains the spec and ships its own data structures.
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 4 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 17, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What SpaceWasm is for, and who it is not for

SpaceWasm is an implementation of the Wasm 1.0 specification meant to interpret Wasm binary on-board spacecraft. It is developed at NASA JPL, and the README gives four reasons for building it. The first is sequencing: high-level spacecraft activities are typically encoded outside the embedded flight software in a command sequence, and the form and capability of sequences has varied from mission to mission, producing assorted implementations. The second is sandboxing: flight-software development is expensive because validating a new capability often means validating interactions with the entire system, and WebAssembly gives untrusted or low-trust executables a way on-board where flight software can restrict access and compute time. Portability and access to a wider tooling community are the other two.

That framing tells you who this is for. It is for engineers writing or embedding flight software who already accept the constraints of that environment: fixed memory regions, deterministic behaviour, and a verification and validation process that cannot absorb a new subsystem every time someone wants to run a script. It is not a general-purpose Wasm runtime for a server or a browser. Nothing in the README positions it that way, and the design decisions described below actively work against that use.

Bytecode is decoded into an intermediate representation first

SpaceWasm does not execute WebAssembly bytecode directly. The README states the reason plainly: Wasm bytecode is meant to be small and structured to validate easily, and those properties make it slow to execute in place. During decoding, SpaceWasm converts instructions into another intermediate representation with properties better suited to interpretation. The IR is documented separately in docs/ir.md.

The project ships two components. The first is a decoder and validator that reads the Wasm binary in chunks and decodes it to an executable form. Validation happens during decoding, so there is no second pass over the bytecode. The second is the interpreter itself, which operates on linear memory and interfaces with hooks supplied by the embedder. Embedding means instantiating the interpreter and providing implementations for the functions the module imports; the README notes that the set of imported functions is typically fixed and should be specified at compile time for both the Wasm module and the embedder.

The streaming part is the interesting design choice. Many Wasm interpreters require the binary to be handed over as one linear blob, which is fine where the same memory regions can be reused for different purposes. Flight software generally assigns fixed portions of memory to fixed purposes, so requiring the whole binary to fit in one chunk is not feasible. SpaceWasm decodes in a single pass as chunks arrive from the filesystem, and the stream must provide chunks synchronously.

The allocation model is the real constraint

SpaceWasm replaces the standard Rust allocator with its own data structures, and the README lists the rules those structures follow. Allocations happen over a discrete number of fixed-size blocks called pages, which are distinct from Wasm linear memory pages. Deallocation cannot precede allocation. Sub-regions inside pages cannot grow or shrink, so sizes must be fixed ahead of time. Memory usage must be deterministic. Allocation failures must not panic.

The README is explicit that the standard Rust allocation API does not meet these constraints even with custom allocators. That is a strong statement and it shapes everything downstream: the project carries its own data structures, and those contain the bulk of the unsafe Rust in the codebase. The remainder of the unsafe code lives in performance-critical paths (src/stack.rs, src/memory.rs, src/ir_reader.rs), the ValType conversion in src/types.rs, and the FFI layer in crates/spacewasm_c_api and crates/spacewasi. Bounds on those raw accesses are established by the up-front verifier, and the optional strict-assertions feature re-checks them at runtime.

Two consequences follow. First, Wasm linear memory pages are allocated outside the dynamic memory pages, so the two memory systems are separate concerns you have to reason about independently. Second, the README adds a note that these limitations are enforced on the implementation of the interpreter and not on the Wasm bytecode it interprets. The constraint is on the runtime, not on what you feed it.

Building it and running a WASI module

SpaceWasm uses the Rust 2024 edition and the minimum supported Rust version is 1.87, matching rust-version in Cargo.toml. The README warns that older toolchains fail to build with an edition2024 error, so check your toolchain before anything else.

The workspace root is a Cargo workspace whose members are everything under crates/. The library itself is named spacewasm and is described in Cargo.toml as a no_std WebAssembly 1.0 decoder, validator, and interpreter for on-board spacecraft use. The release profile sets opt-level 3, thin LTO, codegen-units 1, and panic = abort. That last setting matters: the README's C library is a no_std staticlib/cdylib and cannot unwind, so panic = abort applies workspace-wide, with cargo overriding it back to unwind for cargo test.

For a first real run, the spacewasi crate provides a binary that runs WASI 0.1 (wasip1) modules in a sandboxed environment, with command line flags for mounting host directories and environment variables. The README walks through compiling a test program, converting it, and running it, because the interpreter expects MVP-compatible input:

bash
clang --target=wasm32-wasip1 -mcpu=mvp hello_universe.c -o hello_universe.wasm
crates/spacewasi/scripts/wasm2mvp.sh hello_universe.wasm
spacewasi hello_universe.wasm

The third command prints hello universe! according to the README. The wasm2mvp.sh step is not optional decoration. It converts the module to an MVP compatible file, which is the shape this interpreter is built to accept.

Where SpaceWasm is the wrong tool

The streaming design costs you specification coverage. The README states that SpaceWasm is highly optimized to reduce peak memory usage and to avoid requiring deallocation after allocation for streaming, and that to this end there are certain constraints imposed on the WebAssembly specification, pointing at an interpreter-limitations section. Those constraints are the price of bounded peak memory. If your module relies on a construct outside that subset, this is not a drop-in runtime.

The second limitation is the allocation rule itself. Deallocation cannot precede allocation, and sub-regions inside pages cannot grow or shrink. That rules out workloads whose memory profile is naturally dynamic. A long-running service that allocates and frees buffers of varying size does not fit this model, and the README's note that the constraints apply to the interpreter rather than the bytecode does not change that.

Third, the tooling story is incomplete. The README describes a spacewasm-check executable intended to measure per-binary memory use on the ground, and marks it as not yet developed, with a pointer to a note under limits for Wasm module producers. So the workflow of measuring a module's decoder footprint before flight is described as planned, not available. Treat any assumption about per-module memory budgets as something you must establish yourself.

Finally, the unsafe surface is concentrated but real. The README accounts for it: the custom data structures, the operand stack, linear memory, IR reader, ValType conversion, and the FFI crates. The up-front verifier establishes the bounds, and strict-assertions re-checks them at runtime. If you want those runtime re-checks in a fuzzing or regression build, that feature is not on by default.

How it differs from a general-purpose interpreter such as wasmi

The Makefile names a direct comparison point. One of its targets is fuzz-validate-differential, described as the validate differential fuzzer running SpaceWasm against wasmi. wasmi is a separate Rust WebAssembly interpreter, and the fact that the project fuzzes its validator differentially against it tells you the two are close enough in input handling to be cross-checked.

The difference is in what each one optimizes for. A general-purpose interpreter is built for a host that has an operating system, a heap, and the standard library available, and it can assume that allocation succeeds or that a failure can be handled normally. SpaceWasm is built for the opposite assumption. Its allocation model forbids panics on failure, requires deterministic memory use, and forbids deallocation before allocation. Its decoder is built to consume chunks synchronously rather than a single blob, so peak memory stays bounded on a system where fixed memory regions are assigned per purpose.

That is the trade you are making. If you are on a normal host and want broad Wasm support, the constraints SpaceWasm imposes are pure overhead. If you are on a spacecraft where the binary cannot be assumed to fit in one region and an allocation failure must not take down the flight software, the constraints are the feature. The differential fuzzer is the mechanism the project uses to keep the validator honest against a more conventional implementation.

Licence, maintenance and upgrade cost

SpaceWasm is licensed under Apache-2.0, stated in the README badge and in Cargo.toml under workspace.package, and the repository carries both a LICENSE and a NOTICE file. The NOTICE file is the one to read carefully if you redistribute, since Apache-2.0 obligations around attribution attach to it. That is a description of the files present, not legal advice; your own counsel should review how the licence interacts with your programme's distribution model.

The repository is not archived, and the last push was on 2026-09-16. The most recent releases are v0.7.1 and v0.7.0, both dated 2026-09-04, with v0.6.4 on 2026-08-24. The version field in the workspace manifest is 0.0.0, so the crate inherits its version from the workspace rather than declaring one at the root.

Upgrade cost is dominated by the MSRV and the edition. The project is on Rust 2024 and requires 1.87 or newer, and older toolchains fail with an edition2024 error rather than a warning. Pinning a toolchain older than that in a flight build environment means you cannot build this at all. Beyond the toolchain, the dependency surface is deliberately thin: the only runtime dependency listed is libm at a pinned version, with serde and serde_json pinned as dev-dependencies. Pinned versions cut both ways. They limit surprise breakage, and they also mean you are the one who decides when to move them. There is also a SECURITY.md and an AI_POLICY.md at the repository root, which is unusual enough to be worth reading before you contribute.

Editorial conclusion

Adopt SpaceWasm if you are embedding an interpreter into flight software that must decode a Wasm binary in bounded chunks and cannot tolerate panicking allocation failures. Do not adopt it if you need the full Wasm specification, threads, or the standard Rust allocator, because the interpreter deliberately constrains the spec and ships its own data structures. Before committing, verify which Wasm constructs your toolchain emits, confirm your Rust toolchain is at least 1.87, and check whether your target build needs the strict-assertions feature enabled.

Frequently asked questions

Does Netflix use Wasm?

The SpaceWasm README does not discuss Netflix or any commercial streaming service. It describes SpaceWasm as a Wasm 1.0 implementation developed at NASA JPL for interpreting Wasm binary on-board spacecraft.

Is Wasm still used?

The README does not make claims about overall WebAssembly adoption. It gives four reasons for using Wasm on spacecraft: consolidating sequence implementations around an industry standard, sandboxing low-trust executables, portability, and access to a wider tooling community.

Which coding language does NASA use for SpaceWasm?

SpaceWasm is written in Rust and uses the Rust 2024 edition, with a minimum supported Rust version of 1.87 matching rust-version in Cargo.toml. The README notes that older toolchains fail to build with an edition2024 error.

How can I open a Wasm file with SpaceWasm?

The spacewasi crate provides a binary that runs WASI 0.1 modules in a sandboxed environment. The README's example compiles a C file to wasm32-wasip1, converts it with crates/spacewasi/scripts/wasm2mvp.sh, then runs spacewasi hello_universe.wasm, which prints hello universe!.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. nasa/spacewasm on GitHub
  4. README
  5. Releases
Community notes

Community notes