Building on-chainalpha
What does it mean to compile Motoko on-chain, and why would I want to?
An on-chain build is a Motoko compile performed by a canister on the Internet Computer: you upload your sources into the canister's filesystem, it runs the compiler wasm as a few thousand ordinary update messages, and it hands back the artifact. The constraint that shapes everything else: the canister is subject to the same execution limits as any other canister, so a compile is not one long call — it is a long sequence of calls, and that is why a build takes minutes rather than seconds.
The compiler running in the canister is the same wasm moxzi build runs locally. That identity is the product. A local build asks you to trust your own machine; an on-chain build produces bytes anyone can re-derive from sources anyone can hash, using a compiler whose module hash is public.
Local versus on-chain#
moxzi build (local) | moxzi build --remote (canister) | |
|---|---|---|
| Where the compiler runs | wasmtime on your machine | a canister on the IC |
| Who can verify the result | you | anyone with the source hashes and the compiler's module hash |
hello.mo wall clock | under a second | 52 steps, 71 s measured on mainnet |
| Large project wall clock | minutes | tens of minutes to hours |
| Cost | free | ICP escrow, metered and refunded |
| Sources visible to | you | you and the canister's operators |
| Output | linked, deployable wasm + .did | unlinked wasm; the CLI links it for you |
Measured on mainnet#
These are real builds against canister xcn6l-iqaaa-aaaai-raq6q-cai, 2026-08-21.
| build | source | messages | cycles spent |
|---|---|---|---|
hello.mo | 1 file, 217 B | 52 | ~0.001 T |
| the compiler, compiling itself | 177 files, 8.0 MB | 2,345 | ~1.6 T |
evm_engine | 661 files, 15.3 MB | 4,138 | ~1.6 T/attempt |
Both large artifacts came back byte-identical to the local build (compiler artifact hash 830f3e87…, evm_engine 178c6429… — full SHA-256 values in docs/mainnet-builds.md). The compiler compiling itself on-chain to a byte-identical fixed point is the strongest statement the project makes: the thing that produced the compiler and the thing the compiler produces agree.
Note that evm_engine is nearly twice the source size of the self-compile for about the same cycles per attempt. Cost tracks the shape of the code, not its size — the compiler's own enormous functions are the pathological case at 200,657 cycles per source byte, against 69,677 for evm_engine.
Why you would do this#
Provenance. The artifact was produced by a compiler you did not install, from sources recorded in canister state, on a public machine. A local moxzi build prints compiled by <path> (sha256 ...); for a remote build the compiler is whatever module the canister has installed, and the IC reports that module's hash to anyone who asks. The two hashes are the thing a verifier compares against the release notes.
Reproducibility as a check, not a promise. Because on-chain and local builds are byte-identical, running both is a real test. A difference is a bug in one of them, and that comparison runs continuously in this repo across a 221-program corpus.
Nothing to install. A build needs an ICP-funded principal and a network connection.
Privacy: operators can read your source#
Uploaded sources land in the canister's per-caller virtual filesystem, and that filesystem is canister state. Anyone who controls the canister can read it, until you remove the files or the canister evicts them. There is no encryption and no attempt at one — a canister that must compile your code must be able to read your code.
The mitigation is real but partial: builds that source their dependencies from mops tags upload zero bytes for those packages, because the canister already holds the content-addressed blobs. Your own sources still upload. If your code is closed, either accept the disclosure to the operator you chose, or run your own compiler canister — the wasm is in this repo.
Things you cannot do#
| Why | |
|---|---|
| Run two builds in parallel on one canister | One canister is one job queue. Builds serialize. Parallelism means deploying a second compiler canister. |
Deploy the artifact straight from getArtifact | It is unlinked by design — wasm-validate on it fails. See Linking and installing. |
Price a build from instructionsUsed | That counter reports only what runs inside the compiler's own wasm. Real spend measures 2.3x–3.4x higher, and the ratio is not constant. |
| Hide your source from the operator | The VFS is canister state. Tag builds avoid uploading packages; your own files still land there. |
| Read or steer someone else's job | Every job method checks owner-or-operator, and refuses silently (null/0/false) so probing job ids teaches nothing. |
| Compile an imported actor class library remotely in one shot | The CLI pre-builds each class library as its own job first, then feeds the linked artifact into the entry job. |
| Expect a fresh deployment to charge | Payment is inert until an operator configures a ledger and the CMC. An unconfigured canister compiles for free and says so. |
| Upload more than 64 MiB as one principal | Per-caller VFS quota; the global cap is 1 GiB. Writes past either trap. |
Next#
- Your first on-chain build — one command, start to artifact.
- How a build runs — why minutes, and what each phase does.
- What a build costs — the escrow formula and the refund path.