> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# Rules: readers

Last updated 29 October 2026

Check these rules before you rely on a program that reads or tails a stream: each keeps it from missing or repeating messages.

The requests keep the stream’s URL in `STREAM`, a key in `TONBO_API_KEY`, offsets in `NEXT` and `OFFSET`, and the stream’s incarnation in `INCARNATION`, as [Append](https://tonbo.dev/docs/streams/append.md), [Read and tail](https://tonbo.dev/docs/streams/read.md) and [Snapshots and retention](https://tonbo.dev/docs/streams/snapshots.md) do.

### Keep offsets exactly as the stream returns them

Offsets are opaque strings that sort in stream order. Resume from the last `Stream-Next-Offset` you saw, never from one you compute (see [Read](https://tonbo.dev/docs/streams/read.md#read)).

**Do**

```shell
NEXT=<the Stream-Next-Offset value>
curl -sS -i "$STREAM?offset=$NEXT&live=long-poll&timeout_ms=30000" \
  -H "Authorization: Bearer $TONBO_API_KEY"
```

**Don’t**

```shell
NEXT=$((LAST + 1)) # an offset is not a number to count on
```

### Read until the answer is up to date

One answer carries at most 8 MiB. An answer without `Stream-Up-To-Date: true` stopped short: read on from its `Stream-Next-Offset`.

**Do**

```js
let offset = "-1";
for (;;) {
  const response = await fetch(`${stream}?offset=${offset}`, { headers });
  handle(await response.text());
  offset = response.headers.get("Stream-Next-Offset");
  if (response.headers.get("Stream-Up-To-Date") === "true") break;
}
```

**Don’t**

```js
const response = await fetch(`${stream}?offset=-1`, { headers });
handle(await response.text()); // at most 8 MiB: the rest is missing
```

### Save a reader’s offset with what it did

Tonbo keeps no state for a reader (see [What readers see](https://tonbo.dev/docs/streams/guarantees.md#readers)). A reader that acts on messages and saves its offset later reads them again after a crash in between; saved in one transaction, for example in a [database](https://tonbo.dev/docs/databases.md), both happen or neither does.

**Do**

```js
db.exec("BEGIN");
for (const message of messages) apply(db, message);
db.prepare("UPDATE readers SET next = ? WHERE name = ?").run(next, "billing");
db.exec("COMMIT"); // the work and the offset, together
```

**Don’t**

```js
for (const message of messages) await act(message);
writeFileSync("next-offset", next); // a crash before this acts on them again
```

### Reopen a tail from the last offset it gave you

The server sends no heartbeats, so a proxy may close a quiet tail. Open it again from the last `streamNextOffset` or `Stream-Next-Offset`; a tail opened at `now` skips what was appended while it was closed (see [Tail](https://tonbo.dev/docs/streams/read.md#tail)).

**Do**

```shell
curl -sS -N "$STREAM?offset=$NEXT&live=sse" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Accept: text/event-stream"
```

**Don’t**

```shell
curl -sS -N "$STREAM?offset=now&live=sse" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Accept: text/event-stream"
```

### Guard against a stream deleted and created again

A stream created again under the same name is a new stream with new offsets. Send its `Stream-Incarnation`, and a request to a replaced stream answers `412` instead of reading the new one (see [A stream deleted and created again](https://tonbo.dev/docs/streams/read.md#incarnation)).

**Do**

```shell
curl -sS -i "$STREAM?offset=-1" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Stream-Incarnation: $INCARNATION"
```

### Publish a snapshot, check it, then advance retention

Retention drops the history before the snapshot for good, and the two steps are separate so that you can read the snapshot back first (see [Retention](https://tonbo.dev/docs/streams/snapshots.md#retention)).

**Do**

```shell
curl -sS -i -X PUT "$STREAM/snapshot/$OFFSET" \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":3}'

curl -sS -i "$STREAM/snapshot/$OFFSET" \
  -H "Authorization: Bearer $TONBO_API_KEY"

curl -sS -i -X PUT "$STREAM/retention/$OFFSET" \
  -H "Authorization: Bearer $TONBO_API_KEY"
```
