Skip to content
✨ Markdown

Durable State ​

Durable State adds structured state changes on top of Durable Streams. Instead of treating a stream as raw bytes, you work with typed insert, update, and delete events.

Protocol overview ​

Durable State uses JSON streams (Content-Type: application/json) and define two message types:

  • Change messages for insert, update, and delete
  • Control messages for snapshot boundaries and resets

Clients append these events to a stream and materialize state by applying them in order.

Use Durable Streams directly for raw token or byte streaming. Use Durable State when you want database-style sync semantics on top.

See the full State protocol specification for the formal wire format and requirements.

Installation ​

bash
npm install @durable-streams/state @tanstack/db

@tanstack/db is only needed for StreamDB. If you only need MaterializedState, you can skip it.

Change events ​

The State Protocol defines a standard format for state change events. Each event targets a typed entity identified by type and key, and carries an operation in its headers:

json
{
  "type": "user",
  "key": "user:123",
  "value": { "name": "Alice", "email": "alice@example.com" },
  "headers": {
    "operation": "insert",
    "txid": "abc-123",
    "timestamp": "2025-12-23T10:30:00Z"
  }
}

Fields:

FieldRequiredDescription
typeYesEntity type discriminator -- routes events to the correct collection
keyYesUnique identifier for the entity within its type
valueFor insert/updateThe entity data
old_valueNoPrevious value, useful for conflict detection
headers.operationYesOne of "insert", "update", or "delete"
headers.txidNoTransaction identifier for confirmation
headers.timestampNoRFC 3339 timestamp

Multiple entity types coexist in the same stream. A chat room stream might carry user, message, reaction, and typing events, all interleaved and processed in order.

Control events ​

The protocol also defines control events for stream management, separate from data changes. These have a control field in their headers instead of an operation:

ControlPurpose
snapshot-startMarks the beginning of a snapshot -- a complete dump of current state
snapshot-endMarks the end of a snapshot boundary
resetSignals clients to clear their materialized state and restart
json
{"headers": {"control": "snapshot-start", "offset": "123456_000"}}

{"type": "user", "key": "1", "value": {"name": "Alice"}, "headers": {"operation": "insert"}}
{"type": "user", "key": "2", "value": {"name": "Bob"}, "headers": {"operation": "insert"}}

{"headers": {"control": "snapshot-end", "offset": "123456_789"}}

You'll encounter these when a server sends a full state snapshot rather than incremental changes -- for example, on initial connection or after a schema migration.

MaterializedState ​

MaterializedState is a simple in-memory key-value store that applies change events. It's the minimal way to consume state protocol events -- no schemas, no reactive queries, just a map from (type, key) to the latest value.

typescript
import { MaterializedState } from "@durable-streams/state"

const state = new MaterializedState()

state.apply({
  type: "user",
  key: "1",
  value: { name: "Alice" },
  headers: { operation: "insert" },
})

state.apply({
  type: "user",
  key: "1",
  value: { name: "Alice Smith" },
  headers: { operation: "update" },
})

const user = state.get("user", "1") // { name: "Alice Smith" }
const allUsers = state.getType("user") // Map of all users

MaterializedState is a good fit when you need straightforward state tracking without reactive queries or schema validation.

StreamDB ​

For applications that need reactive queries, filtering, joins, and optimistic updates, use StreamDB.

StreamDB is the createStreamDB / StreamDB layer in @durable-streams/state/db, built on top of State Streams and TanStack DB.

Durable Sessions ​

A Durable Session multiplexes AI token streams with structured state into a persistent, shared session. Multiple users and agents can subscribe to and join the session at any time, making it a natural fit for collaborative AI applications.

The pattern layers protocols on top of each other:

  1. Durable Streams -- reliable, resumable byte delivery
  2. State Protocol -- structured CRUD operations over streams
  3. Application protocols -- AI SDK transports, presence, CRDTs

This enables scenarios where an AI agent streams tokens into a session while structured state (tool results, user presence, shared documents) flows through the same infrastructure.

For a detailed walkthrough, see the Durable Sessions for Collaborative AI blog post.

Learn more ​


See also: Core concepts | JSON mode | StreamDB | Yjs