Actor platforms overviewalpha
Run Motoko actors inside Dapr, Restate, Orleans, Akka, Proto.Actor, Cloudflare, Temporal, Ray or wasmCloud.
A compiled Motoko actor can run inside an actor platform you already operate. The platform decides where each actor runs, delivers its messages one at a time, and stores its state. moxzi runs the actor's code inside the platform's process, with the same rules as on the Internet Computer: a trap rolls back the message, await waits for another actor's reply, and upgrades keep state.
Start a project#
moxzi host new PLATFORM DIR writes a working project: a Motoko actor, moxzi's adapter for the platform, and a run.sh that builds both, starts what the platform needs, and calls the actor.
moxzi host new wasmcloud hello
host new: wasmcloud project (counter example) in ./hello — cd there and ./run.sh
cd hello
ONCE=1 ./run.sh
actor.mo -> actor.wasm + actor.did (242743 bytes, 1 file(s), 50 steps, 0.50s)
Counter/1.inc() -> [1]
Run it again, and the same actor answers 2: a new host process got the state back from the platform's store.
ONCE=1 ./run.sh
actor.mo -> actor.wasm + actor.did (242743 bytes, 1 file(s), 50 steps, 0.50s)
Counter/1.inc() -> [2]
Without ONCE=1, run.sh keeps the host running so you can call it:
curl -X POST localhost:8090/actors/Counter/1/method/inc \
-H 'content-type: application/json' -d '[]'
Platforms#
| Platform | host new name | You need | Actor state is stored in |
|---|---|---|---|
| wasmCloud | wasmcloud | Nothing else | A wasi:keyvalue bucket on disk |
| Proto.Actor | protoactor | Go, Docker | Postgres |
| Dapr | dapr | Go, the Dapr CLI after dapr init | Dapr's state store |
| Restate | restate | Docker | Restate's state |
| Temporal | temporal | Go, the Temporal CLI, Docker | Postgres |
| Cloudflare Durable Objects | cloudflare | Node.js | Durable Object storage |
| Orleans | orleans | .NET 8 SDK, Docker | Postgres |
| Akka (Apache Pekko) | akka | A JDK, coursier (cs), Docker | Postgres |
| Ray | ray | Python 3 with the project's host/requirements.txt, Docker | Postgres |
moxzi host new needs the release's SDK and hosts bundles, unpacked into the same directory as moxzi:
tar -xzf moxzi-0.1.0-alpha.7-aarch64-apple-darwin.tar.gz
tar -xzf moxzi-sdk-0.1.0-alpha.7-aarch64-apple-darwin.tar.gz
tar -xzf moxzi-hosts-0.1.0-alpha.7-aarch64-apple-darwin.tar.gz
Download them from https://moxzi.ai/releases/v0.1.0-alpha.7/. The install script installs only the compiler and server.
Examples#
--example NAME writes a project that shows more than a counter. moxzi host examples lists them.
ledger#
Accounts that deposit, withdraw and transfer:
moxzi host new wasmcloud shop --example ledger
cd shop
ONCE=1 ./run.sh
host new: wasmcloud project (ledger example) in ./shop — cd there and ./run.sh
account.mo -> account.wasm + account.did (259677 bytes, 1 file(s), 58 steps, 0.53s)
Account/alice.deposit(100), invocation dep-1 -> [100]
(the host process was killed and started again)
retry dep-1 after a host restart -> [100]
Account/alice.withdraw(500), an overdraft -> trapped
Account/alice.balance() after the trap -> [100]
Account/alice.transfer("Account/bob", 30) -> [70]
Account/bob.balance() -> [30]
A deposit retried with the same invocation id (the x-moxzi-invocation-id header) is applied once, even after the host restarts. The overdraft traps, and the whole message is rolled back. The transfer awaits the other account's reply.
saga#
A trip that books a hotel and a flight, and releases the hotel if the flight is full:
moxzi host new wasmcloud trip --example saga
cd trip
ONCE=1 ./run.sh
host new: wasmcloud project (saga example) in ./trip — cd there and ./run.sh
trip.mo -> trip.wasm + trip.did (266077 bytes, 1 file(s), 58 steps, 0.52s)
hotel.mo -> hotel.wasm + hotel.did (243946 bytes, 1 file(s), 51 steps, 0.49s)
flight.mo -> flight.wasm + flight.did (243946 bytes, 1 file(s), 51 steps, 0.48s)
Trip/t1.book(h1, f1), h1 held -> parked on call moxzi-call-v1.eyJuYW1lc3BhY2UiOiJ0cmlwIiwiZGVwbG95bWVudCI6ImRldiIsImFjdG9yX3R5cGUiOiJUcmlwIiwiYWN0b3Jfa2V5IjoidDEifQ.60954ddb3c8ea7be3946a85453dcf8ec.0000000000000001.await
(the host process was killed and started again)
Trip/t1.state() after a host restart -> ["booking"]
deliver the held call to Hotel/h1 -> ["booked"]
Trip/t2.book(h2, f1): the flight is full -> ["no seat (hotel released)"]
Hotel/h2.rooms() after the compensation -> [2]
The booking waits on the hotel's reply while the host process is killed and restarted, then finishes when the reply arrives.
Calling an actor#
Each actor has a key, Type/id, such as Counter/1 or Account/alice. The platform creates the actor the first time its key is called.
| Request | Effect |
|---|---|
POST /actors/TYPE/ID/method/METHOD with a JSON array body | Call a method. The reply is a JSON array of results |
Header x-moxzi-invocation-id: ID | Make a retry safe: a call with an id that already ran returns the recorded result |
An actor finds another actor by key through the host's management canister:
transient let host : actor { moxzi_actor_of : ({ key : Text }) -> async Principal } = actor "aaaaa-aa";
public func payBob(amount : Nat) : async Nat {
let bob : actor { credit : (Nat) -> async Nat } =
actor (debug_show (await host.moxzi_actor_of({ key = "Account/bob" })));
await bob.credit(amount)
};
moxzi_actor_of exists only on actor platforms, not on the Internet Computer.
Next steps#
- How it works: what the platform and moxzi each do, and the library behind every adapter.
- The page for your platform.