moxzi
Docs / Reference / moxzi command reference

moxzi command reference

What are the commands and flags of the moxzi CLI?

Every command and flag of the moxzi binary, transcribed from its own --help. If this page and moxzi --help disagree, the binary is right and this page is a bug.

moxzi compiles Motoko by running the self-hosted compiler — the same wasm that runs on-chain — locally, so a local build is byte-identical to an on-chain one.

Commands#

CommandWhat it does
build <entry>Compile a Motoko program to wasm.
link <input>Link an unlinked module, from build --no-link or from an on-chain compile.
deps <entry>Show the upload closure: every file that would be sent, and its VFS path.
verify --remote <url> --canister <id>Check an on-chain build: what the canister says it built, and from what. Every claim is checked, not believed. Read-only — no identity, no cycles.
stable-compatible <pre.most> <post.most>Check whether an upgrade between two stable signatures is safe — moc's --stable-compatible as a subcommand. The arguments are exactly what dfx canister metadata <id> motoko:stable-types prints. Silent exit 0 means compatible; otherwise each line is one reason it is not (M0169 a dropped variable, M0170 an incompatible type, M0216 data silently discarded).
infoReport the loaded compiler: module load path, cache state, exported methods.
cache path / cache clearShow where cached modules live; delete every cached module.

Global options#

These are accepted on every subcommand.

FlagMeaning
--compiler <WASM>Path to the compiler wasm. Overrides MOXZI_COMPILER and the search path.
--timingsPrint timing for the runtime phases.
--pem <FILE>Sign remote calls with this PEM private key instead of moxzi's own build identity. ed25519, secp256k1 and prime256v1; PKCS#8 or SEC1 — what dfx identity export and icp identity export both emit.
--identity-dfx <NAME>Shortcut for --pem ~/.config/dfx/identity/<NAME>/identity.pem (or under $DFX_CONFIG_ROOT).
--identity-icp <NAME>Shortcut for --pem <data-dir>/identity/keys/<NAME>.pem (or under $ICP_HOME). icp-cli stores keys in the system keyring by default, in which case there is no file to read and the error says how to export one.

The three are mutually exclusive. There is no shared identity store on the IC — dfx and icp-cli keep deliberately separate ones — so a PEM file is the only interop surface, and the name shortcuts are conveniences over --pem rather than a separate mechanism. Note that a dfx or icp-cli identity is usually the controller of the canisters it created, while moxzi's own key is a hot wallet holding what a few builds cost; the default is the safer one on purpose.

| -h, --help | Print help. --help is long-form; -h is the summary. |

| -V, --version | Print version, including the git SHA it was built from. |

build — output#

FlagMeaning
-o, --out <WASM>Where to write the wasm.
--debugEmit a debug build.
--no-idlDo not write the generated Candid interface next to the wasm.
--no-linkStop after compiling, leaving the module unlinked. Not deployable — the RTS is absent and link-only exports remain — but it is what an on-chain link consumes.
--linker <WASM>Path to the linker wasm. Overrides MOXZI_LINKER and the search path.
-v, --verboseForward the compiler's own debug output.

build — sources and packages#

FlagMeaning
--package <NAME> <PATH>Make a package available to mo:<name> imports, as moc --package does. Repeatable. Without it, mops sources supplies the project's packages.
--root <DIR>The directory that /src maps to. Defaults to the nearest ancestor holding mops.toml or dfx.json, else the entry file's own directory.

The --root default matters more than it looks: the VFS path is what the compiler records in its output, so two builds that disagree about the root produce different bytes from the same sources. moxzi deps --paths-only is the way to check that a local build and an on-chain one will agree.

build — moc-compatible flags#

These carry the same meaning they have in the reference compiler.

FlagMeaning
--stable-regionsForce the stable-regions RTS even without explicit Region use.
--stabilization-instruction-limit <N>Instruction budget for graph-copy stabilization. 0 = the canister default.
--measure-rts-stackHave the RTS track peak stack usage.
--max-stable-pages <N>64 KiB stable-memory pages reserved for stable variables. 0 = the canister default (100 GiB).
--default-persistent-actorsTreat a bare actor as persistent actor — it rewrites the actor to be persistent.
--trap-on-call-errorMake a failed send trap rather than return an error.

build — migrations#

Both flags carry moc's semantics; Migrations is the long-form explanation.

FlagMeaning
--enhanced-migration <DIR>Directory of migration modules, each exporting a public migration function. With it, a stable variable declares its type and omits its initializer — the value arrives through the migration chain, not from the declaration.
--stable-baseline <MOST>The .most signature the canister is already deployed with. With it, a stable field the chain does not produce is an error (M0267) rather than a warning (M0254) — there is a previous version, and it did not have that field. The check resumes at the migration the baseline records as already applied.

build — metadata#

FlagMeaning
--public-metadata <NAME>Emit this metadata section as icp:public instead of icp:private. Repeatable. Names: candid:service, candid:args, motoko:stable-types, motoko:compiler. The default is dfx's — candid:service and candid:args public — and passing the flag replaces that default.
--omit-metadata <NAME>Do not emit the section at all. Same names. Note motoko:stable-types is what the replica checks an upgrade against: omitting it does not make upgrades unsafe, it makes them unchecked.

