Skip to content
ElectricElectric
PricingBlog
Main Navigation
Go to Cloud
Cloud
Go to Cloud
Are you an LLM? You can read better optimized documentation at /streams.md for this page in Markdown format

Electric Streams

The data primitive for the agent loop

QuickstartDocs

The agent loop is a stream of durable events

Every prompt, tool call, and generation is appended at a known offset on a persistent, real-time stream. Replay from any offset, branch off, or fan out to humans, agents, and Electric Agents — over plain HTTP.

USER_MESSAGE11:21:50
Three clients reading the same session URL. Bob's connection drops, the others keep going, then Bob reconnects and catches up.
URL/v1/stream/session/design-review
Alicelive
agentlive
Boblive

Every stream is multiplayer

Streams aren't single-consumer. Any number of agents — and humans — can attach to the same stream, see each other's events as they land, and pick up exactly where they left off. The shared stream is the coordination layer.

Read Durable Sessions for Collaborative AI →

The 30-second tour

Four curl commands. Create a stream, append a message, read it back, then tail it live.

Animated terminal walkthrough: creates an Electric Stream, appends a message, reads it back from offset -1, then tails it live with server-sent events.
Terminal — durable-streams quickstart
$▍
0:00 / 0:23

Run this yourself → /docs/streams/quickstart

Ready to build

Ship your first durable stream in minutes

Install the client, create a stream, and start appending events. Subscribe live from anywhere on the network.

QuickstartDocs

From the blog

Go deeper on Electric Streams, the Durable Streams protocol, and what teams are building on them.

Fork — branching for Durable Streams

Read post →

Durable Streams — the data primitive for the agent loop

Read post →

AI agents as CRDT peers — building collaborative AI with Yjs

Read post →

StreamDB — a reactive database in a Durable Stream

Read post →
Electric BlogFollow @ElectricSQL

Streaming needs to be durable

SSE drops on a refresh. Tokens get lost on flaky networks. Resuming means re-running the request and re-billing the LLM.

Real apps need streams that survive disconnects, persist across sessions, and let many users and agents read and write the same conversation. That's what an Electric Stream is.

Comparison of two streaming token responses. Without Electric Streams, a dropped connection forces a full retry that re-bills the LLM. With Electric Streams, the client resumes from the last offset and only the missing tokens stream in.
Without Electric Streams
POST /v1/chat/completions
▶ The capital of
▶ France is Pa ✕ connection lost
⤴ retry
POST /v1/chat/completions re-bills
▶ The capital of
▶ France is Paris.
With Electric Streams
POST /v1/stream/chat-42
▶ The capital of
▶ France is Pa ✕ connection lost
⤴ resume
GET  /v1/stream/chat-42?offset=7
▶ ris.
✓ exactly-once, no extra LLM call

Three properties that change everything

Electric Streams is a protocol, not a SaaS. The protocol is the product.

{ url }

URL-addressable

Every stream lives at its own URL. Works with curl, fetch, any load balancer, any CDN.

PUT /v1/stream/hello
POST /v1/stream/hello
GET /v1/stream/hello
▤ append-only

Append-only

Once data is at an offset, it never changes. Offsets are opaque cursors that always sort in order.

POST → 200 OK
Stream-Next-Offset:
01JQXK5V00
↻ resumable

Resumable

Reads return Stream-Next-Offset. Reconnect with ?offset=… and pick up exactly where you left off.

GET ?offset=01JQXK5V00
→ next chunk only
Diagram showing a producer service posting chunks to an Electric Stream server that de-duplicates by Producer-Id, Producer-Epoch and Producer-Seq headers, while a consumer client reads chunks and resumes from the last offset it saw after a connection drop.
producer
svc
producer-idsvc-1
producer-epoch2
producer-seq15
POST
durable streams
GET ?offset=…
consumer
client
● connected

Replay from any offset, exactly once

Producers identify themselves with three headers. Servers de-dupe. Clients resume from the last offset they saw. No external coordination required.

Read the protocol →

It's just HTTP — works everywhere

If your runtime can speak HTTP, it can read and write an Electric Stream. No SDK lock-in. No proprietary transport. No WebSocket infrastructure.

A single HTTP stream URL is consumed by five clients written in TypeScript, Python, Swift, Go and curl. Each receives the same server-sent events.
GEThttps://api.streams.dev/v1/stream/chat-42?live=sse
data: {"role":"user","text":"Hello"}
data: {"role":"assistant","text":"Hi there!"}▍
one URL, every client
TSTypeScript
import { stream } from '@durable-streams/client'​for await (const m of stream({  url: STREAM_URL, live: 'sse'})) render(m)
Browser · Node · Workers
PyPython
from durable_streams import stream​with stream(STREAM_URL, live='sse') as r:  for x in r.iter_json():    process(x)
Data scientists · Workers
SwSwift
let task = URLSession.shared  .dataTask(with: URLRequest(url: URL(...)))task.resume()// SSE lines parsed by EventSource
iOS · macOS
GoGo
resp, _ := http.Get(STREAM_URL)scanner := bufio.NewScanner(resp.Body)for scanner.Scan() {  handle(scanner.Bytes())}
Servers
$_curl
curl -N "$URL?live=sse"​# prints SSE# data: lines
Shell · Scripts · Debugging

One protocol, four layers

