moxzi
Docs / Server / Running moxzid

Running moxzidalpha

Start moxzid with a manifest, protect it with a token, install and upgrade actors while it runs, and serve a website from an actor.

This page covers starting moxzid, managing actors on a running server, and serving an actor as a website. For a first walkthrough, see Your first actor.

The manifest#

moxzid reads a JSON manifest that names each actor and its wasm module. The default file name is actors.json; pass another with -m.

actors.json
{
  "actors": [
    { "name": "greeter", "wasm": "greeter.wasm" }
  ]
}
FieldRequiredMeaning
nameYesThe name used in URLs, such as /call-json/greeter/greet
wasmYesPath to the module, relative to the manifest
idNoA fixed actor id in hex. Without it, the id is derived from the server's seed and the name, and stays the same across restarts

If one module fails to load, moxzid prints REJECTED with the reason and starts the others. If every module fails, it exits.

Start the server#

export MOXZID_TOKEN=$(openssl rand -hex 32)
moxzid -m actors.json -s ./state

-s ./state stores actor state on disk. Without it, everything is in memory and lost when the server stops.

MOXZID_TOKEN (or --auth-token) sets a bearer token that every request except health checks and website traffic must carry. On 127.0.0.1 the token is optional. On any other address moxzid refuses to start without one, unless you pass --insecure.

moxzid starts listening before it has finished loading actors. Wait for /health/ready before sending requests:

until [ "$(curl -s http://127.0.0.1:7000/health/ready)" = ready ]; do sleep 0.2; done
echo ready
ready

Requests without the token are refused:

curl -s -w '\n' -d '["ada"]' http://127.0.0.1:7000/call-json/greeter/greet
missing or wrong bearer token
curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" \
  -d '["ada"]' http://127.0.0.1:7000/call-json/greeter/greet
["Hello, ada!"]

Common options#

OptionDefaultEffect
-m, --manifest FILEactors.jsonThe manifest
-l, --listen ADDR127.0.0.1:7000Address and port to listen on
-s, --state DIRnoneWhere to store actor state. Without it, state is in memory only
--auth-token TOKEN$MOXZID_TOKENRequired on every endpoint except health checks, /version and website routes
--web ACTORServe this actor's http_request at /
--verbosePrint actors' Debug.print output to the server's stderr
--log-jsonLog one JSON object per event instead of text
--no-outcallsActors can't make HTTPS requests

moxzid reference lists every option.

Read an actor's output#

Debug.print output is buffered per actor. /logs/ACTOR returns it and clears it:

greeter.mo
import Debug "mo:core/Debug";

