Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

wasmmod

v0.1.4-alpha — signed packs for Python (.wasm / .aot / .elf).

Experimental. This is a pre-release (-alpha). APIs, pack layout, verify/HTTP surfaces, and build flags may change without a stable compatibility promise. Default-off on purpose (MICROPY_PY_WASM=0); treat enablement as an experiment until a non-alpha release. Engine matrix and demos are exercised often, but the surface is still moving fast.

One pack file can ship C + Rust + embedded Python, talk to peer packs, and call back into the host. Wasm/AOT run on WAMR; ELF (.elf / .<arch>.elf, ET_REL) uses an in-tree reloc loader (no dlopen). Same MicroPython wasm module either way (CPython path planned). Opt-in only (MICROPY_PY_WASM=0 by default); host trees stay unchanged unless they include the submodule and turn the flag on.

Drop-in submodule: extmod/wasmmod → #include "extmod/wasmmod/...".

Install (two ways)

Role How
In MicroPython / metalpython Git submodule at extmod/wasmmod (upstream-compatible layout). Firmware via ports/micropython/micropython.mk.
Standalone host pip install --pre pymergetic-wasmmod — ships a trimmed source tree + pulls pymergetic-wasmmod-tools (wasmmod CLI). No µPy checkout required for pack/sign/inspect/cdn.
pip install --pre pymergetic-wasmmod
# optional: upy pack targets need mpy-cross on PATH (or MPY_CROSS=…)
wasmmod pack path/to/example -o hello.wasm
python -c "from pymergetic.wasmmod.rt import require_root; print(require_root())"

Override tree discovery with WASMMOD_ROOT= if you keep a git checkout beside the wheel.

Real MicroPython REPL — wasm builtin, load packs


Why it shows off

Superpower What you get
Multi-impl packs C, Rust, and pack-local Python in one artifact
Containers .wasm · .aotN · .elf / .<arch>.elf (same MPWI/sign/finder)
Guest → guest bridge / ELF client import peers without host glue per call
Guest → host → Py wasmmod.host slots, buffers, mem cookies, object handles
Engine matrix Interp · AOT · Fast JIT · LLVM JIT — same Wasm packs; ELF skips WAMR
caller\callee | Py  | C   | RS
--------------+-----+-----+----
Py            |  ✓  |  ✓  |  ✓
C             |  ✓  |  ✓  |  ✓
RS            |  ✓  |  ✓  |  ✓
+ guest→guest, guest→host, rich i64/f32/f64 …

make -C examples test-engines → 57 cases × 4 engines.


Taste in three languages

Pack Python calls a native export in the same .wasm:

# hello/src/__init__.py
def greet():
    return "hello from pack py"

def answer():
    return hello()   # → C export in this pack

C guest imports peers + host with one macro:

#include "../../guest.h"

MP_WASM_IMPORT("hello", int, hello, void);
MP_WASM_IMPORT("mixed", int, mixed_answer, void);
MP_WASM_IMPORT("wasmmod.host", int, call_i32, int slot, int arg);

int via_rs(int x)   { return rs_square(x); }   /* same-pack Rust */
int via_hello(void) { return hello(); }        /* peer pack */
int via_host(int x) { return call_i32(0, x); } /* host slot */

Rust freestanding export (linked into mixed / bridge):

#![no_std]

#[no_mangle]
pub extern "C" fn rs_answer() -> i32 { 7 }

#[no_mangle]
pub extern "C" fn rs_i64_answer(x: i64) -> i64 { x + 7 }

Host — real unix MicroPython REPL (wasm is a built-in; see help('modules')):

MicroPython … on 2026-08-03; linux [GCC …] version
Type "help()" for more information.
>>> import sys
>>> sys.implementation.name
'micropython'
>>> help("modules")
…  requests/__init__ wasm
…  select            websocket
…  socket
Plus any modules on the filesystem
>>> import wasm
>>> wasm.path.append("packs")
>>> wasm.install_hook()
>>> import hello, mixed, bridge    # packs/hello.wasm etc.
>>> hello.greet()
'hello from pack py'
>>> bridge.via_rs(6)
36

HTTP roots use the same static layout (native C GET). With a baked root CA, signed packs verify over the wire:

make -C examples test-signed   # → test-http-verify OK

Signed HTTP pack: baked trust, [httpd] GETs, import hello

Example without verify: wasm.verify(False); wasm.install_hook("http://host/packs/") then import hello. See also make -C examples test-http.

FQN → file: import a.b.c searches wasm.path then sys.path. A pack can sit at any depth; if only the leaf file exists, missing parents are thin namespace packages (PEP 420-ish) so a.b.c stays attribute-reachable. VFS pack roots are scanned so import a / a.b work when children exist (flat a.b.c.wasm or tree a/b/…).

Path form (dots → /): a/b/c/__init__.wasm · a/b/c.wasm
Flat form (e.g. under packs/): a.b.c.wasm

