Message passing
Processes in RUSM share nothing — separate memories, separate permissions. So they communicate the only safe way: by copying bytes through the host. Each process owns a mailbox (an async channel); sending is fire-and-forget, and receiving suspends the receiver until a message arrives. It's Erlang's model, made airtight by Wasm's memory boundary.
The flow of a message
- The sender builds a message in its own linear memory and calls the host
sendop (send: func(to: pid, message: list<u8>)in therusm:runtimeactor interface; raw core modules call the equivalentrusm.send). - The host copies those bytes out of the sender's memory and pushes them onto the target's mailbox (a Tokio channel). No memory is ever shared.
- The target calls
receive(receive: func() -> list<u8>), which awaits the mailbox; the host copies the bytes into the target's memory.
Why copy, not share
Sharing memory between instances would break isolation — the whole point of the model. A shared buffer means one process's bug, or one permission boundary, can reach into another's memory. Copying keeps every crash and every capability boundary strictly local.
The messages themselves are ordinary serialized data: the rusm-rs, rusm-ts, and rusm-go guest SDKs all send serde JSON over one shared wire, so a Rust, a TypeScript, and a Go guest can message each other transparently.
Receiving suspends — it never spins
receive() is an async host call. An empty mailbox parks the Tokio task (see fibers & blocking→async) rather than busy-waiting, so a million processes all blocked on receive cost almost nothing — they're just idle tasks. That's exactly what makes "a process per request, per connection, per unit of work" affordable.
Shipped in Phase 2. (Opt-in mailbox depth surfaces in the observer snapshot.)