Ursula: Durable streams over plain HTTP, backed by S3

Palmik1 pts0 comments

Ursula - Durable Streams over HTTP, backed by S3

put creates a stream.<br>post appends at quorum.<br>get replays or tails.<br>Durable streams<br>over<br>plain HTTP,<br>backed by S3.<br>One durable timeline per document, session, or agent run. Replayable, tailable, self-hosted.<br>Quick start →GitHub →

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

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

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

Measured<br>7.4msp99 append · 100 streams · quorum ack across 3 availability zones<br>41,552commits/s aggregate · 500 streams<br>8.3 mssse fan-out p99 · 1,000 subscribers, one stream<br>253 mscatch-up replay p99 · 1,000 cold clients<br>measured 2026-05-22 · 3 × c7g.4xlarge · s3 cold flush on, ~675 MiB uploaded · 256 B payloads · one client binary<br>Full method and data →

What Ursula keeps<br>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.<br>01 Open-source self-hosting

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

02 Low write latency

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

03 Plain S3 economics

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

04 Quorum-replicated durability

Acknowledged writes survive a single-node failure.

Why Ursula →

Proven live<br>Survives single-node failure,<br>on record.<br>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.<br>nodesintegrityfeedchaos test · ec2 · feed unreachable<br>--d--h--m--srunning<br>----faults injected<br>------offsets verified<br>-data corruptions

7 d agonow

Full record →

How<br>Replicate in memory,<br>flush to S3 in the background.<br>append http postowner core hot ring · in memoryraft quorum 2 of 3 votersack single-digit msbackground flushhots3 · cold tier plain s3 standardcoldget one read · hot + coldursula · write path v0.4.1-alpha.5 · port 4437

Thread-per-core × multi-RaftEach 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.<br>Hot ringWrites commit at Raft quorum in memory, so appends take single-digit milliseconds with no separate broker and no batched commits.<br>S3 cold tierOlder segments flush to S3 in the background. One GET transparently spans hot and cold.<br>Architecture →

When it's the wrong shape<br>Where do your clients run,<br>and what is one stream?<br>These two questions decide the fit. In-network services pushing a few high-throughput pipelines is the wrong shape:<br>Kafka / Redpanda A few high-throughput topics, in-network consumer groups<br>S2 Managed service, willing to adopt a vendor API<br>S3 directly Immutable blobs, no append or tail<br>etcd Small consistent cluster-wide state<br>Ursula Many small per-resource streams, open-internet HTTP clients, replay and live tail in one primitive→<br>Full comparison →

Run it<br># single in-memory node on :4437<br>$ docker run --rm -p 4437:4437 ghcr.io/tonbo-io/ursula:0.4.1-alpha.5<br>Quick start →·Protocol spec →·llms.txt →

http ursula streams quorum cold durable

Related Articles