Containers / preference: MICROPY_WASM_CONTAINERS (unix default elf,aot,wasm) picks *.elf / *.<arch>.elf / AOT / Wasm (+ .zlib). Nested names inside one pack (hello.util) still come from embedded pack-Python when no leaf file exists. Explicit: wasm.load_pack("packs/foo.wasm", "foo") or …/foo.elf. Browser builds stay wasm-only (MICROPY_PY_WASM_ELF=0).

Inspect (offline): wasm.symbols / addr2line / locations / disasm / mpy_disasm (and wasmmod inspect, wasmmod-read symbols|…). CDN Inspect UI talks HTTP only. See docs/PACK.md.

make -C examples demo    # real REPL: micropython -i < demo_readme.py
make -C examples repl    # interactive — type it yourself
make -C examples test-elf

Layout

wasmmod/
├── pack.*  runtime.*  forward.*  verify.*   portable core
├── fetch.*  io.h  wasmmod.c  finder.*  host.*  MicroPython host
├── ports/                                   make / cmake / mpconfig / PORT.md
│   └── micropython/webassembly/             browser: js.fetch I/O + Emscripten WAMR
├── tools/wasmmod.py                         shim → pymergetic-wasmmod-tools (`wasmmod` CLI)
├── python/pymergetic/wasmmod/rt/            PyPI package (bundles share/ on build)
├── hatch_build.py                           stages cleaned share/ into the wheel
├── examples/                                sources + packs/ + call matrix
│   └── packs/                               built <name>.{wasm,elf,aotN} artifacts
├── screenshots/                             README eye-catchers
├── docs/PACK.md                             section format
├── BRANCHES.md                              MetalPython host branch layout
├── VERSION                                  → wasm.version (always when built)
└── third_party/wamr                         WAMR (Apache-2.0; not on PyPI — firmware only)

Pack sections are named wasmmod.pack / wasmmod.imports / wasmmod.host / wasmmod.sig — see docs/PACK.md. Host I/O replaceability (Metal async, browser js.fetch): ports/PORT.md.

CDN / channel publish (lead + @version pins, index schema, browser µPy shell): separate repo metal-cdn.

One-shot release (pack → AOT → sign → zlib → upload):

pip install --pre pymergetic-wasmmod
wasmmod publish examples/hello --version 0.1.0 \
  --key .keys/sign/leaf.key.pem --chain .keys/sign/chain.der \
  --cdn-url https://cdn.example/cdn --token "$METAL_CDN_TOKEN" --claim

# Also upload a prebuilt ELF twin (staged under -o; --arch inserts CDN infix):
wasmmod publish examples/hello --version 0.1.0 \
  --elf examples/packs/hello.elf --arch x86_64 \
  --key .keys/sign/leaf.key.pem --chain .keys/sign/chain.der \
  --cdn-url https://cdn.example/cdn --token "$METAL_CDN_TOKEN"

Remote index / lookup / download (pip-style):

python3 tools/wasmmod.py cdn list
python3 tools/wasmmod.py cdn search hello
python3 tools/wasmmod.py cdn show hello
python3 tools/wasmmod.py cdn get hello -o ./packs --unwrap

CI: .github/workflows/publish-pack.yml (optional elf / arch inputs). Offline ELF section tests: pytest tools/test_wasmmod_elf.py; device smoke: make -C examples test-elf.

flowchart LR
  subgraph host [Host Python]
    PY[import hello / bridge]
    WM[wasm module]
    HS[host slots / mem / handles]
  end
  subgraph packs [Signed .wasm packs]
    H[hello<br/>Py+C]
    M[mixed<br/>C+RS]
    B[bridge<br/>Py+C+RS]
  end
  PY --> WM
  WM --> H
  WM --> M
  WM --> B
  B -->|guest→guest| H
  B -->|guest→guest| M
  B -->|wasmmod.host| HS
  HS --> PY

MicroPython integration

git submodule add https://github.com/pymergetic/wasmmod.git extmod/wasmmod
git submodule update --init --recursive extmod/wasmmod

Host tree only needs thin includes (no py/mpconfig.h edits):

# extmod/extmod.mk
ifeq ($(MICROPY_PY_WASM),1)
include $(TOP)/extmod/wasmmod/ports/micropython/micropython.mk
endif
# extmod/extmod.cmake
if(MICROPY_PY_WASM)
    include(${MICROPY_DIR}/extmod/wasmmod/ports/micropython/micropython.cmake)
endif()

Enable with make MICROPY_PY_WASM=1 (optional MICROPY_PY_WASM_{AOT,ELF,JIT,FAST_JIT,MATRIX}, MICROPY_WASM_CONTAINERS=elf,aot,wasm). Unix defaults ELF on when Wasm is on; browser forces wasm-only. Optional C defaults: #include "extmod/wasmmod/ports/micropython/mpconfig_wasm.h".
wasm.version is always the package release string from VERSION (e.g. '0.1.4-alpha').
wasm.wamr_version() returns the linked WAMR major.minor.patch string.
wasm.AOT_VERSION is the AOT file-format N used in CDN artifact names.
Guests see the same loader surface as WAMR imports on module wasmmod: version, mode, verify, trust_count, call_i32 (dynamic pack export; peer of host wasm.c_call / rs_call). Callbacks/slots stay on wasmmod.host.

