Features
RUSM is Erlang/OTP's concurrency and fault-tolerance model, rebuilt in Rust, where every process can be a sandboxed WebAssembly component — "let it crash" meets "run untrusted code safely," at millions of lightweight processes, with none of the wasmCloud ceremony. The whole design rests on one principle: the actor core (rusm-otp) is Wasm-free; WebAssembly is a pluggable process backend. That's why it's both a great native actor runtime and a great Wasm host.
This page is the map; each item links to the concept that teaches it.
Concurrency & the actor core
- Massive lightweight concurrency (Tokio-driven) — hundreds of thousands of processes over a few OS threads; spawning is near-free (~2.4M/sec measured). → the process model
- Isolated, lightweight processes — one process = one task (and, for Wasm, one isolated instance); no shared mutable state.
- Superior messaging — per-process mailboxes, by-value messages, selective receive, ~21M msgs/sec. → message passing
- Fault tolerance & supervision — links, monitors,
trap_exit,spawn_link,exit, one-for-one / one-for-all / rest-for-one, windowed restart-intensity. → links & supervision - Preemptive fairness — a runaway guest can't starve others; Wasmtime epoch interruption forces it to yield. → epoch preemption
- "Write blocking code, get async" —
receiveparks the fiber and frees the worker. → fibers & blocking→async - Process management — named registry, timers, graceful shutdown, full introspection. → process management
- Backpressure & overload — bounded byte-stream channels; opt-in bounded mailboxes that shed user messages but never system/exit signals.
WebAssembly & safety
- WASM components (WASI p2 + p3) — the modern component model, not just core modules (the headline difference from Lunatic). → components & the actor world
- WASM core modules (wasip1) — also runs raw preview-1 modules via a bridge.
- Default-deny capability sandboxing —
sandboxed/network-client/trustedprofiles + per-spawn overrides; per-instance memory caps; spawn-from-guest never escalates. → permissions & sandboxing - Trap isolation — a guest trap becomes that one process
Crashed; the runtime and its neighbours are untouched. - Durable key-value storage — an embedded, transactional store (
rusm-kv/redb, no daemon) behind the default-denyallow-storagecapability; thekvAPI is the same in Rust and TS. → permissions & sandboxing - Guests in Rust or TypeScript — the
#[service]macro, the concealed typed client, the shared rquickjs runner (tiny TS components vs jco baking an engine into every one) + bytecode precompile, plus nativefetchandcrypto.subtle(SHA/HMAC/AES-GCM) for TS. → guests: Rust & TypeScript
Serving & streaming
- HTTP / WebSocket / SSE serving — from a Rust or TypeScript component, always process-per-unit-of-work: a fresh sandboxed instance per HTTP/SSE request, one sandboxed process per WS connection. No head-of-line blocking, crash containment, and full isolation by construction; cheap on the pooled spawn path (~440k spawns/sec). → the serving model
- Declarative routing — a per-listener
rusm.toml[serve.routes]subtable (one per[[serve]]HTTP/SSE listener, so multiple ports route independently) maps"METHOD /path/:param" = "component#action"(:namepath param, trailing*wildcard); the host gateway resolves it (most-specific wins; 405 on method mismatch, 404 on no match). Rust handlers arepub fns under#[rusm_rs::handlers]— nomain, no router code. - Streaming & async — incremental SSE (a 3-arg
fn(Request, Params, Sse)action streams for the life of the connection), per-connection WS processes, Tokio back-pressure throughout. - Shared state lives elsewhere — never in the ephemeral serving instance: a long-lived
[components.<name>]service (resident = true) reached over the actor API (whereis/call/send), or durablekv. Pairs with thepubsub::Topicsprimitive (keyed fan-out, monitor-based pruning): one publish → every connected client, no subscriber bookkeeping in app code. - Cross-process byte streams — a bounded, back-pressured byte channel between processes. → byte streams
Apps, clusters & DX
- App model —
rusm.tomldescribes components and servers;rusm build(cargo wasm32-wasip2 / Bun, no jco) →./wasm/. → the app model - CLI —
rusm new(scaffold),rusm run,rusm serve,rusm dev(watch + reload),rusm attach(a live REPL into a local or remote node). - Dynamic bundle sourcing — a component's JS can load from a URL or the durable
kvstore (source = "…") instead of a local artifact: deploy JS live, no node rebuild. → configuration - Distributed clustering —
ClusterNode::connect(theNode.connectequivalent), cross-node send, a gossiped global registry, remote spawn, all over QUIC + mutual TLS. → distributed nodes - Live attach — inspect/control a running node's processes live. → live attach
- DX: infra never bothers you — you write application functions; RUSM owns all the infrastructure (spawn/receive/reply/supervise/sockets).
- No funky rules — no execution-time cap, no "this must be a service," no lattice/provider ceremony.
Observability & quality
- Live process statistics — an Erlang-
observer-style view (process count, scheduler load, memory, per-instance table), nearly free. - Live benchmark dashboard — real scenarios streamed to a React/uPlot UI. → benchmark & dashboard
- Fair, out-of-process benchmarking —
rusm-loadtestdrives a realrusm serveport from a separate process, so the numbers are the server's. - TDD, ~100% coverage; OTP first, WASM second — the dependency graph enforces the Wasm-free core. → architecture
Where it's going
The core is audited-solid today; Phase 12 (serving TLS, serve-path admission control, cluster gossip authentication) is explicitly planned. See the roadmap.