Skip to content

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"receive parks 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 sandboxingsandboxed / network-client / trusted profiles + 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-deny allow-storage capability; the kv API 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 native fetch and crypto.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" (:name path param, trailing * wildcard); the host gateway resolves it (most-specific wins; 405 on method mismatch, 404 on no match). Rust handlers are pub fns under #[rusm_rs::handlers] — no main, 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 durable kv. Pairs with the pubsub::Topics primitive (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 modelrusm.toml describes components and servers; rusm build (cargo wasm32-wasip2 / Bun, no jco) → ./wasm/. → the app model
  • CLIrusm 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 kv store (source = "…") instead of a local artifact: deploy JS live, no node rebuild. → configuration
  • Distributed clusteringClusterNode::connect (the Node.connect equivalent), 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 benchmarkingrusm-loadtest drives a real rusm serve port 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.

MIT licensed