build — local performance#

Both of these change how long a build takes and neither changes its bytes. Yields are checkpoints, so where the compiler pauses cannot affect what it emits.

FlagMeaning
--stack-budget <N>Scheduler inner-yield ceiling. 0 uses the canister's default of 200.
--force-gcCollect garbage at every message boundary.

--stack-budget is the single largest local speed lever on a big project. Off-chain the instruction counter is a stub, so the compiler's budget check can never fire and every yield becomes tick-driven — one self-call round trip each. The Internet Computer's small fixed stack is what forces a low ceiling there; locally you choose your own wasm stack, so it can go far higher. On a large project this is the difference between minutes and hours.

--force-gc exists because the compiler's memory valves assume a collector that reclaims between messages. Off-chain the incremental collector is scheduled off that same stub counter and can fail to keep up, leaving the heap climbing through phases that are waiting for it to fall. It is expensive in instructions, which does not matter locally.

verify — checking an on-chain build#

moxzi verify --remote https://icp-api.io --canister xcn6l-iqaaa-aaaai-raq6q-cai \
  --entry bin/compiler-canister/Main.mo \
  --expect-module bin/compiler-canister/compiler.wasm
job             0
entry           /src/bin/compiler-canister/Main.mo
files           177
sourcesDigest   db42464e59b3ec3a21334bf58593f1bd0e2edef13506f4e2c0371e3d6254f45a
compilerVersion 1.14.1
artifact        bfa18998…  (7717676 B, unlinked)

ok    sourcesDigest recomputed from 177 files and matches
ok    canister module hash (from the IC) c423310b…
ok    and the record's cacheVersion agrees
ok    the canister is running bin/compiler-canister/compiler.wasm
ok    all 177 local files match the recorded hashes

*** 4 check(s) passed ***
FlagMeaning
--job <n>Which build. Omit for the most recent.
--listList recent builds instead of checking one.
--entry <file.mo>Compare every file in your local closure against the recorded hashes.
--artifact <file.wasm>Compare a wasm's sha256 against the recorded artifact.
--expect-module <file.wasm>Assert the canister is running exactly this wasm.

Two things make this a check rather than a report. The module hash is read from the IC, not from the canister's own methods — a compromised compiler can say anything about itself through its API but cannot forge the hash the replica reports for the code it is running. And sourcesDigest is recomputed locally from the file list, so the digest is verified rather than displayed. A failing check exits non-zero.

The recorded artifact is the unlinked module: the canister compiles and does not link. Linking is deterministic, so the linked bytes follow from it — --artifact on a linked wasm will differ, and the message says so.

build — on-chain builds#

FlagMeaning
--remote <URL>Build on a compiler canister instead of locally: the replica or boundary-node URL. Requires --canister.
--canister <ID>Canister id of the compiler service. Required with --remote.
--progress <N>Report progress every N steps while compiling. 0 is quiet.
--status-secs <SECS>Additionally name the compiler's phase at most this often. 0 disables.
--max-steps <N>Give up after this many steps. Default 5000000.
--resume-job <N>Resume driving an existing remote job instead of uploading and starting a new one — for when the driver died mid-build. Skips upload entirely; the job id is in the CLI's earlier output or moxzi verify --list.
--yesAnswer yes to the payment confirmation, for scripts. Without it a paid remote build shows the escrow quote and asks before any money moves.

--status-secs is deliberately coarse: naming the phase is a query, and a query costs a state snapshot on the canister side.

Transport failures are retried, and a dead driver is recoverable. A dropped HTTP request is retried up to 5 times with backoff — but only calls that are safe under duplication (step, whole-file write, queries); a chunked upload restarts its whole file instead, because a duplicated appendChunk would corrupt it silently. If the driver still dies, nothing on-chain is lost: the job is a state machine on the canister, and --resume-job drives it home from wherever it stopped.

Retired flags#

Both are still accepted so that you get an explanation rather than an unknown-argument error.

FlagWhy it is gone
--no-eopThe classical non-EOP backend is unsupported. Its IR is never closed — @setTimer lives in InternalsSource, which only the EOP path parses — so it traps in ConstFold even on hello.mo.
--legacy-actorsLegacy non-persistent actors are not supported. persistent is how a program says its state survives an upgrade, and silently accepting an actor that does not say it is exactly the class of mistake worth failing on. Use --default-persistent-actors if you want bare actors rewritten.

Environment variables#

VariableMeaning
MOXZI_COMPILERDefault path to the compiler wasm. --compiler overrides it.
MOXZI_LINKERDefault path to the linker wasm. --linker overrides it.

Next#

On this pageCommandsGlobal optionsbuild — outputbuild — sources and packagesbuild — moc-compatible flagsbuild — migrationsbuild — metadatabuild — local performanceverify — checking an on-chain buildbuild — on-chain buildsRetired flagsEnvironment variablesNext