What moxzid isalpha
What is the server for, and how is it different from a local dfx replica?
moxzid is a single-process HTTP server that hosts compiled Motoko actors off-chain with Internet Computer execution semantics: one message at a time per actor, traps that roll back, upgrades that keep state, heartbeats and timers, cycles as a real budget, actor-to-actor calls, HTTPS outcalls, and durability across a kill -9. The constraint that shapes everything else is that in alpha it is one node — one process, one queue per actor, round-robin fairness — so horizontal scale means running more moxzids, and nothing coordinates them.
What it is for#
The same wasm you would deploy to a canister, running somewhere you control. That covers three jobs: developing against IC semantics without a replica, hosting actors as an ordinary long-lived service, and serving an actor's http_request interface as a real website. The runtime underneath is the same moxzi-runtime crate the moxzi CLI uses, so what you test locally and what the server executes are one code path.
An actor reaches moxzid as a compiled module named in a manifest, or posted to POST /install/<name> at runtime, or created by another actor. All three are addressable and all three are durable.
How it differs from a local replica#
A local replica (dfx start) emulates the IC's protocol as well as its execution. moxzid emulates the execution and replaces the protocol with plain HTTP.
| moxzid | Local IC replica | |
|---|---|---|
| Wire protocol | HTTP/1.1, raw Candid request/response bodies (POST /call/<actor>/<method>) | the IC agent protocol (CBOR envelopes under /api/v2) |
| Caller identity | always the anonymous principal 0x04; nothing is signed | the identity the agent signs with |
| Certification | none. ic0.data_certificate_present returns 0 | subnet certificates, verifiable by an agent |
| Addresses | derived per host: SHA-224(seed ‖ 0x00 ‖ name) with the derived tag 0x03 | canister ids assigned by the replica, opaque tag 0x01 |
| Consensus / replication | none — a message runs once, on one thread | the replica's own machinery |
| Durability | snapshot + write-ahead log under -s <dir>; back it up with tar | the replica's state directory |
Verbs reaching http_request | every verb, including PROPFIND, MKCOL, MOVE, LOCK | boundary-node dependent; mainnet's edge rejects the DAV verbs |
| Instruction limit | --instruction-limit, enforced with wasmtime fuel | the replica's per-message limit |
The address tag is a deliberate choice, not a detail: off-chain actors are tagged derived precisely so one can never be mistaken for — or collide with — a real canister id, which is the space the IC allocates from.
What it keeps from the IC#
Everything an actor can observe about how it is executed.
| Property | How it shows up |
|---|---|
| One message at a time | a single actor thread; a message runs to completion, never preempted |
| Trap rollback | a trapping message loses its writes and the actor keeps serving; the caller gets 400 |
| Query rollback | POST /query/... copies the heap before the message and restores it after |
| Commit points | await commits, and re-entrancy (A→B→A) is delivered and observable |
| Upgrades | pre_upgrade → memory persists → the new module's start runs over it → post_upgrade |
| Cycles | charged per message: 590_000 plus 4 cycles per 10 instructions; 402 when the allowance is dry |
| Instruction limit | exhaustion traps and rolls back — including in canister_init and the upgrade hooks |
| Heartbeats and timers | separate mechanisms with separate flags; --heartbeat-ms 0 does not disable Timer.setTimer |
| HTTPS outcalls | the IC's own http_request management interface, transform included, responses journalled for replay |
Fair scheduling is moxzid's own addition rather than an IC property: each actor has its own queue and they are served round-robin, so an actor's wait depends on how many other actors are busy, not on the length of somebody else's backlog.
Things you cannot do#
| Not possible | Why |
|---|---|
Point @dfinity/agent's Actor at moxzid | it submits an update and polls read_state for a certificate. There is no subnet to sign one, and a forged certificate would be verified and believed — worse than none. Use the generated idlFactory and the Candid bodies directly |
| Authenticate a caller | every ingress message arrives as the anonymous principal 0x04. msg.caller cannot distinguish two clients; the bearer token authorizes the request, not a principal |
Use certified-data patterns | data_certificate_present/_size/_copy are wired to report nothing |
| Cluster, replicate, or share a state directory between two servers | there is no coordination layer. Two moxzids over one directory is undefined |
| Terminate TLS in the binary | there is no TLS stack in it; a reverse proxy does this |
Follow a streaming_strategy in a gateway response | it is decoded and detected but not followed. moxzid logs a line and serves the first chunk only — off-chain replies have no 3 MB ceiling, so send the response whole |
Run legacy non-persistent actors | the compiler rejects them unless you pass --default-persistent-actors |
| Preempt a running message | not preemptive by design. --instruction-limit is what bounds one message's hold on the thread |
Next#
- Running a server — the manifest, the flags, and a first call end to end.
- The HTTP API — every endpoint, what it takes, and what it returns.
- Persistence and durability — what survives a clean stop, a crash, and
kill -9.