Moxzi Alpha 5 adds Nat256/128 to Motoko, plus a bunch of scalability and fixes

https://x.com/afat/status/2098104815653912657

0.1.0-alpha.5 (2026-09-09)

Three themes: 256-bit integers for EVM and cryptographic work, thousands of actors on
one machine
for simulation and swarm workloads, and verifiable provenance — a canister
you can prove was built from stated sources by a compiler running on the Internet Computer.


Added: native Nat256 and Nat128

256-bit arithmetic is unavoidable in EVM and cryptographic code. Motoko’s Nat is an
arbitrary-precision bignum, so every operation on a 256-bit value allocated on the heap.
Nat256 and Nat128 are now first-class fixed-width types held as 32- and 16-byte values,
with no allocation for chained arithmetic.

Complete from the start: + - * / % ** and their wrapping forms (+% -% *% **%), & | ^,
<< >>, all six comparisons, debug_show, conversions to and from Nat, Nat64, each
other and big-endian Blob, candid nat on the wire, and stable variables that survive an
upgrade.

These are an extension: moc cannot compile a program that uses them. Programs that don’t
are unaffected and still compile to identical bytes.

Added: running thousands of actors on one machine

Each Motoko actor is a separate WebAssembly instance owning its own memory, so a few thousand
of them exhausted a laptop. Four changes together make dense actor workloads practical —
simulations, swarms, one-actor-per-entity designs:

  • --ephemeral marks a module as disposable, and the runtime skips the per-message
    durability snapshot it would otherwise take. 129× the message throughput, for actors
    whose state need not survive a crash. It is recorded in the artifact rather than set by the
    host, so a module built durable cannot be run ephemerally by accident.
  • --compact-rts links a runtime with smaller garbage-collector partitions, for
    off-chain targets. A leaf actor’s committed memory drops from 4.06 MB to 0.31 MB.
    --actor-stack-kib and a memory cap tune it further.
  • Shared module code. Identical modules are compiled once and instantiated many times,
    in the server and in the browser.
  • Hibernation (moxzid --live-cap N). Beyond N live instances the least recently used
    actor is written to disk and its instance dropped; the next message to it wakes it in
    35–45 ms. 10,000 actors run on a laptop with memory bounded by the cap — uncapped
    they need roughly 26 GB. Snapshots are compressed (5.42 MB → 0.04 MB each, since an actor’s
    heap is mostly zeros) and identical module code is stored once rather than per actor.

Demo: a marble swarm with one actor per marble, thousands of them, rendered in a browser tab
or streamed from the server over a WebSocket.

Added: publishing a canister with verifiable provenance

You can now build a canister on the Internet Computer itself and publish it to an
ICRC-118 registry with a record of how it was
built — so anyone can check that the deployed bytes came from the sources claimed.

  • moxzi publish writes a registry entry carrying the source digest, the build options,
    the compiler version and the artifact hashes.
  • The compiler links the module itself, so the hash it vouches for is the hash of the
    bytes you actually deploy. The entry records which canister did the linking.
  • moxzi verify checks a deployed canister against that record.
  • The registry pulls the compiler’s word. After the upload, moxzi publish asks the
    registry to call the compiler canister with the LINKED hash (attestLinked); the registry
    files the answer as the compiler’s own ICRC-126 attestation and the CLI prints the verdict:
    verified (built on-chain), unverified (the compiler never linked these bytes – a
    local build publishes fine and says so), or DIVERGENT (an open report outranks every
    attestation). Change one byte of a verified wasm and the answer is a definite no.
  • moxzi publish refuses a --compact-rts artifact unless --allow-offchain: it is the
    off-chain runtime’s build, and a registry is what on-chain orchestrators install from.
  • --previous <version> records what a release upgrades from, giving the registry a real
    upgrade path. moxzi deprecate marks a version superseded without removing it.
  • --repo and --commit record where the source came from, read from git automatically.
    A modified working tree is not auto-filled: a commit hash that doesn’t describe the bytes
    being published would be worse than none.
  • moxzi build --gzip writes a gzipped module beside the wasm. Needed when a canister is
    passed as a call argument, which is capped near 2 MiB regardless of how the receiving
    canister was installed — a typical Motoko canister compresses several-fold.
  • icrc126-mo, a new Motoko package implementing
    ICRC-126 wasm verification: attestations,
    divergence reports, and a verification policy where a single attestation from a compiler
    canister carries the weight of several independent rebuilds.

Fixed

Miscompilations

  • debug blocks ran in release builds. debug { … } is meant to be removed entirely by
    --release, and never was — so release builds carried debug logging and paid for it. Now
    removed, matching moc.
  • let (?a, ?b) = e else f bound garbage. Destructuring a tuple with anything other
    than plain variables — an option, a literal, a variant tag, a nested record — silently
    failed to bind, and using the result trapped with no diagnostic. Worse, a pattern that
    should have failed matched instead: let (8, b) = (7, 9) returned 9 where it must
    trap. Affected any tuple destructure whose right-hand side wasn’t a constant.
  • let x : Nat256 = <literal> read out of bounds. A wide literal bound with let
    was stored in the wrong representation and read back as garbage rather than as zero.
  • Timers armed before an actor hibernated never fired. The deadline was preserved but
    nothing scheduled it, so the task simply never ran until unrelated traffic woke the actor.
  • return inside a local async function poisoned the function’s result type to Any.
  • Linked modules lost their function names, making profiles and traps harder to read.

Programs that were accepted but should have been rejected

Each of these compiled here and is refused by moc, so code could pass here and fail on a
real deployment:

  • Colliding field or variant-tag hashes (M0122, M0019), duplicate definitions
    (M0018) and duplicate bindings in a pattern (M0017).
  • Stable variables of non-stable type (M0131) — the check was missing entirely.
  • shared silently dropped from generic function types, so a shared function’s body could
    disagree with its declared control flow (M0078, M0079, M0041).
  • system capability errors both over- and under-reported (M0077).
  • Non-productive type definitions (M0157), contextual-dot calls with the wrong arity
    (M0233), generic calls with mismatched arguments (M0098), signed literals against
    non-fixed-width types (M0050), and M0257.
  • ?, weak and ?? accepted type forms the grammar does not allow there, such as
    ?actor {} and ?async T.

Also added: M0155 warns that Nat subtraction may trap, and M0195.

Diagnostics

  • An unannotated <system> function, and an actor class body, now hold the system
    capability as they should.
  • Member types are checked before bodies, so the reported error is the first real one.
  • A non-toplevel actor (M0069) is reported after type checking, so a program with two
    errors reports the same one moc reports.
  • --package pointing at a missing directory is an error (M0012) rather than silently
    ignored.

Contributions back to Motoko:

Coming in Alpha 6

  • Get back to parity with moc (now at 1.16.0)
  • Loop optimization when dealing with array indexes.
  • More motoko magic. I want 100,000 marbles
  • What else?

Back then life was simple. Now basic addition comes with unsolicited heap allocations, misdirected pointers, and a cleanup crew.

Thank you for sharing your work here.