Pick the layer you need. Bytes → JSON messages → typed CRUD events → reactive type-safe DB. Every layer above adds power; every layer below remains available.

01 · the wire

Electric Streams

Append bytes, replay from any offset. The HTTP base protocol every other layer is built on.

PUT /v1/stream/x
POST /v1/stream/x
GET /v1/stream/x?offset=…
Concepts →
02 · messages

JSON mode

Append JSON values, GET arrays. Message boundaries are preserved on the wire — no framing logic in your code.

Content-Type: application/json
POST → {"hello":"world"}
GET → [ {…}, {…}, {…} ]
JSON mode →
03 · typed CRUD

Durable State

Typed insert / update / delete events on the wire, plus snapshot markers. Materialise them into a live key-value view.

{"type":"user","value":{…},"headers":{"operation":"insert"}}
{"type":"user","key":"1","headers":{"operation":"delete"}}
{"headers":{"control":"snapshot-end"}}
Durable State →
04 · reactive DB

StreamDB

Live, typed collections with queries and optimistic actions, layered on top of MaterializedState.

db.users.where({ … })
db.users.insert(row)
useLiveQuery(query)
StreamDB →
Open protocol · Apache 2.0

Built on the open Durable Streams protocol

Electric Streams is one implementation. The protocol spec, conformance suite and reference clients live on durablestreams.com — independent, fully open.

durablestreams.comRead the spec

Built for the agent loop

From token streams to multi-agent collaboration — Electric Streams plugs into the AI stack you already use.

TanStack AITanStack AI

Durable connection adapter. Resumable, shareable AI sessions across tabs and devices.

Docs → Blog post →
Vercel AI SDK

Durable Transport for the AI SDK. Drop-in replacement for streamText transport.

Docs → Blog post →

Your stack, not ours

Self-host the server with one binary, or run it on Electric Cloud. Producers and consumers are anything that speaks HTTP.

Your producer
Anthropic · Express · FastAPI · cron
POST /v1/stream/…
Electric Streams
Electric Cloud · self-host
GET ?live=sse · ?offset=…
Your consumer
browser · agent · worker · iOS · Python
import { DurableStream, IdempotentProducer } from "@durable-streams/client"

const handle = await DurableStream.create({
  url: STREAM_URL,
  contentType: "application/json",
})

const producer = new IdempotentProducer(
  handle, "llm-relay-1", { autoClaim: true }
)

for await (const chunk of llm.stream(prompt))
  producer.append(chunk)

await producer.flush()
import { stream } from "@durable-streams/client"

const res = await stream<ChatMessage>({
  url: STREAM_URL,
  offset: lastSeen ?? "-1",
  live: "sse",
})

res.subscribeJson(async (batch) => {
  for (const msg of batch.items) render(msg)
  lastSeen = batch.nextOffset
})
curl -X POST $URL \
  -H 'Content-Type: application/json' \
  -d '{"event":"click"}'

curl -N "$URL?offset=-1&live=sse"

Your first stream, end to end

Create a stream. Append a message. Subscribe live. Three steps, one package, real APIs.

stream.ts
import { DurableStream, stream } from "@durable-streams/client"

const url = "https://streams.example.com/v1/stream/chat"

const handle = await DurableStream.create({
  url,
  contentType: "application/json",
})

await handle.append(JSON.stringify({
  role: "user", text: "Hello"
}))

const res = await stream<{ role: string; text: string }>({
  url,
  offset: "-1",
  live: "sse",
})

res.subscribeJson(async (batch) => {
  for (const msg of batch.items) console.log(msg)
})
Terminal
$ npx tsx stream.ts
✓ Created stream chat
✓ Appended message
→ { role: "user", text: "Hello" }
→ { role: "assistant", text: "Hi there!" }
→ ▍
1
One package, two entry points

DurableStream for read/write handles. stream() for fetch-style consumption.

2
DurableStream.create opens or creates

Idempotent: returns the existing handle if the stream already exists.

3
Pick your content type

application/json enables JSON mode — message boundaries are preserved.

4
Append messages

Each append is a single POST. Wrap with IdempotentProducer for exactly-once delivery and batching.

5
Resume from any offset

"-1" = beginning. Pass a saved offset to resume from exactly that point. "now" = skip the backlog.

6
Live, in real time

"sse" opens a long-lived Server-Sent Events stream. "long-poll" works in environments that can't hold a connection open.

7
Subscribe with batches

subscribeJson calls your handler with a batch.items array. The batch carries the next offset — save it to resume from later.

Demos

Reference apps you can clone, run locally, and learn from.

Durable Doom

Durable Doom

Doom on Durable Streams. Live spectating, time-travel, and fork-to-continue — all client-side, no backend.

Collaborative AI Editor

Collaborative AI Editor

Collaborative rich text editor where an AI agent is a server-side CRDT peer.

Territory Wars

Territory Wars

Multiplayer territory capture game built with Yjs CRDTs on Durable Streams.

All demos
Apache 2.0 · open source

Start streaming today

Create a stream, append events, subscribe live.

QuickstartDocsGitHub

ElectricElectric

AboutContactLegalDocsDemosBlogSign up
TanStack DBPGliteXBlueskyDiscordGitHub

© 2026 Electric DB Inc. Released under the Apache 2.0 License.

✨ Markdown