moxzi
Docs / Server / Authentication

Authenticationalpha

Protect moxzid with a bearer token, and what is and isn't covered by it.

moxzid uses a single bearer token. Every request except health checks, the version, the inspector page and website traffic must carry it. The token protects the server from strangers; it doesn't identify users.

Set a token#

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

--auth-token TOKEN does the same as MOXZID_TOKEN; the option wins if both are set. The token is read once at startup. To change it, restart the server.

Clients send it in the Authorization header:

curl -s -w '\n' -H "Authorization: Bearer $MOXZID_TOKEN" \
  -d '["ada"]' http://127.0.0.1:7000/call-json/greeter/greet

On a loopback address (127.0.0.1, ::1, localhost) the token is optional. On any other address moxzid won't start without one:

moxzid -m actors.json -l 0.0.0.0:7001
Error: refusing to bind 0.0.0.0:7001 without authentication: anyone who can reach this port could install and run arbitrary wasm.
  Fix: pass --auth-token <t> (or set MOXZID_TOKEN), or bind 127.0.0.1, or state the decision with --insecure.

--insecure starts it anyway. Only use that on a network you control.

What needs the token#

Open to anyoneNeeds the token
GET /health, GET /health/ready, GET /versionCalls and queries: /call, /query, /call-json, /query-json
GET /inspector, and GET / without --web (the page itself; its requests carry the token)Managing actors: /install, /upgrade, /cycles
Website routes: /site/ACTOR/…, and every other path when --web is setObserving: /actors, /inspect, /logs, /events, /history, /metrics, /openapi
CORS preflight (OPTIONS) on non-website paths/mops/closure

A request without a valid token gets 401 missing or wrong bearer token. The token is checked before the request body is read.

Open paths must match exactly: with a token set, /health?x=1 needs the token.

Website routes are public#

Website routes behave like a canister's website on the IC: anyone can request them. GET requests reach the actor's http_request method as a query, which can't change state. An actor changes state only if it answers upgrade = ?true and handles the request again in http_request_update. The Authorization header is never passed to the actor.

Public actor and website request bodies are limited to 8 MiB. Authenticated control, image and actor-service requests can be up to 192 MiB, which covers the 128 MiB stored-image ceiling after JSON/base64 expansion.

Callers aren't authenticated#

The token says a request is allowed, not who sent it. Calls arrive from the anonymous principal, or from whatever principal the x-caller header claims. Anyone with the token can claim any principal, so msg.caller checks in a hosted actor don't protect anything.

If your actors need to know who's calling, authenticate users in front of moxzid (in your application or a proxy) and pass the verified principal in x-caller.

Keep the token safe#

Anyone with the token can install and run arbitrary code on the server, and read every actor's logs and recent messages.

DoWhy
Put a TLS proxy in front of any non-local servermoxzid doesn't do TLS, so the token would cross the network in plain text
Keep the token out of web pages you don't controlIt allows installs as well as calls
Don't print secrets from actors/logs and /history return them to anyone with the token
Point --project only at directories you'd publish/mops/closure serves files from it

Limitations#

LimitationWhat to do
One token; no read-only or per-user tokensPut a proxy in front that enforces finer rules
No IP allowlists or rate limitsConfigure them in the proxy
Changing the token needs a restartRestart with SIGTERM; state is kept
On this pageSet a tokenWhat needs the tokenWebsite routes are publicCallers aren&#x27;t authenticatedKeep the token safeLimitations