Skip to content

Runtime Model

SpaceTimeDB runs module code inside a strict host contract: each reducer, procedure, view, HTTP handler, and lifecycle handler is synchronous, run-to-completion, single-transaction where applicable, and deterministic. effect-spacetimedb runs Effect programs inside that contract rather than pretending the module runtime is a normal JavaScript environment.

Finalizers from scoped Effects still run synchronously before the host commits or rolls back, so Effect.acquireRelease is safe when the acquired resource is bounded to the current call.

Every replica must replay the same input log to the same state. Use Random.* from effect/Random for deterministic randomness and use the call timestamp for time. Do not call Math.random, Date.now, or performance.now for server semantics. See Randomness & Determinism for the full guard and UUID-helper contract.

Determinism is reproducibility, not secrecy. Generate tokens, secrets, and cryptographically unpredictable values outside the reducer and pass them in.

Reducers run inside one ambient writable transaction and may yield Db directly. Procedures and HTTP handlers open transactions explicitly with Tx or HttpTx. Transaction bodies may be retried after optimistic commit conflicts, so keep them pure database work and put external side effects outside them.

  • Keep handler bodies synchronous.
  • Use Random.* from effect/Random, not Math.random.
  • Use call timestamps, not ambient clocks.
  • Write directly in reducers; use tx.run(...) in procedures and HTTP handlers.
  • Keep transaction bodies idempotent and database-only.
  • Import effect-spacetimedb/server-polyfills once in module entrypoints next to effect-spacetimedb/server-compiler; native spacetime build keeps that side effect without bundle prelude injection.