Skip to content

Quick Start Guide

This guide walks you through using the Unbroken Protocol. You’ll create a stream, write data, and read it back using different modes.

Prerequisites

  • A running Unbroken Protocol server (default port: 9999)
  • curl for command-line examples
  • A modern browser for SSE examples

Starting the Server

Terminal window
# Using the reference implementation
bun run start:dev
# Server starts on http://localhost:9999

Step 1: Create a Stream

Create a new JSON stream:

Terminal window
curl -X PUT http://localhost:9999/events \
-H "Content-Type: application/json" \
-v

Response:

HTTP/1.1 201 Created
Location: /events
Content-Type: application/json
Stream-Next-Offset: 0000000000000000_0000000000000000

The stream is created and ready for data.

Step 2: Append Data

Add an event to the stream:

Terminal window
curl -X POST http://localhost:9999/events \
-H "Content-Type: application/json" \
-d '{"event": "user_signup", "user_id": 42}' \
-v

Response:

HTTP/1.1 204 No Content
Stream-Next-Offset: 0000000000000000_0000000000000039

The data is now durably stored. Add more events:

Terminal window
# Batch append (array is flattened)
curl -X POST http://localhost:9999/events \
-H "Content-Type: application/json" \
-d '[{"event": "page_view", "page": "/home"}, {"event": "click", "button": "signup"}]'

Step 3: Read Data (Catch-Up)

Read all data from the beginning:

Terminal window
curl "http://localhost:9999/events?offset=-1"

Response:

[
{ "event": "user_signup", "user_id": 42 },
{ "event": "page_view", "page": "/home" },
{ "event": "click", "button": "signup" }
]

Headers include:

Stream-Next-Offset: 0000000000000000_0000000000000108
Stream-Up-To-Date: true

Step 4: Read Data (Long-Poll)

Wait for new data using long-polling:

Terminal window
# In terminal 1: Wait for new data
curl "http://localhost:9999/events?offset=0000000000000000_0000000000000108&live=long-poll"
# In terminal 2: Append new data
curl -X POST http://localhost:9999/events \
-H "Content-Type: application/json" \
-d '{"event": "purchase", "amount": 99.99}'

Terminal 1 immediately receives the new data:

[{ "event": "purchase", "amount": 99.99 }]

Step 5: Read Data (SSE)

For real-time streaming in browsers, use Server-Sent Events:

// Browser JavaScript
const offset = "-1" // Start from beginning
const eventSource = new EventSource(`/events?offset=${offset}&live=sse`)
let currentOffset = offset
eventSource.addEventListener("data", (event) => {
const data = JSON.parse(event.data)
console.log("Received:", data)
})
eventSource.addEventListener("control", (event) => {
const control = JSON.parse(event.data)
currentOffset = control.streamNextOffset
console.log("Offset:", currentOffset)
})
eventSource.onerror = () => {
console.log("Connection lost, reconnecting...")
// Reconnect with currentOffset
}

Or using curl:

Terminal window
curl "http://localhost:9999/events?offset=-1&live=sse"

Output:

event: data
data: [{"event":"user_signup","user_id":42}]
event: control
data: {"streamNextOffset":"0000000000000000_0000000000000039"}
event: data
data: [{"event":"page_view","page":"/home"},{"event":"click","button":"signup"}]
event: control
data: {"streamNextOffset":"0000000000000000_0000000000000108"}

Step 6: Resumable Reading

Persist the offset to resume after disconnection:

// Save offset to localStorage
function saveOffset(offset) {
localStorage.setItem("events_offset", offset)
}
function loadOffset() {
return localStorage.getItem("events_offset") || "-1"
}
// Use saved offset when connecting
const offset = loadOffset()
const eventSource = new EventSource(`/events?offset=${offset}&live=sse`)
eventSource.addEventListener("control", (event) => {
const { streamNextOffset } = JSON.parse(event.data)
saveOffset(streamNextOffset)
})

Step 7: Stream Metadata

Check stream existence and get current offset:

Terminal window
curl -I http://localhost:9999/events

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Stream-Next-Offset: 0000000000000000_0000000000000142

Step 8: Delete Stream

Remove the stream when done:

Terminal window
curl -X DELETE http://localhost:9999/events

Response:

HTTP/1.1 204 No Content

Complete Example: Chat Application

Here’s a complete example using the protocol for a chat application:

Server-Side (Creating Conversations)

// Create a conversation stream
await fetch("/conversations/room-123", {
method: "PUT",
headers: { "Content-Type": "application/json" },
})

Sending Messages

async function sendMessage(roomId, message) {
await fetch(`/conversations/${roomId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
type: "message",
user: currentUser,
text: message,
timestamp: Date.now(),
}),
})
}

Receiving Messages

function connectToRoom(roomId, onMessage) {
let offset = localStorage.getItem(`room_${roomId}_offset`) || "-1"
const eventSource = new EventSource(
`/conversations/${roomId}?offset=${offset}&live=sse`
)
eventSource.addEventListener("data", (event) => {
const messages = JSON.parse(event.data)
messages.forEach(onMessage)
})
eventSource.addEventListener("control", (event) => {
const { streamNextOffset } = JSON.parse(event.data)
localStorage.setItem(`room_${roomId}_offset`, streamNextOffset)
})
return () => eventSource.close()
}
// Usage
const disconnect = connectToRoom("room-123", (msg) => {
console.log(`${msg.user}: ${msg.text}`)
})

Using the TypeScript Client

For TypeScript/JavaScript applications, use the official client:

Terminal window
npm install @unbroken-protocol/client
import { UnbrokenStream } from "@unbroken-protocol/client"
// Create a stream handle
const stream = new UnbrokenStream("http://localhost:9999/events", {
contentType: "application/json",
})
// Write data
await stream.append({ event: "click", x: 100, y: 200 })
// Read with subscription
const response = await stream.read({ offset: "-1" })
for await (const message of response.messages()) {
console.log("Message:", message.data)
}

Next Steps