Lots of cool stuff in alpha 7!
Motoko is now supported in 9 major AI/Agent/Actor based platforms beyond the Internet Computer -
Dapr.io
Microsoft Orleans
https://Restate.dev
https://Temporal.ai
https://Ray.io
https://Proto.Actor
Apache Pekko
https://wasmCloud.com
Cloudflare Durable Objects
- and you can talk back to the IC from all of them and your games using https://mops.one/ic-agent
There are also a bunch of optimizations, fixes, and upgrades:
0.1.0-alpha.7 (2026-09-29)
-
Release packaging: the toolchain tarball is the toolchain again (~18 MB), with optional
bundles beside it.moxzi-sdk-…(the in-process runtime library, header, C example and the
adapter sourcesmoxzi host newcopies),moxzi-hosts-…(the prebuilt Restate, wasmCloud and
Cloudflare hosts) andmoxzi-rts-static-…tar.xz(libmoxzi_rts.a) all unpack into the same
directory. One 148 MB tarball could not be published (the site canister hashes an asset in a
single message) and the installer downloaded all of it to keep a seventh; 68 MB of it were Go
build outputs shipped as adapter “source” by mistake, which the release now refuses. -
Fixed: the compiler merged same-named self-tail-recursive functions (miscompile, present in
alpha.6). Two zero-argument functions of one name that call themselves in tail position –
an iteratornextthat callsnext(), asbase’sTrieMap.entriesandText.tokensdo –
were compiled to ONE function after tail-call optimization, so one object ran the other’s code
and trapped. It surfaced asmo:http-assetstrapping in every upload (viamo:http-parser).
Held bytest/regress-behaviour/same-name-self-tail-calls.mo. -
Fixed: a library could use names it never imported (accepted programs
mocrejects).
Imported libraries were checked in the main file’s scope, soimport Iterin the main file
madeIterresolve inside a library that did not import it. Each library now sees only its
own imports, and such programs getmoc’s exact error (M0057 / M0026). Held by
scripts/lib_scope_gate.sh. -
Signed-in callers on every host, each platform’s own way. A caller who presents a
credential is authenticated by the host’s front the way its platform does – Cloudflare Access
(Cf-Access-Jwt-Assertion, verified with WebCrypto), ASP.NET Core JwtBearer on Orleans,
pekko-http + Nimbus on Pekko, golang-jwt on Proto.Actor / the Dapr host / Temporal, PyJWT on
Ray,jsonwebtokenon Restate and inside the wasmCloud component, and in the standalone
moxzi-dapr(the bearer the sidecar forwards) andmoxzi-restate(verified once, journaled in
ctx.run) – andmsg.calleris the
principal derived from (issuer, subject): the same person is the same principal on every host,
never an actor’s. ConfigureMOXZI_AUTH_ISSUER,MOXZI_AUTH_AUDIENCE,MOXZI_AUTH_JWKS. A
credential that does not verify is refused (401), never run as anonymous. The Caller Identity
row now also proves a valid token and refuses foreign-key, expired and wrong-audience ones. -
Security: nothing outside a host can choose an actor’s
msg.callerany more. Every
front forwarded ANYadmin/{op}to the service with the client’s body, so
admin/begin {caller: X}ran a turn as X, andadmin/resumeanswered a parked await with
invented bytes; the control routes (/_moxzi/deliver,hold,placement, …) had no
authentication; Restate’s handlers were all reachable on Restate’s own ingress with a
client-set caller; amoxzi-actor-hostservice tookcallerfrom any client. Now: admin
forwards onlyinstall|passivate|drain|upgrade|state|outstandingand dropscaller; the
control surface needsMOXZI_HOST_TOKEN(loopback only without one); services need
MOXZI_SERVICE_TOKEN(loopback only without one); Restate handlers need the token as an
invocation header; Dapr callbacks need the sidecar’sAPP_API_TOKEN. A new §61 row,
Caller Identity, proves on every host that ingress is anonymous, a routed call is the
calling actor, and each vector is refused (docs/host-abi.md, “Who may setmsg.caller”).
Operators of a host listening beyond loopback must now setMOXZI_HOST_TOKEN(and
MOXZI_SERVICE_TOKENfor HTTP-mode services). -
Fixed:
candid:serviceprinted Candid keywords bare as record fields and variant tags. A
field calledprincipalcame out asprincipal: opt principal;, which does not parse, so no
agent or host could read the interface – the ICDevs ICRC-72 orchestrator’s among them. Labels
are now quoted exactly where moc quotes them ("principal": opt principal;); the test program’s
section is byte-identical to moc’s. Held byscripts/candid_service_gate.sh. -
Fixed: a timer’s oneway sends waited for the next message. Oneways a timer sent (to its
own actor or another) ran only when something else next called the actor – on moxzid, on
every host, and in the browser engine – so a notification sent from a timer could sit
indefinitely. They now run in the timer’s own turn (scripts/moxzid_messages_gate.sh). -
moxzi-dapr (Motoko actors as a Dapr app) speaks host contract v4. Every call a message
parks is routed through the sidecar – several at once, a timer’s, and one started by a
message that already replied – and oneway sends are delivered; timers fire on the Dapr
reminder as messages of their own. A routed call presents the calling actor asmsg.caller
when the apps shareMOXZI_DAPR_CALLER_KEY(HMAC-signed; anonymous otherwise). The matrix’s
ICRC-72 and three v4 rows now pass on it. -
Fixed: a remind for a hibernated actor fired nothing. The v4 timer path did not wake an
actor that had gone to sleep with its timer due. -
Local-actor containers have timers. An
actor*container’sTimer.setTimernow fires
(LocalRuntime::fire_timer, one local transaction; a trapping run rolls back whole), so
moxzi.game’s loading protocol works for local-actor games (scripts/local_timer_gate.sh). -
The real ICDevs ICRC-72 stack on all nine host platforms. The mvevent orchestrator,
broadcaster, publisher and subscriber, built by moxzi, run end to end on Proto.Actor, Dapr,
Temporal, Restate, Orleans, Pekko, Ray, Cloudflare and wasmCloud: publication and subscription
registered, events published, handled by the subscriber and confirmed
(HOST_TEST=icrc72_real_stack scripts/host_matrix_gate.sh). Getting there fixed host bugs no
matrix row reached, among them: a due timer firing inside another message’s step, a timer armed at
install never reported, a remind that dropped a still-armed timer, timer writes racing outside
the key’s turn, a call to an unknown principal failing the step instead of being rejected, and a
oneway to self replacing its sender’s answer (docs/host-abi.md, version 4). -
Host contract v4: actors on a platform now behave like messages, not one chain of awaits.
Three shapes of ordinary Motoko failed on every multi-process host: a timer that awaits another
actor (its call was never routed, so the await never finished), two calls outstanding at once
(only the first was routed), and a method that replies while a call it started is still running
(the reply was dropped). Each step now reports the one message it ran: the invocation it
answered, every call it started, its sends and its timer (docs/host-abi.md, version 4). All
nine platforms (Proto.Actor, Dapr, Temporal, Restate, Orleans, Pekko, Ray, Cloudflare,
wasmCloud) route each call independently and pass the matrix’s three new rows, in-process and
as services (Restate’s service mode was not run: it needs a Restate server). moxzid passes them
too (scripts/moxzid_messages_gate.sh). A host that has
not moved still works on programs that never needed v4. Temporal: drain open workflows before
deploying, since the workflow code changed. -
Fixed a miscompile:
return await* f()(unitf) in a oneway or anasync ()method
trapped with “function invocation does not match its signature”. The implicit async of a
function with no result components was typedasync Anyinstead ofasync (), giving its
return continuation a parameter a unitasync*never passes. In a oneway the trap was silent
and rolled the message back, so the ICDevs ICRC-72 broadcaster lost state. With this fix the
mvevent PocketIC integration suite passes on canisters built by moxzi, as it does on moc’s.
Held byscripts/regress_behaviour_gate.sh(both compilers on PocketIC, one moc transcript). -
Three type-checker gaps that kept the ICDevs ICRC-72 canisters from compiling are fixed
(moc accepted all three). A onewaypublic func f() { await* g() }with no return type was
M0038; anasync*function passed where a<system>async*function type is expected was
M0096; a class field reading a module-path alias (let Vector = Types.Current.Vector) was
M0055. moxzi also emits moc’s M0242 warning on an implicit oneway now. The real orchestrator,
broadcaster, publisher and subscriber (mvevent) build with moxzi. -
moxzi host new <platform> <dir>: a Motoko actor inside your platform in one command.
Writes a project –actor.mo, moxzi’s adapter for the platform inhost/, arun.shand a
README – for Proto.Actor, Dapr, Temporal, Orleans, Akka (Pekko) or Ray (moxzi host list).
run.shbuilds the actor and the host, starts what the platform needs (Postgres in Docker, a
Temporal dev server), hands the host the actor and calls it. Every project is run twice on the
board:[1], then[2]from a new host process (scripts/host_new_gate.sh). Restate,
wasmCloud and Cloudflare are not scaffolded yet: their hosts build inside the moxzi repository. -
Three programs moc rejects are now rejected here too. An
inspectwhose message variant
leaves out a shared method, queries included (M0127: moc checks the declared type by
subtyping; moxzi checked only its shape); anasync/async*carrying a type that is not
shared, such as a local function (M0033, previously unchecked); and a genericasync* T
(a type parameter is not shared). An actor* reference is still allowed in anasync*.
Found by running the actor* port in OCaml moc against moxzi’s own fixtures. -
Actors address each other by key; no
learnstep. A hosted actor asks the host’s
management canister for another actor:moxzi_actor_of({ key = "Account/bob" })answers the
principal and routes it at once. The keys an actor resolves or calls travel in its image, so
a host restart needs nothing re-learned.POST /_moxzi/learnremains for principals obtained
another way. (Found on the way: management calls from an actor on a platform were parked as if
remote, soraw_randand friends could not work there; fixed.) -
deliveranswers in JSON when the turn was started with JSON, across a host restart too:
which invocations want JSON now travels in the parked actor’s image, and all nine hosts answer
in JSON when the runtime renders a JSON result. (Also fixed: the Cloudflare engine failed every
JSON call – the matrix drives it in raw Candid – and dropped JSON for a parked turn.) -
moxzi host newcovers all nine platforms. Restate (Docker for the server), wasmCloud
(nothing else) and Cloudflare (Node forwrangler dev) scaffold from PREBUILT hosts a release
ships underhosts/(scripts/stage_hosts.sh). Counter, ledger and saga run on each. -
moxzi host new --example ledger|saga: two use cases, proven on six platforms.ledger
(Account actors): a deposit retried with the same invocation id is applied once even after
the host process is killed and restarted; an overdraft traps and rolls the whole turn back; a
transfer awaits the other account’s credit.saga(Trip, Hotel, Flight): a booking parked on
anawaitsurvives the host process being killed and completes when the held call is
delivered; a full flight releases the room it took.moxzi host exampleslists them;
moxzi host principal <Type/id>prints the principal another actor calls a key by. The
scaffold gate runs both on Proto.Actor, Dapr, Temporal, Orleans, Akka/Pekko and Ray and checks
every answer. -
The exactly-once record travels one entry at a time: a turn is 49 → 36 µs and stores
~1.4 KB (from ~1.7 KB). The last 32 invocation outcomes, which answer a retried turn, were
recompressed into the image metadata on every commit. They are now 32 ring slots, a new chunk
kinddedupbesidememoryandstable: a turn writes one, and a platform stores them like
pages. All nine adapters store them;docs/host-abi.mdspecifies the kind for other
platforms. An activation whose slots are missing is refused, never run without its record;
images written before this carry the record in their metadata and still activate (their
next commit writes the slots). -
Restate on Linux: 50.9 ms → 5.2 ms per call. The Restate hosts now set
TCP_NODELAYon
the connection Restate opens into them; on Linux every step waited for a delayed ACK (macOS
hid it). Linux figures now cover Proto.Actor (1.5x its own actor), Temporal (1.3x) and Restate
(1.3x) in process (scripts/linux_host_latency.sh). -
A turn stores ~21% fewer bytes: ~1.7 KB, from ~2.2 KB. The image metadata every commit
rewrites now names its module (deployment: {deployment_id, module_hash}) instead of
repeating the stable signature, interface hash and compiler version; activation reads those
from the module. Adapters read onlymodule_hashand are unchanged; images stored before
this activate unchanged. -
A committing turn is ~22% cheaper, and activation ~30% faster. Pages a turn ships are
still raw DEFLATE, now written and read by zlib-rs instead of miniz_oxide: 63 → 49 µs a turn
in process, 0.59 → 0.41 ms to bring an actor back from its image, for ~5% more bytes a turn
(2.1 → 2.2 KB). Images stored before this activate unchanged. -
The in-process runtime ships prebuilt. Release tarballs carry
lib/libmoxzi_rts
(.so/.dyliband.a),include/moxzi_rts.h,examples/rts_hello.cand the adapters
host newcopies. The release is refused unless the example compiles against the shipped
library and runs an actor, and unless the macOS library is relocatable. -
100,000 actors in one process. The in-process runtime now reserves address space for
each actor’s memory by what it holds, not ~4 GiB up front; one process ran out of address
space at ~20,000 actors before, and now holds 100,000 (13.9 GB resident). Memory per actor,
bytes per turn and activation time are held by a new ratchet on the release board. -
Cold activation is ~5x faster: ~0.6 ms, from 2.9 ms. Bringing an actor back from its
stored image hashed the 380 KB module twice, and every platform read that module from its
store and sent it with each activation. Module hashes are now remembered, and the platform
sends the image alone; the service asks for a module (409 {needModule: hash}) only when it
does not already have it. All adapters speak this; the host ABI documents it. -
A turn stores ~1.6-2.4 KB, down from ~5.8 KB (1.6 KB averaged over 200 turns, 2.4 KB over
20,000, where the collector’s page rewrites dominate). A counter increment changes about 45 bytes, but
each commit stored whole compressed pages plus a metadata record whose dedup list wrote every
reply byte as a JSON number. Changed pages that the platform already holds now travel as byte
patches inside the metadata record (applied on activation), and the dedup list is stored
compressed. Actors with larger replies save much more: a 1 KB reply cost ~3.5 KB in every
later commit. Images written before this still activate. -
An in-process actor now holds ~115 KiB, down from ~10.6 MiB. The host runtime kept a full
copy of every actor’s heap for rollback, and read every page of a new actor’s memory to build
its first image (on macOS that read makes each page resident). Rollback now uses the
before-images of the pages a message writes, and new actors track their writes from the
moment they are created. Cold activation writes only the image’s non-zero pages: ~2.8 ms for
a fresh actor. The Cloudflare Durable Object engine gets the same change, and three copies of
the heap leave its 128 MB isolate. Every actor of a type now shares one copy of its module
(each held two), which took the last ~460 KiB. SetMOXZI_BASELINE_VERIFY=1to have each image checked
against a full scan. On Cloudflare,--compact-rtskeeps the wasm memory’s length near what
the heap uses. -
i<22is now a syntax error, as in moc. Motoko reads</>as a comparison only with
whitespace on both sides; otherwise it is a type-argument bracket, and moc rejectsi<22,
d>0,i <22with M0001. moxzi accepted all of them – a program that built with moxzi
could fail with moc. Now both agree (i <= 22andi >= 22are unaffected). Found by
comparing theactor*fork against our fixtures; pinned inscripts/diagnostics_ours_gate.sh.
If your code has an unspaced comparison, add the spaces.mo:local-actors0.1.1 carries the
same fix forJobs/JobQueue. -
Forward references now rejected where moc rejects them (M0055). A class field without a
type annotation that reads a sibling value of the enclosing actor or module (var v = seed(i),let w = Worker()) was accepted by moxzi and rejected by moc; and an unannotated
letin a block that reads something declared later reported M0016 instead of moc’s M0055.
Both now match moc. Fix: annotate the field (var v : Nat = seed(i)). Also: after a type
error moxzi no longer adds use-before-define errors (M0016) that moc never reaches. One gap
remains, listed inscripts/diagnostics_ours_gate.sh: a field reading an annotated sibling
is still accepted in some cases moc rejects. -
Every host measured against its own native actor, and much faster. The host gate times
calls end to end (MOXZI_LATENCY=N) beside a durable counter written each platform’s own
way. Restate went from 59 ms to 6.6 ms per update, Temporal from 100 ms to 8.9 ms, Orleans
from 4.8 ms to 2.4 ms, Proto.Actor from 3.4 ms to 2.0 ms; queries on the Postgres-backed
hosts are now faster than the native actor’s durable write. Temporal timers no longer run
seconds late after a crash.