actor {
  var greeted : Nat = 0;

  public func greet(name : Text) : async Text {
    greeted += 1;
    Debug.print("greeting " # name);
    "Hello, " # name # "!"
  };
};
curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" http://127.0.0.1:7000/logs/greeter
greeting ada

/events/ACTOR streams the same output as server-sent events, and --verbose prints it on the server's stderr.

Install an actor while the server runs#

POST /install/NAME with a module as the body creates a new actor and replies with its id:

id=$(curl -s -H "Authorization: Bearer $MOXZID_TOKEN" \
  --data-binary @greeter.wasm http://127.0.0.1:7000/install/welcome)
echo $id
curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" \
  -d '["grace"]' http://127.0.0.1:7000/call-json/$id/greet
cbdd2cfd30d27f8d12a3beb0660ee46dba7af6b8d1362b4515042fdc03
["Hello, grace!"]

Address the new actor by the id in the reply. Installing again under the same name is refused, because it would discard the existing actor's state; use /upgrade to change its code.

Actors installed this way are saved in the state directory and come back after a restart, like those in the manifest.

Upgrade an actor#

POST /upgrade/ACTOR replaces an actor's code and keeps its state. Add ?report=1 to see what changed:

greeter2.mo
import Debug "mo:core/Debug";

actor {
  var greeted : Nat = 0;

  public func greet(name : Text) : async Text {
    greeted += 1;
    Debug.print("greeting " # name);
    "Hi, " # name # "!"
  };

  public query func stats() : async Nat {
    greeted
  };
};
curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" \
  --data-binary @greeter2.wasm 'http://127.0.0.1:7000/upgrade/greeter?report=1'
{
  "actor": "a0262c046d055da214312e6cd04dc0d79134f3ac548b25c67e941d4003",
  "candid_interface": {
    "added": [
      "stats"
    ],
    "changed": [],
    "compatible": true,
    "reason": null,
    "removed": [],
    "subtyping_checked": true
  },
  "persistent_heap": "unchanged (stable-types signature identical)",
  "upgrade": "done"
}
curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" \
  -d '[]' http://127.0.0.1:7000/query-json/greeter/stats
[1]
FieldMeaning
persistent_heapWhether the stable variables changed, and whether the change was checked
candid_interface.added, removed, changedPublic methods that differ from the previous version
candid_interface.compatibletrue if every client of the old interface can still call the new one. Reported, not enforced

An upgrade that would lose stable data is refused with the reason. See Migrations. To check an upgrade that changes the stable variables, moxzid runs moxzi stable-compatible, so moxzi must be next to moxzid, on your PATH, or named by MOXZI_BIN. If it can't be found, such upgrades are refused; --upgrade-policy warn lets them proceed with a warning instead.

An empty body re-reads the module from the path in the manifest, so you can rebuild in place and upgrade with curl -X POST …/upgrade/ACTOR.

Serving a website#

An actor that implements the HTTP gateway interface (http_request, and optionally http_request_update) can serve web pages:

site.mo
import Nat "mo:core/Nat";
import Text "mo:core/Text";

actor {
  type Header = (Text, Text);

  type Request = {
    method : Text;
    url : Text;
    headers : [Header];
    body : Blob;
  };

  type Response = {
    status_code : Nat16;
    headers : [Header];
    body : Blob;
    upgrade : ?Bool;
  };

  var visits : Nat = 0;

  func page(status : Nat16, body : Text) : Response {
    {
      status_code = status;
      headers = [("Content-Type", "text/html; charset=utf-8")];
      body = Text.encodeUtf8(body);
      upgrade = null;
    }
  };

  // Queries answer GET requests. They can read state but not change it.
  public query func http_request(req : Request) : async Response {
    if (req.method == "POST" and req.url == "/visit") {
      // Ask the gateway to send this request again as an update call.
      return { page(200, "") with upgrade = ?true };
    };
    if (req.url == "/") {
      return page(200, "<h1>Hello from Motoko</h1><p>Visits: " # Nat.toText(visits) # "</p>");
    };
    page(404, "not found")
  };

  // Updates can change state.
  public func http_request_update(req : Request) : async Response {
    visits += 1;
    page(200, "Thanks. Visit number " # Nat.toText(visits))
  };
};
moxzi build site.mo
moxzid -m actors.json -s ./state --web site

--web site puts the site actor at the root of the server:

curl -s -w '\n' http://127.0.0.1:7000/
<h1>Hello from Motoko</h1><p>Visits: 0</p>

GET requests go to http_request, a query, so they can't change state. To handle a request as an update, return upgrade = ?true; moxzid then sends the same request to http_request_update:

curl -s -w '\n' -X POST http://127.0.0.1:7000/visit
curl -s -w '\n' -X POST http://127.0.0.1:7000/visit
curl -s -w '\n' http://127.0.0.1:7000/
Thanks. Visit number 1
Thanks. Visit number 2
<h1>Hello from Motoko</h1><p>Visits: 2</p>

The actor sets the status code:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:7000/missing
404

Every actor's website is also available at /site/ACTOR/PATH, with or without --web:

curl -s -w '\n' http://127.0.0.1:7000/site/site/
<h1>Hello from Motoko</h1><p>Visits: 2</p>

Website routes don't need the token: they're public, like a canister's website on the IC. All HTTP methods are passed through, and the Authorization header is not forwarded to the actor.

The inspector#

Open http://127.0.0.1:7000/ in a browser (or /inspector when --web is set) to see every actor, its methods and its recent messages, and to call methods from a form. Paste the token into the token box if you set one.

Next steps#

On this pageThe manifestStart the serverCommon optionsRead an actor&#x27;s outputInstall an actor while the server runsUpgrade an actorServing a websiteThe inspectorNext steps