In process: the platform owns the imagealpha
How does a Motoko actor run inside my platform's own process, and what does it cost?
In-process hosting runs a Motoko actor inside the platform's own process through moxzi-rts, a library, while the platform keeps the actor's durable image in its own store. It is the alpha-7 shape on all nine platforms. The separate moxzi-actor-host service each host page describes first still works; this is the faster form, and the one the numbers below measure.
Who owns what#
| Concern | Owner |
|---|---|
| One turn at a time per key, placement, routing | the platform, as for its own actors |
| The actor's image (4 KiB pages, up to 32 dedup slots, a small metadata record) | the platform's store: Postgres, Dapr's state store, Restate state, a keyvalue bucket, Durable Object storage |
| Executing Motoko, rollback on a trap, parked awaits, upgrades | moxzi-rts, in the platform's process |
| Durable timers | the platform's own (reminders, alarms, delayed sends) |
Nothing is written to disk by moxzi. After a committing turn the runtime hands back a delta — the changed pages and the new metadata — and the platform commits it atomically, only if its stored image version is the one the delta extends. If it cannot commit, it evicts the actor; the next turn reactivates it from what the store holds.
The contract, as a function call#
moxzi-rts answers the actor-host contract version 3 (docs/host-abi.md in the repository) without HTTP. Its C interface is moxzi/rts/include/moxzi_rts.h:
int moxzi_rts_new(ns, ns_len, dep, dep_len, &rts);
int moxzi_rts_add_type(rts, name, name_len, wasm, wasm_len);
int moxzi_rts_learn(rts, key, key_len);
int moxzi_rts_call(rts, method, method_len, url, url_len, body, body_len, &status, &out, &out_len);
void moxzi_rts_free_buf(out, out_len);
moxzi_rts_call("POST", "/v3/begin", body) returns what POST /v3/begin returns over HTTP. Every adapter drives a turn the same way:
| Step | What the platform does |
|---|---|
| 1 | send the turn with expectVersion, the image version its store holds |
| 2 | on 409 {notActive}, send the same turn again with activate: {image} from the store |
| 3 | on 409 {needModule: <hash>}, read the module stored under that hash and send the turn once more with activate: {image, module} |
| 4 | commit the returned delta only if the stored version is delta.baseVersion; if that fails, call /v3/evict and fail the turn |
The runtime usually has the module already — it hosts the type, or saw it earlier — so step 3 is rare, and a 380 KB module is neither read from the store nor sent on an ordinary activation. Store meta exactly as it arrives: meta.patches carries byte-level patches for pages whose stored copy is out of date, and only the runtime reads it. Store a delta's dedup chunks the way you store pages (overwrite by index) and return them as the image's dedup: they are the record that answers a retried turn exactly once, and an activation without them is refused.
Bindings#
| Platform | Binding | Build the in-process host (from the named directory) |
|---|---|---|
| Restate | Rust, linked | moxzi/: cargo build --release -p moxzi-restate --bin moxzi-restate-host |
| Temporal | cgo, turns as local activities | moxzi/adapters/temporal/: go build -tags inproc -o moxzi-temporal-host . |
| Dapr | cgo, inside the Dapr app | moxzi/adapters/dapr/host/: go build -tags inproc -o moxzi-daprhost . |
| Proto.Actor | cgo, in the cluster member | moxzi/adapters/protoactor/: go build -tags inproc -o moxzi-protoactor . |
| Orleans | P/Invoke, in the silo | moxzi/adapters/orleans/: dotnet publish -c Release -o out/orleans |
| Akka (Pekko) | JNA, in the entity | moxzi/adapters/pekko/: bash build.sh <out> |
| Ray | ctypes, inside each key's Ray actor | moxzi/adapters/ray/: nothing to build; set MOXZI_RTS_LIB to the library |
| wasmCloud | a wash-runtime host with a moxzi plugin | moxzi/adapters/wasmcloud/: cargo build --workspace --target wasm32-wasip2 --release --features in_process && (cd host && cargo build --release) |
| Cloudflare | the contract engine inside each Durable Object | moxzi/adapters/cloudflare/: bash build-native.sh <modules-dir> |
The cgo, P/Invoke, JNA and ctypes bindings load libmoxzi_rts from moxzi/target/release: build it first with cargo build --release -p moxzi-rts in moxzi/. Those hosts and Restate select the in-process form with MOXZI_IN_PROCESS=1; wasmCloud's and Cloudflare's in-process hosts are separate builds. The launch files the conformance harness uses, moxzi/conformance/hosts/<platform>-inproc.json, hold the exact commands and environment.
From a release, without Rust#
Each release carries the runtime prebuilt, in bundles that unpack into the same directory as the toolchain tarball (moxzi-<version>-<target>/):
| Bundle | Contents |
|---|---|
moxzi-sdk-<version>-<target>.tar.gz | lib/libmoxzi_rts.so (Linux) or lib/libmoxzi_rts.dylib (macOS), include/moxzi_rts.h, examples/rts_hello.c, and the adapter sources moxzi host new copies |
moxzi-hosts-<version>-<target>.tar.gz | the prebuilt Restate, wasmCloud and Cloudflare hosts |
moxzi-rts-static-<version>-<target>.tar.xz | lib/libmoxzi_rts.a, for linking statically (large: it is all of wasmtime). Published in parts; join them with cat moxzi-rts-static-<version>-<target>.tar.xz.part-* > moxzi-rts-static-<version>-<target>.tar.xz, then check it against SHA256SUMS |
tar -xzf moxzi-<version>-<target>.tar.gz # the toolchain
tar -xzf moxzi-sdk-<version>-<target>.tar.gz # + runtime and adapters
tar -xzf moxzi-hosts-<version>-<target>.tar.gz # + prebuilt hosts (Restate, wasmCloud, Cloudflare)
The release is refused unless the example compiles against the staged library and runs an actor, so it is known to work:
cc examples/rts_hello.c -I include -L lib -lmoxzi_rts -Wl,-rpath,lib -o rts_hello
./moxzi build hello.mo -o hello.wasm
./rts_hello hello.wasm
# status 200; the answer is … bytes of JSON, most of it the delta to commit
# "response":{"candid":"…","result":["Hello, world!"],"status":"replied"},…
| Binding | Using the release's library |
|---|---|
| C, or any language with a C FFI | link lib/ as above |
| Orleans, Pekko | set MOXZI_RTS_LIB_DIR to the tarball's lib/ |
| Ray | set MOXZI_RTS_LIB to lib/libmoxzi_rts.so (or .dylib) |
| Go (cgo), Rust | build from the repository today (cargo build --release -p moxzi-rts); linking them against the tarball is not yet tested |
What it costs#
Measured on the conformance fixture customer.mo, in process (docs/actor-cost-report.md):
| Cost | Value |
|---|---|
| Resident memory per active actor | ~105 KiB (Linux), ~112 KiB (macOS) |
| Actors in one process | 100,000 in 13.9 GB |
| Activation from a stored image, to first reply | ~0.4 ms |
| Bytes a small turn stores | ~1.4 KB, averaged over 2,000 turns |
| Update latency against the platform's own durable actor | 1.4–1.7x on Linux (Proto.Actor) |
scripts/actor_cost_ratchet.sh holds these on the release board.
Checking a host#
| Tool | What it proves |
|---|---|
HOST=<platform>-inproc scripts/host_matrix_gate.sh | all 16 conformance scenarios against the real platform |
MOXZI_BASELINE_VERIFY=1 | every image is checked against a full scan, and every rollback against live memory; the matrix gate sets it by default |
scripts/host_latency_ratchet.sh | update latency as a ratio to the platform's own actor, per platform |
Things you cannot do#
- Share one runtime between processes. The runtime lives and dies with the host process; after a restart the host adds its modules and keys again (
add_type,learn). - Skip the per-key guarantee. Two turns for one key at once are the platform's bug to prevent, as for its own actors.
- Read the image yourself. Pages are DEFLATE-compressed and
meta.patchesmust be applied; treat the image as opaque bytes you store and hand back. - Rely on parallelism under
wrangler dev. It runs every Durable Object in one isolate; Actor Parallelism is proven on the other eight platforms, not locally on Cloudflare. For Cloudflare, compile with--compact-rts: 2 MB GC partitions keep the wasm memory's length near what the heap uses, which matters inside a 128 MB isolate.
Next#
- /docs/hosts/overview/ — the nine platforms and how the matrix is run.
- /docs/hosts/restate/ — one platform end to end.
- /docs/server/overview/ —
moxzid, when you want no platform at all.