Open questionsalpha
What design problems are still unresolved?
These are problems moxzi has not solved, as distinct from decisions it has made. A known limit with a stated reason belongs on the limits page; this page is for the things that are genuinely still arguments. Everything here is verifiable in the repository, and none of it is chased in the alpha.
Correctness#
| Question | Where it stands |
|---|
Some warnings are still missing. Pattern coverage (M0145/M0146) and unused identifiers (M0194/M0198) landed 2026-08-26 — a faithful port of moc's coverage.ml plus a use-count scope discipline. Still absent: M0155 (operator may trap), M0244 (never-reassigned var), M0240/M0241, and unused warnings for function parameters, top-level ids and object fields. | The gap is measured, not guessed: on the compiler's own 177 sources moc reports 15 warnings, all in codes we do not yet emit, and we report zero false positives. The remaining codes are a parity gap like any other. |
Mutable data cannot cross Candid — and that is moc's design, not a gap. to_candid on [var T] or {var f} is a TYPE ERROR in both compilers (measured: moc M0173; ours now reports the same code, region and rendered type). The unimplemented write_alias path guards only classical (pre-EOP) stabilization — and a disassembly of moc 1.14.1's own EOP output shows it emits no write-side alias functions either: there is nothing to be byte-identical WITH. The read side (read_alias, for upgrading FROM classical state) is fully implemented here. | The serializer trap stays as a named sentinel in a path EOP cannot reach; if a classical-stabilization mode is ever added, write_alias comes with it (moc's design is documented in Note [mutable stable values], and the protocol is understood — buffer_size marks StableSeen, serialize rewrites tags to stream offsets). |
| The compiler's nesting ceiling in V8. A guest frame on V8's stack costs far more than one on wasmtime's, so a tab compiles ~60 chained operators where the CLI is untroubled. A frame diet took the requirement from ~13,759 JS-frame-equivalents to ~8,426 against a tab's ~11,154 — enough for ordinary programs, not a fix for nesting. | The designed fix is to move codegen's expression descent onto an explicit heap work-stack, so native frame depth stops tracking input nesting. Prior work is on the workstack-fullpipe branch and is not landed; byte parity is its absolute gate. |
Operations#
| Question | Where it stands |
|---|
moxzid is one process with one actor thread. Messages execute one at a time across the whole server; per-actor queues and round-robin give fairness, not parallelism (moxzi/server/src/main.rs:14, scripts/moxzid_fairness.sh). | Horizontal scale means more moxzids, and nothing coordinates two of them. A shared-runtime design is not attempted because the runtime is not yet safe to share. |
| Recovery is serial and blocking. Every actor is installed first, then each one's journal is replayed in turn, because a replayed message may call another actor and a half-populated registry turns a real call into "no actor at …". | The server binds and answers /health throughout, and /health/ready reports progress — but a large state directory means a long, sequential start. |
Auth is one shared bearer token, checked in constant time, with TLS delegated to a reverse proxy. Roles and per-user keys are post-alpha (main.rs:144). | Honest rather than half-RBAC, but it means every client of a moxzid is the same client. |
| Browser preemption rewinds coarsely. A terminated worker cannot report where it got to, so the rewind is to the last completed message, not the trapping one. | Autosave narrows the window to one message. Making it exact needs a preemption mechanism that survives the termination, which a worker does not provide. |
No certificates off-chain, by design. ic0.data_certificate_present returns 0 in every off-chain runtime. | This one is settled — a forged certificate would be verified and believed, which is worse than none — but it leaves certified-data programs with no off-chain test path at all, and that consequence is not solved. |
Trust and economics#
| Question | Where it stands |
|---|
| On-chain build operators can read uploaded source. The VFS is canister state; whoever controls the canister can read it. | Building from a mops tag uploads zero bytes and avoids the disclosure entirely. Otherwise the disclosure is printed and accepted. There is no confidential-build design. |
Escrow pricing rests on three measurements. escrowCyclesPerByte is 750,000, three times the worst rate observed (200,657 cycles per source byte, the compiler compiling itself); escrowCyclesFloor is 0.5 T. | instructionsUsed under-reports real spend by 2.3×–3.4× and the ratio is not constant across phases, so there is no formula — only a worst case with margin. Both constants are operator-settable precisely because they are estimates. |
| Storage is capped, not metered. The build canister bounds each store (VFS 64 MiB per caller and 1 GiB total, blobs 256 MiB, build cache 200 MiB, artifacts 100 MiB) instead of charging rent. | The argument is that all four stores saturated together cost about 2.2 T cycles a year, which one build covers — so the liability is a number that can be stated. If the storage rate moves an order of magnitude, that argument needs redoing. |
raw_rand is predictable to anyone holding the host seed. It is derived from (seed, canister, counter) so that log replay reproduces it. | Stated plainly in the source. There is no off-chain design that is both replayable and unpredictable, and this is not treated as solvable — only as something a program must not confuse with the IC's guarantee. |
Process#
| Question | Where it stands |
|---|
The registry sweep proves compiling, not correctness. scripts/mops_sweep.py compiles the top-40 mops packages (391 modules) with both compilers and compares function bodies; the 2026-08-26 run against moc 1.14.1: moc built 384, moxzi 383, differing function bodies 1.56% — one real finding: a module bound by destructuring (let { Buffer } = Utils) does not carry its type namespace, so Buffer.Buffer<Nat8> in type position is M0026 (test/fixtures/destructured-module-type-path.mo). | The genuinely open half is FUNCTIONAL: running each package's own test suite against the moxzi-built artifact. Compiling is not the bar — a miscompile builds cleanly and computes the wrong answer. That harness is not built. |
The corpus sweeps do not run in CI. Per-PR jobs run the native tests and the gates; the weekly corpus-sweeps job is a reminder until a corpus mirror exists in CI (docs/ci.md). | The sweeps run in make alpha-check before a tag, so a release is covered and a pull request is not. |
No invocation-ID deduplication on /call. A client retry after a timeout can execute an update twice; the IC dedups by request id, moxzid does not yet. | Documented at the endpoint and in the server skill. The durable fix (invocation envelopes with IDs, a dedup window over the WAL seq) is scoped in .plan/host-gap.md §3 (spec §47). |
| Documented counts drift. Prose figures in README/CHANGELOG lag the tree the moment a gate lands. | Resolved by policy: those documents now point at scripts/alpha_check.sh instead of quoting numbers. That prose figures can drift while gate figures cannot is exactly the argument for executable claims. |
A remote build cannot pre-build an imported actor-class library. The CLI refuses with an explicit message (moxzi/src/remote.rs:188). | Build the library first, or compile locally. The dependency ordering a remote build would need is not designed. |
Next#
- Known differences — the divergences that are decided.
- Limits — the bounds that are constants rather than arguments.
- Changelog — what is provisional and allowed to break.