CDN loader without loading a pack:

wasm.cdn("https://cdn.example/cdn")   # MetalCdnDriver; no pack fetch
wasm.install_hook()                   # import finder on; still no pack load
wasm.session_id("…")                  # optional correlation id (autoexec sets this)
# import hello                        # first HTTP / VFS pack load
names = wasm.catalog()                # GET …/index/lead → package name list
# wasm.publish("pkg", "0.1.0", data)  # NotImplemented until host request op

wasm.load(path_or_bytes) loads a raw .wasm module (not a metal pack). Prefer wasm.load_pack(...) / import for CDN packs.

Browser (Emscripten): out-of-tree variant under ports/micropython/webassembly/ — js.fetch I/O (with optional Bearer + X-Shell-Session-Id when configured), no pollution of vanilla ports/webassembly/. See that folder’s README for build + sync into metal-cdn static/repl/.

Optional host trampoline (metalpython): tools/wasmmod.py → extmod/wasmmod/tools/wasmmod.py.
MetalPython host branch layout (wasmmod PR track under product master): BRANCHES.md.


Build & test

From the host MicroPython / metalpython tree:

make -C ports/unix submodules
make -C mpy-cross BUILD=build -j"$(nproc)"   # pack freeze (.mpy)

make -C extmod/wasmmod/examples test           # interp matrix
make -C extmod/wasmmod/examples test-elf       # ELF containers (+ arch reject)
make -C extmod/wasmmod/examples test-engines   # all engines
make -C extmod/wasmmod/examples test-http      # static HTTP pack fetch
make -C extmod/wasmmod/examples test-verify    # embed wasmmod.sig + baked root CA
make -C extmod/wasmmod/examples test-signed    # PKI sign + VFS + HTTP verify (incl. ELF)
make -C extmod/wasmmod/examples demo           # real micropython -i session
make -C extmod/wasmmod/examples repl           # interactive unix REPL

# Browser µPy + wasmmod (needs emsdk on PATH):
source ~/emsdk/emsdk_env.sh
make -C extmod/wasmmod/ports/micropython/webassembly -j"$(nproc)"
# → ports/webassembly/build-wasmmod/micropython.{mjs,wasm}
Engine summary
================================================================
Engine       Result
------------ ----------------
Interp       PASS (57 cases OK)
AOT          PASS (57 cases OK)
Fast JIT     PASS (57 cases OK)
LLVM JIT     PASS (57 cases OK)
----------------------------------------------------------------
4 passed, 0 failed, 0 skipped
ALL ENGINES OK

Details: examples/README.md · pack format: docs/PACK.md · browser port: ports/micropython/webassembly/README.md · CDN channels / shell: metal-cdn.


Preview

More shots (click through for full size):

Pack calls — Py / C / RS / guest→guest   Same-pack call matrix   Engine summary — 4/4 PASS

pack-calls.png Cross-lang pack calls
matrix-table.png Same-pack Py ↔ C ↔ RS matrix
engine-summary.png Interp · AOT · Fast JIT · LLVM JIT
repl-demo.png Full MicroPython REPL (hero above)
http-verify.png Signed HTTP fetch + baked trust

License

MIT — Copyright (c) 2026 Rouven Raudzus <raudzus@pymergetic.com>
Nested third_party/wamr is Apache-2.0 (Bytecode Alliance); see LICENSE.

Metadata

Release files for pymergetic-wasmmod 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pymergetic-wasmmod 0.1.0a2
File Size Uploaded
pymergetic_wasmmod-0.1.0a2.tar.gz 165.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pymergetic-wasmmod 0.1.0a2
File Interpreter ABI Platform
pymergetic_wasmmod-0.1.0a2-py3-none-any.whl Python 3 none any Details

Total release size: 308.6 kB

Release files / pymergetic_wasmmod-0.1.0a2.tar.gz

Download URL pymergetic_wasmmod-0.1.0a2.tar.gz
Size 165.8 kB
Tags Source
SHA-256 checksum
How to use checksums
7d504eeb9b4aea80aa5eee07b1530fd14be424552277b551e252e61ff89a1109
BLAKE2b-256 checksum
How to use checksums
6c5605b488f9fdd091be075548aa99dd57bafe2faec8a5c874366cfc8537d43c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log

Release files / pymergetic_wasmmod-0.1.0a2-py3-none-any.whl

Download URL pymergetic_wasmmod-0.1.0a2-py3-none-any.whl
Size 142.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e97afdfa7a0270c15873a2292f440a2059b5289444e7e7444d20c397fd5db213
BLAKE2b-256 checksum
How to use checksums
fa23cce91cdc1f0ac37e09214c8f410d63b419c997a193a73e0ceb101d4c4963
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 5, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page