Skip to content

Component lifecycles — overview

Every component in RUSM — an HTTP handler, a WebSocket worker, an SSE stream, a per-call worker, a resident service — is the same runtime object: a process. One Wasm instance = one Tokio task, one mailbox, one fiber, one capability set. The lifecycle is therefore uniform; what differs by type is only when a process is spawned and how it ends.

This overview covers what's shared — the two domains, the phases, and the failure vocabulary. Then there's a chapter per component type, each walking that type's events (normal, disconnect, connection error, crash, memory/OOM crash, kill):

(A stock wasi:cli command component is covered briefly at the end.)

The two domains

The lens that matters most is the two domains the lifecycle runs through:

  • Platform domain — what RUSM owns: routing, spawning, the scheduler and fibers, the mailbox, socket ownership, the reply/stream plumbing, capability and memory enforcement, exit detection, links/monitors, and the supervisor. You never write this.
  • Application domain — what you write: the handler action, the service functions, the worker body. Pure logic — no router, no wire, no socket, no async, no lifecycle bookkeeping.

The boundary is deliberate and load-bearing: a failure in the application domain is caught by the platform domain and turned into a status code, a Down message, or a supervisor restart — it is never allowed to take down the listener, a sibling, or the node. Every per-type chapter is organised around this split: "Platform owns / You write," then an events table whose columns are Platform domain and Application domain.

Who owns what, phase by phase

PhasePlatform domain (RUSM)Application domain (you)
Registerresolve a name → a prepared component (compiled module + linked imports)declare [[serve]] / [components.<name>] / [serve.routes]
Spawninstantiate on the pooling allocator + copy-on-write + InstancePre (~440k/s); apply default-deny capabilities + a StoreLimiter memory ceiling; create the mailbox, fiber, and abort handle
Dispatchmatch the route, own the socket, spawn the handler, send the request over the actor wire, run the ephemeral reply responder
Runsuspend/resume the fiber on every blocking call (receive, Stream::read, a service call); enforce epoch preemption on CPU-bound guestsyour action / service / worker body — straight-line, blocking-looking code
Reply / streamturn your Response into the HTTP response; drain your byte stream into the chunked body with back-pressure; own the WS socket sinkreturn a Response, or sse.data(…), or conn.send(…)
Exitclassify the exit (Normal / Crashed / Killed), reclaim the instance + mailbox + streams, fire links and monitors, drive the supervisoroptionally monitor / Supervisor / trap_exit

Blocking is async, for free. In the Run phase your code can call receive or read a stream and block — the platform suspends the fiber and frees the scheduler thread until data arrives, then resumes you. You write sequential code; the platform makes it cooperative.

The shared failure vocabulary

Every component ends in one of these ways. The per-type chapters refer back to these definitions rather than repeat them.

OutcomeWhat it isDomain that originates it
Normal exityour entrypoint returnedapplication (clean finish)
Crash (trap)a guest fault — panic!, .unwrap() on None/Err, unreachable, an out-of-bounds access, or a capability violation that traps. Wasmtime raises a trap; the platform marks the process Crashed with the trap as the exit reasonapplication fault, caught by the platform
Memory crash (OOM)the guest tries to grow linear memory past its StoreLimiter ceiling (the capability profile's max-memory-mb). The allocation fails → trap → Crashed. It is an ordinary crash — bounded to that one instance, never the nodeapplication fault, bounded by the platform
Killan external kill, or an exit cascade from a crashed link. The platform fires the process's futures abort handle; the signal rides the same mailbox (no second channel)platform / supervision
Client disconnectthe peer closed the socket. Not a crash — the platform surfaces it (a failed write or a closed request body) and the unit ends cleanlyplatform (detected), application (observes via a false write)
Connection errora socket- or protocol-level failure around your code: a malformed request line, an unreadable body, a write to a half-open socket. Usually your code never runs — the platform answers directlyplatform

On any exit the platform reclaims the instance (returned to the pool), the mailbox, and any byte streams, then notifies links (an exit cascade, unless the peer set trap_exit) and monitors (a Down carrying the reason). A supervisor restarts per its strategy (one-for-one / one-for-all / rest-for-one) under windowed restart-intensity — too many restarts in the window and it escalates instead of crash-looping.

Why a failure stays local

The two-domain boundary is what makes the blast radius of any failure exactly one unit of work — the guarantee every chapter relies on:

  • Isolation by instance (platform). Each request/connection/worker is its own Wasm instance with its own linear memory and capabilities. A trap or OOM can corrupt only that instance's memory, which is discarded on exit.
  • No head-of-line blocking (platform). Serving is process-per-unit, so a slow, stuck, or crashed handler never holds up the listener or other clients.
  • Crash containment (platform). An application-domain fault becomes a Crashed exit — surfaced as a 502, a dropped connection, or a Down/exit signal — never a node-wide failure. Per-request work just gets a fresh instance; resident work is restarted (or escalated) by a supervisor.
  • No leaks, no spins (platform). Exit reclaims the instance, mailbox, and streams; byte streams are bounded and back-pressured, so a producer parks rather than spins, and a disconnect always tears the process down.

You write the application domain and reason about one request at a time; the platform domain guarantees the rest.

Other: wasi:cli command components

A stock command component (WasmRuntime::spawn_command) has no actor world — just wasi:cli/run. Normal: runs run, exits with code 0. Crash / non-zero exit / OOM: Crashed, the exit reason carrying the status. Same isolation and reclaim as any process; it just has no serving or messaging role.

See also: the process model, links & supervision, fibers & blocking→async, permissions & sandboxing, and the serving model.

MIT licensed