Durable streams
over
plain HTTP,
backed by S3.

One durable timeline per document, session, or agent run. Replayable, tailable, self-hosted.

# create a bucket, then a stream
$ curl -X PUT http://127.0.0.1:4437/demo
$ curl -X PUT http://127.0.0.1:4437/demo/hello

# append, acknowledged at Raft quorum
$ curl -X POST http://127.0.0.1:4437/demo/hello \
    --data-binary 'hello world'

# replay from any offset, or tail live
$ curl 'http://127.0.0.1:4437/demo/hello?offset=-1'
hello world
Any HTTP client is a valid client. No SDK.

Measured

7.4msp99 append · 100 streams · quorum ack across 3 availability zones
41,552
commits/s aggregate · 500 streams
8.3 ms
sse fan-out p99 · 1,000 subscribers, one stream
253 ms
catch-up replay p99 · 1,000 cold clients

measured 2026-05-22 · 3 × c7g.4xlarge · s3 cold flush on, ~675 MiB uploaded · 256 B payloads · one client binary

Full method and data →

What Ursula keeps

Every other server we evaluated for the Durable Streams Protocol gives up at least one of these four. Keeping all four is Ursula's reason to exist.

  1. 01

    Open-source self-hosting

    Apache-2.0, the complete server. Deploys as one binary, a Docker image, or a Helm chart.

  2. 02

    Low write latency

    Appends commit in a quorum-replicated in-memory ring. No batching window, no S3 PUT on the write path.

  3. 03

    Plain S3 economics

    Cold tier on S3 Standard. No Express tier, no per-GB SaaS markup.

  4. 04

    Quorum-replicated durability

    Acknowledged writes survive a single-node failure.

Why Ursula →

Proven live

Survives single-node failure,
on record.

A 3-node cluster on EKS takes voter Pod failures around the clock. Kubernetes recreates each voter while every read is verified against a running checksum.

nodesintegrityfeedchaos test · ec2 · feed unreachable
--d--h--m--s
running
----
faults injected
------
offsets verified
-
data corruptions
7 d agonow

Full record →

How

Replicate in memory,
flush to S3 in the background.

Thread-per-core × multi-Raft
Each stream hashes to one Raft group and one owner core. Hot-path requests touch that core only. A slow follower in one group never stalls another.
Hot ring
Writes commit at Raft quorum in memory, so appends take single-digit milliseconds with no separate broker and no batched commits.
S3 cold tier
Older segments flush to S3 in the background. One GET transparently spans hot and cold.

Architecture →

When it's the wrong shape

Where do your clients run,
and what is one stream?

These two questions decide the fit. In-network services pushing a few high-throughput pipelines is the wrong shape:

Full comparison →

Run it

# single in-memory node on :4437
$ docker run --rm -p 4437:4437 ghcr.io/tonbo-io/ursula:0.3.22

Quick start →Protocol spec →llms.txt →