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": [
{ "name": "greeter", "wasm": "greeter.wasm" }
]
}
| Field | Required | Meaning |
|---|---|---|
name | Yes | The name used in URLs, such as /call-json/greeter/greet |
wasm | Yes | Path to the module, relative to the manifest |
id | No | A 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#
| Option | Default | Effect |
|---|---|---|
-m, --manifest FILE | actors.json | The manifest |
-l, --listen ADDR | 127.0.0.1:7000 | Address and port to listen on |
-s, --state DIR | none | Where to store actor state. Without it, state is in memory only |
--auth-token TOKEN | $MOXZID_TOKEN | Required on every endpoint except health checks, /version and website routes |
--web ACTOR | Serve this actor's http_request at / | |
--verbose | Print actors' Debug.print output to the server's stderr | |
--log-json | Log one JSON object per event instead of text | |
--no-outcalls | Actors 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:
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:
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]
| Field | Meaning |
|---|---|
persistent_heap | Whether the stable variables changed, and whether the change was checked |
candid_interface.added, removed, changed | Public methods that differ from the previous version |
candid_interface.compatible | true 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:
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#
- Calling actors: arguments, errors and Candid.
- Persistence.
- Operations: running
moxzidas a service.