moxzi
Docs / Server / Running a server

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.

ClientHow
curl--data-binary @file against /call/<actor>/<method>; the reply body is Candid
JavaScriptencode 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 browserGET / 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#

FlagDefaultWhy you would change it
-m, --manifest <FILE>actors.jsonwhere the actor list lives
-l, --listen <ADDR>127.0.0.1:7000a 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>offserve that actor as the site at /
--project <DIR>offenables GET /mops/closure/<entry> for a browser forge
--verboseoffforward each actor's debug output to the server's stderr
--no-outcallsoffthe hosted actors cannot reach the network
--log-jsonoffone 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#

On this pageThe manifestBuild, start, callDeploying to a running serverPointing a client at itThe flags that matter on day oneNext