Changelogalpha
What changed, and what breaks?
moxzi is at 0.1.0-alpha.6. This page summarises each alpha and states the compatibility policy that governs what happens next, because an alpha whose stability rules are unwritten is one where every change is a surprise. The full, itemised record is CHANGELOG.md in the repository; the release notes quote the artifact hashes.
How versions work#
| Rule | Detail |
|---|---|
| One version for four products | the CLI, the server, the browser package and the on-chain builder version together; one tag covers the release train (CHANGELOG.md) |
| SemVer | with the alpha caveat below |
| Artifacts carry hashes | compiler.wasm and linker.wasm ship beside each release and their SHA-256s are quoted in the release notes, taken from dist/SHA256SUMS |
| The wasm is platform-neutral | the same compiler bytes for every target tarball; only the native binaries differ |
| Releases are gated, not hoped | scripts/release.sh stages a tarball and then builds hello.mo from inside it with MOXZI_COMPILER and MOXZI_LINKER scrubbed. If the clean directory cannot build, there is no release |
alpha-7 (unreleased)#
Three themes: Motoko actors on nine actor platforms, in the platform's own process, with the platform owning the actor's durable image; what one of those actors costs; and mops interop, including an Internet Computer agent written in Motoko. CHANGELOG.md under ## Next is the itemised record.
| Area | What changed |
|---|---|
| Hosts | one compiled actor passes the 16-scenario conformance matrix on Restate, Temporal, Dapr, Orleans, Akka (Pekko), Proto.Actor, Ray, wasmCloud and Cloudflare Durable Objects, each in process through moxzi-rts (linked, cgo, P/Invoke, JNA, ctypes, a wasmCloud host plugin, or inside the Durable Object); see /docs/hosts/in-process/ |
| Durability | contract v3: the platform keeps the actor's image in its own store, fenced by an image version, and commits each turn's delta before acting on it; the module is sent only when the runtime asks (409 {needModule}) |
| Cost | an active in-process actor holds ~110 KiB (was 10.6 MiB); one process held 100,000; activation from a stored image takes ~0.5 ms (was 2.9); a small turn stores ~2 KB (was ~5.8) |
| Agent | mo:ic-agent: query, submit, poll and read canisters over the IC HTTP interface from Motoko, with response verification; moxzi agent-bindings <file.did> writes a typed Motoko façade for a canister |
| Tooling | moxzi candid-compatible old.did new.did; a library can declare it needs moxzi (a [moxzi] table in mops.toml); mops test can run moxzi; games can ask for the network (capabilities: [http]) |
| Diagnostics | i<22 is a syntax error (M0001), as in moc; forward references rejected where moc rejects them (M0055); M0157 fires inside actor, object and module bodies; M0131 names --experimental-local-actor-persistence when an actor* instance makes a type non-stable; no false M0194 for an import used only in types |
| Correctness | generic self tail calls loop instead of recursing; a library of several declarations is the implicit module moc makes of them (import L "lib" exposes L.A, L.B) |
| Size | the compiler is 347 KB smaller (the embedded actor* runtime source is minified) |
0.1.0-alpha.6 (2026-09-22)#
Three themes: actor* — many independently addressable, independently upgradable actors inside one canister; a moc 1.16.1 baseline; and cheaper indexed loops and message arguments.
| Area | What shipped |
|---|---|
| Language | actor*: an instance lives in one canister's heap but has its own principal, its own Candid methods and its own upgrade story. async* methods are internal (callable with await*, never a message, absent from the interface); async and query : async are external, with a real msg.caller, exposed on the container as Class_method(id, …) so dfx, an agent or another canister can reach a subactor. Actor.id(c) is a principal you can hand out and store; a stale or retired one fails closed. A persistent actor* class keeps its instances, their state and their identity across an upgrade, and they can be held directly in a stable variable — adding a method does not disturb that, while changing a stored variable's TYPE takes a migration function, as it would for an actor |
| Flags | actor* is experimental and behind two flags: --experimental-local-actors <N> enables it, and holding instances in a stable variable additionally needs --experimental-local-actor-persistence. Without the second, a stable actor* collection is rejected with M0131 … non-stable type, which named the variable rather than the flag (fixed since: see alpha-7). No package import is required — the runtime is injected by the compiler. moc cannot compile these programs |
| Baseline | the compiler now tracks moc 1.16.1. Programs that use no moxzi extension still compile byte-identical to moc in the reference configuration |
| Code generation | the checked array/blob index optimization is on by default (--no-index-fast-path restores reference output); --wasm-tail-call and --candid-fast-path are opt-in |
| Embedders | deadlines, cooperative slices and checkpointed schedules |
| Correctness | and/or type operands were judged for forward references in the wrong place: type T = A and { s : S } with S declared later was rejected even though the two records have disjoint fields and the intersection never needs S — which is what kept mo:jwt, and therefore evm.mo, from building. Fixing it surfaced the mirror-image bug, that the union form had no check at all and silently accepted type A = B or C with B/C declared later. A self tail call inside a named nested function — func go(…) declared within another function — was not being turned into a loop, so it took a stack frame per iteration and trapped with a stack overflow around 24,000 levels deep; anonymous and top-level functions were optimized correctly, which is why the effect stayed invisible until a program leaned on mo:core's pure/List accumulator loops at depth. Generic self tail calls (func go<T>(…) calling itself at <T>) were still not optimized at this release (fixed since: see alpha-7) |
| Runtime | the GC now knows about upgraded static root arrays (a canister that grew its static roots across an upgrade could have them collected out from under it); fuel-backed performance counters no longer drift across budget refills; application stack exhaustion is identified as itself rather than surfacing as an unrelated failure; --compiler <path> pointing at a file that is not there is an error instead of a silent fall back to the search path; upgrading a suspended actor container is refused rather than producing a canister whose in-flight work cannot be resumed |
0.1.0-alpha.5 (2026-09-09)#
Three themes: 256-bit integers, thousands of actors on one machine, and verifiable provenance.
| Area | What shipped |
|---|---|
| Language | native Nat256 and Nat128 as fixed-width values (32 and 16 bytes, no allocation for chained arithmetic): the full operator set, wrapping forms, shifts, comparisons, conversions, candid nat on the wire, stable across upgrades. A moxzi extension: moc cannot compile programs that use them; programs that don't still compile to identical bytes |
| Server + browser | --compact-rts (2 MiB GC partitions) so a churning actor collects early; hibernation of idle actors with compressed snapshots and shared module code; a marble-swarm demo with one actor per marble |
| Provenance | moxzi publish to an ICRC-118 registry with the source digest, options digest, compiler version and linked hash; the compiler canister links its own output and indexes the linked hash; the registry pulls the compiler's attestation (attestLinked) and the CLI prints verified / unverified / DIVERGENT; --previous, moxzi deprecate, --repo/--commit, --gzip; the icrc126-mo package |
| Registry (astroflora) | open publication gated by ICRC-86 domain claims and bought storage (30 XDR per 70 MB, three years); verification badges and a provenance panel in the UI at registry.astroflora.xyz |
| Runtime | a canister that migrated from classical persistence can be upgraded again: classical-heap bytes in the newer metadata slots are validated instead of trusted (stock moc 1.10+ traps with cannot upgrade from an actor using enhanced migration; filed upstream as caffeinelabs/motoko#6363) |
| Correctness | miscompiles and wrongly-accepted programs fixed from the fail-suite campaign, diagnostics aligned with moc; --package to a missing directory is M0012 |
0.1.0-alpha.4 (2026-09-04)#
The release that came out of one question — "does moxzi implement blob:file:?" — and the audit of why the answer had been silently no. blob:file: imports, annotated imports, first-class .size on arrays, ±0.0 literals, Float32 comparisons, Int literals under a Float declaration; shared no longer silently dropped from generic function types; the compiler's crashes on ill-typed input turned into diagnostics. Every fix landed with the gate that would have caught it: the whole upstream corpus compiled and run against 1.14.1's expectations, data segments compared, the test-tree manifest, test/trap and test/fail executed with error codes matched.
0.1.0-alpha.3#
The first public alpha: one compiler, one semantics, four places.
| Product | What shipped |
|---|---|
moxzi (CLI) | compiles Motoko by running the self-hosted compiler wasm under wasmtime; build with 15+ moc-compatible flags, link, deps, info, cache, and build --remote |
| On-chain builder | the same compiler as a canister: per-caller VFS with quotas, ICRC-2 escrow at the CMC's live ICP↔cycles rate with refunds, a mops tag layer, a whole-build cache, self-driven resumable stepping at mainnet caps |
moxzid (server) | Motoko actors off-chain with IC semantics: atomic messages with rollback, timers, upgrades over HTTP, cycles, actor-to-actor calls with re-entrancy, durable spawned actors, journaled outcalls, kill -9 durability |
moxzi-web (npm) | the same runtime in a browser, plus a client library: install from a page, call through a dfx generate idlFactory unmodified, persist to IndexedDB, preempt via a worker, make outcalls |
Between alpha.3 and alpha.4#
- Pattern-coverage warnings (M0145 non-exhaustive, M0146 unreachable pattern): a faithful port of moc's
coverage.ml, firing at switches, try handlers, refutablelets andforpatterns. Verified message-for-message against moc 1.14.1 on switches over primitives, variants, tuples and options;let … elsecorrectly does not warn in either compiler. - Unused-identifier warnings (M0194, and M0198 for punned pattern fields) for block bindings, match-arm variables and loop patterns. Function parameters, top-level ids and object fields are deliberately not yet covered — under-warning is the safe direction. Zero false positives measured on the compiler's own 177 sources.
- Warnings from
--packagelibraries are suppressed, as measured against moc's behaviour; errors never are. - Diagnostics now print moc's exact position format — 1-based columns,
l.c-l.cranges collapsing to a point — for both errors and warnings, so editor jump-to-error and habits carry over byte-for-byte. - Fixed: a literal pattern over a fixed-width scrutinee (
switch (i64) { case (-5) … }) trapped the coverage checker — moc narrows the literal in the typer, we now narrow at the coverage boundary. Found by the old-vs-new byte-neutrality sweep (104/104 programs byte-identical otherwise); pinned by a fixture and a new gate that compiles every fixture with our compiler, not just reference moc. to_candidon mutable data now reports M0173 at the same region with the same rendered type as moc (was: M0175, per-argument, internal type syntax).is_localin the CLI parses the URL host instead of substring-matching the URL — a root-key-trust hardening.- The site's asset-canister library had two more proven bugs (upstream findings §7): updating any existing non-aliased asset trapped the whole batch, and a failed batch left
certified_datapointing at the batch's staging tree — every response then failed gateway verification and the whole site 503ed. Both fixed in the vendored library, both pinned by PocketIC reproducers that fail against the unpatched code; recovery from the live outage cost one identical-contentstore()call, not a re-upload. - moxzid ingress messages can carry a real caller (
x-caller), and the Change Date moved to 2030-08-31. - A build escrows at least 1 ICP, whatever its size — a gate on starting a build, not a price: a hello still charges ~0.009 and the rest refunds. Denominated in ICP rather than cycles so it does not drift with the ICP/XDR rate.
- Escrow recalibrated against measured builds. Compile cost is not proportional to source bytes — there is a large fixed part (prelude, internals, the typing environment) that a small program pays in full: a hello runs 25,100,000 cycles/byte where the compiler runs 229,000. The old flat per-byte rate over-escrowed a hello a hundredfold and left no headroom on large builds, because its "3x the worst measured" safety factor cancelled exactly against the 3x charge. Escrow is now
(fixed + slope x bytes) x safetyMultiple, with the multiple explicitly above the charge multiplier. A hello escrows 0.015 ICP instead of 0.29, and every size gets a uniform 1.67x headroom over its charge. - Fixed: the CMC top-up needs both the destination subaccount (which canister) and the
TPUPmemo (which operation). Sending the subaccount alone got a refund-with-reason instead of minted cycles. A top-up now actually converts revenue into cycles — verified on mainnet,cycles-minted=5,091,137,766. - Fixed: the job-id counter was
transient, so every upgrade reset it to 0 and the next job reused an id an existing settlement still occupied — overwriting the record naming whose escrow it was. For a settlement still open, that is the customer's refund with no path back. The counter is stable now, and id allocation additionally skips any id a settlement or job still holds. - Paid builds are now billed from the observed cycle burn (balance before minus after) rather than the per-message counters, which read ~20% low because the replica bills for page dirtying, GC and scheduling that no counter sees — so the canister's 1x share never refilled what a build actually cost. Reading the delta is only sound because builds are serial. It is also attackable, since anyone can send cycles to any canister: a deposit mid-build makes the balance rise and the burn unreadable, so the measurement falls back to the counters (which no outside deposit can move) and then to the escrow itself. Charging the full escrow is deliberately the worst outcome for whoever made the measurement unreadable. A refund can never exceed the escrow.
- Fixed, found by the first paid mainnet build: the 1x cycles top-up transferred to the CMC's main account, which
notify_top_upcan never accept — it re-derives the expected subaccount from the target canister's principal and refuses any other destination. Every paid build would have stranded its own top-up and the compiler would never have self-funded. And because that failure was retried forever, the settlement froze one step short of the donation, which no escape hatch could reach; a permanently-invalid notify now records the stranded block and advances. - Remote builds can sign with a key you already have:
--pem <file>, plus--identity-dfx <name>and--identity-icp <name>as shortcuts into those tools' stores. There is no shared identity store on the IC — dfx and icp-cli keep separate ones, down to different keyring service names — so PEM export is the interop surface, and a key held in an OS keyring gets an error saying how to export it rather than "file not found". The default is still moxzi's own build key, which is a hot wallet rather than a canister controller. - A remote build now quotes its escrow from the bytes it is about to upload rather than a flat maximum: a 206-byte hello quoted 6.43 ICP and now quotes 0.28. The escrow was always refunded, but locking up that much to compile hello is its own kind of broken.
scripts/canister_install.mjsinstalls modules over the ingress limit via the chunk store. The compiler is 5.8 MB, so every compiler upgrade needs it; before this the call died inside the agent with a stack trace that never named the size as the cause.- The on-chain compiler now enforces one build at a time: a start while another caller's job is live is refused up front (before any escrow moves), the owner of the live job may restart it (the actor-class flow), and a job whose driver goes silent for 10 minutes stops blocking. The compile pipeline's library-hoisting state is global, so this was a correctness guarantee, not just fairness.
The compiler is at moc 1.14.1#
The compiler began as a faithful port of moc 1.8.2; during the alpha it was modernized to 1.14.1, and its artifacts say so in motoko:compiler. The claim is measured — zero unported upstream language tests, 384/384 mops modules in the top-40 sweep, 359 of moc's own programs behaving identically, and a byte-identical self-compile fixed point that is now a standing gate. The features that arrived on the way: enhanced migration chains with --stable-baseline (see Migrations), moxzi stable-compatible, idl: Candid-interface imports, nested mixins, structural implicit synthesis, bit-exact float literals, warnings on successful builds, and --public-metadata/--omit-metadata.
Verification that shipped with 0.1.0-alpha.3#
| Claim | Figure |
|---|---|
| Corpus programs byte-identical across two compiler builds | 221 |
| Corpus programs identical browser vs native | 160, 0 diverging |
| Native runtime tests | 81 (cargo test --workspace -- --list) |
| Behaviour gates | 16 server, 16 browser, plus demos and compiler regressions |
| On-chain self-compile | 177 files, 2,345 messages, artifact 830f3e87… |
On-chain evm_engine build | 661 files, 4,138 messages, artifact 178c6429… |
Prose test-counts drift; scripts/alpha_check.sh is the source of truth for what a release actually runs, and the other documents now point there instead of quoting numbers.
Licensing, and the date that matters#
| Item | Value |
|---|---|
| License | Business Source License 1.1 — source-available, not open source, and we do not call it that |
| Free | non-commercial use, and commercial development, testing and evaluation |
| Requires a license | commercial production use, and competing hosted offerings (a compile service, actor hosting, an embedded runtime) |
| Change Date | 2030-08-31 — four years after this release |
| Converts to | Apache-2.0, per released version |
What is allowed to break#
During 0.x-alpha, treat everything below as provisional. This is not a promise to break them; it is a statement that a break would not be a violation.
| Surface | Stability |
|---|---|
| Output bytes for a given source | not allowed to drift silently — compiler_byte_neutral.sh is a release gate. A deliberate change appears here |
| Diagnostic codes and message text | provisional. M0000 is a catch-all today and may be split |
moxzid HTTP endpoints and JSON shapes | provisional |
The moxzi-web client library API | provisional |
| The compiler canister's candid interface | provisional; quoteEscrow() already exists only for older clients alongside quoteEscrowFor(sourceBytes) |
| Escrow pricing constants | operator-settable at runtime, and expected to move as measurements improve |
Persisted moxzid state directories | the write-ahead log format is positional; a format change would require a rebuild from a snapshot |
| Auth | one shared bearer token today; roles and per-user keys are post-alpha and will change the shape |
| Ingress identity | every ingress caller is the anonymous principal; authenticated ingress will change what actors see |
Things that are not in this release#
| Absent | Where it stands |
|---|---|
stable_sub, the real stable-type compatibility relation | unimplemented; moxzid warns instead. See open questions |
| Off-chain certificates | not planned — a forged certificate is worse than none |
A multi-node moxzid | out of alpha scope; more moxzids do not coordinate |
| CI corpus sweeps | the weekly job is a reminder until a corpus mirror exists in CI; the sweeps run in make alpha-check before a tag |
| Published crates and npm package | the manifests are reserved in packaging/reserve/; publishing is a human step |
Next#
- Open questions — what is unresolved rather than merely unshipped.
- Known differences — the deliberate divergences this release ships with.
- Limits — the numbers quoted above, with their sources.