Running a serveralpha
How do I start moxzid, install an actor, and point a client at it?
Starting moxzid takes a manifest naming the actors to host and, for anything you want to survive a restart, a state directory. The constraint to internalise first: the listener binds before recovery runs, so /health answers while the server is still starting — wait on GET /health/ready, which returns ready only once the manifest is installed and every journal replayed.
The manifest#
actors.json maps names to compiled modules. An id is optional; without one the address is derived from the host seed and the actor's name, which is stable across restarts but changes if you rename the actor.
{ "actors": [
{ "name": "counter", "wasm": "counter.wasm" },
{ "name": "front", "wasm": "front.wasm", "id": "00000000000000000a01" }
] }
Relative wasm paths resolve against the manifest's own directory. A module that fails to load is reported and skipped, not fatal — one bad actor does not stop the others from serving. A manifest with no actors starts and serves empty; a manifest whose every actor was rejected is an error.
Build, start, call#
This is a real transcript against moxzi 0.1.0-alpha.1:
cat > counter.mo <<'MO'
persistent actor {
var n : Nat = 0;
public func add(k : Nat) : async Nat { n += k; n };
public query func peek() : async Nat { n };
};
MO
moxzi build counter.mo -o counter.wasm
# counter.mo -> counter.wasm + counter.did (222424 bytes, 1 file(s), 58 steps, 0.78s)
echo '{"actors":[{"name":"counter","wasm":"counter.wasm"}]}' > actors.json
moxzid -m actors.json -s ./state -l 127.0.0.1:7411 --auth-token sekrit &
# WAIT ON READY, not health
until [ "$(curl -sf http://127.0.0.1:7411/health/ready)" = ready ]; do sleep 0.25; done
curl -s -H 'Authorization: Bearer sekrit' http://127.0.0.1:7411/actors
# [{"name":"counter","id":"9c1329bdc670cfa16f66e0afb34959c00481d47b02f1a7143b62a29103"}]
Request bodies are raw Candid bytes. The empty argument is the six bytes DIDL\x00\x00 — never an empty body. Write the arguments to files and use curl --data-binary @file; shell $'...' quoting silently truncates the NUL bytes.
printf 'DIDL\x00\x01\x7d\x05' > nat5 # (5 : nat)
printf 'DIDL\x00\x00' > unit # ()
curl -s -H 'Authorization: Bearer sekrit' --data-binary @nat5 \
http://127.0.0.1:7411/call/counter/add | xxd -p
# 4449444c00017d05 -> (5 : nat)
curl -s -H 'Authorization: Bearer sekrit' --data-binary @unit \
http://127.0.0.1:7411/query/counter/peek | xxd -p
# 4449444c00017d05
<actor> is a manifest name or a hex principal, everywhere it appears in a path. Actors created at runtime have no manifest name, so the hex id is their name.
Deploying to a running server#
POST /install/<name> with the module as the body creates a new actor and answers with its hex id. It is refused if the name is already installed — that would discard state, and replacing code while keeping state has its own verb.
id=$(curl -fsS -H "Authorization: Bearer $T" --data-binary @agent.wasm \
http://127.0.0.1:7000/install/agent)
curl -fsS -H "Authorization: Bearer $T" --data-binary @agent-v2.wasm \
http://127.0.0.1:7000/upgrade/agent # keeps state and address
POST /upgrade/<name> with an empty body re-reads the wasm from the path the manifest gave — writing a new build over that file and posting is a deploy. An actor created at runtime has no recorded path, so its upgrade must carry the module in the body. If the new module's stable-types signature differs from the running one, moxzid prints a loud WARNING (and, with --log-json, a stable-types-changed event) and proceeds: on the IC the replica would refuse, here the operator is trusted and told.
Pointing a client at it#
There is no agent protocol to speak, so a client is whatever encodes Candid and posts bytes.
| Client | How |
|---|---|
curl | --data-binary @file against /call/<actor>/<method>; the reply body is Candid |
| JavaScript | encode with @dfinity/candid using the idlFactory dfx generate wrote, fetch the bytes, decode the reply. @dfinity/agent's Actor will not work — it polls for a certificate that cannot exist off-chain |
| A browser | GET / is the inspector dashboard, baked into the binary: it reads each actor's Candid interface out of its own module and generates callable forms. Paste the token into its token box |
| A browser, as a website | --web <actor> puts that actor's http_request interface at the root, so absolute links work and the inspector moves to /inspector. Any actor is reachable at /site/<actor>/<path> regardless |
moxzid answers CORS preflights and sets Access-Control-Allow-Origin: * on every reply, so a page served from anywhere can call it — the bearer token is still what authorizes the request.
The flags that matter on day one#
| Flag | Default | Why you would change it |
|---|---|---|
-m, --manifest <FILE> | actors.json | where the actor list lives |
-l, --listen <ADDR> | 127.0.0.1:7000 | a non-loopback bind requires a token or --insecure |
-s, --state <DIR> | none (in-memory) | pass it for anything real — without it everything dies with the process |
--auth-token <TOKEN> | none (MOXZID_TOKEN also read) | required off loopback |
--web <ACTOR> | off | serve that actor as the site at / |
--project <DIR> | off | enables GET /mops/closure/<entry> for a browser forge |
--verbose | off | forward each actor's debug output to the server's stderr |
--no-outcalls | off | the hosted actors cannot reach the network |
--log-json | off | one JSON line per event on stderr, for log shippers |
--instruction-limit, --cycles, --snapshot-below, --heartbeat-ms, --timer-ms and --history are covered under operations and persistence.
Next#
- The HTTP API — the full endpoint table with request and response shapes.
- Authentication — which routes are open and what must never be exposed.
- Operating moxzid — systemd, TLS, metrics, logs, and shutdown.