Changelogalpha
What changed, and what breaks?
moxzi is at 0.1.0-alpha.1, its first public release, so nothing has broken yet — but plenty will. This page records what shipped 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.
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 |
0.1.0-alpha.1#
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 |
Since alpha.1 tagging began (unreleased)#
- 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.1#
| 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.