Sessions and state
Machines are the canonical state — but a machine instance doesn’t sit in memory waiting for the next request. This page explains where state actually lives between requests, and the short lifecycle each request runs.
Stateless between requests
Section titled “Stateless between requests”Stator does not keep a long-lived actor per session in process memory. That would scale with sessions × routes-visited and foreclose multi-replica deployments. Instead, canonical state lives as snapshots in the store, and each request spins up only the actors it needs, then throws them away.
This was an early, load-bearing decision: going stateless-between-requests turned out cheaper architecturally than persisting live actors, and every later feature (store adapters, SSE, the future inbox) composes cleanly on top of it.
SessionRuntime lifecycle
Section titled “SessionRuntime lifecycle”Each request creates a SessionRuntime that runs a short, predictable cycle:
- Load —
loadGraphpulls only the machines this request reads from the store and hydrates transient actors from their snapshots. Machines the request doesn’t touch are never loaded. - Wire — cross-machine
subscribeslisteners are rebuilt for this request’s actor graph. - Process — the event runs through the relevant machine’s transition; the runtime records which machines were touched.
- Persist —
persistTouchedwrites the touched session machines back to the store with a refreshed TTL. - Dispose — every transient actor is stopped; after dispose the runtime is done.
Only touched machines are written, and only read machines are loaded — the request does the minimum I/O its work requires.
The Store as the swap point
Section titled “The Store as the swap point”The runtime never talks to Redis or a Map directly; it talks to the Store interface. That indirection is the swap point: InMemoryStore for development, RedisStore (optionally behind CachedStore) for production, with no change to machines or templates. TTL is applied per session — a whole session expires together on inactivity, so a cart never loses individual line items mid-checkout. See Persistence.
App machines
Section titled “App machines”lifecycle: 'app' machines are the exception to all of the above. They live in process memory for the server’s lifetime, shared by every session, and are not persisted on touch — a seeded catalog re-seeds on boot. They’re loaded once at startup rather than per request.
The single-replica boundary
Section titled “The single-replica boundary”Within one replica, concurrent events to the same session are serialized by a per-session async lock, so transitions never interleave and corrupt state. This is correct and sufficient on a single replica.