NAME

celeriant is a fast, two-node append-only event store

SYNOPSIS

// write your events to an aggregate
await pool.WriteAsync(key, [event], clientId, expectedVersion: version);

// you can do atomic, cross-aggregate writes with optimistic concurrency control
await pool.WriteAsync(new WriteRequest {
    ClientId = clientId,
    Writes = new() {
        [from] = new() { Events = [withdrawn], ExpectedVersion = fromVersion },
        [to]   = new() { Events = [deposited], ExpectedVersion = toVersion },
    },
});

// per-aggregate reads for read models
await foreach (var batch in pool.ReadAllAsync(key, ReadFilters.From(version)))
    foreach (var e in batch.Events)
        state = Apply(state, e);

PURPOSE FOR EXISTING

There is currently no good, fast, dedicated event store implementation that is open source.

It exists because as a consultant, I see clients fuck up event sourcing over and over again and there needs to be a better primitive for this pattern.

Dev teams reach for Kafka to move events between services, but there is no optimistic concurrency control and no per aggregate reads. State between services gets coupled which is often subtle and the system invariants break in prod.

The popular open source alternative is Marten on Postgres, which needs a background daemon just to hand your async projections the events in order.

Celeriant is not a vibe-coded project. See PROVENANCE.

FEATURE DUMP

The quick TLDR of what Celeriant is and what it isn't.

A server, not embedded

Two node cluster, high availability, requires an S3 compatible object store but only for leadership arbitration. Any event you write is durably stored to disk before acknowledging to the client.

OCC + DCB

An implementation subset of dynamic consistency boundaries. Means you can do atomic writes over multiple aggregates. Optimistic concurrency control to make sure your system rules don't get broken under concurrent writes to aggregates. Limited to aggregates co-located on the same shard.

Exactly-once append

Proper idempotency implementation for clients. Never get duplicate events in your event store for an aggregate. Reference implementation available.

Per aggregate reads and watch API.

Provide an offset position and get the events for an aggregate after that. No out of order events and no gaps in the sequence. Connect to the server via the watch API and get an instant notification when events hit an aggregate.

End to end encryption

Encrypt individual events to adhere to GDPR using crypto shredding techniques. Event log stays immutable, but you stay compliant.

Server-side schema validation

Unlike Kafka, server-side schema validation for events is built in and open source. JSON, Protobuf, avro support.

What it's not

Celeriant doesn't have read projections or a read model. No SQL, no analytical queries. Write side only. Pure, hardcore, CQRS.

PERFORMANCE

The only database that makes the most hardcore AWS i4i instances sweat.

Connections
60,000
Durable writes / sec
1,057,000
p50
48 ms
p95
72 ms
p99
108 ms
Client Concurrency
60,000 durable writes in flight at once, across four load-generating clients. Easily handles 100k connections without breaking.
Payload
one "Hello World" event per acknowledged write. No client-side batching
Hardware
two AWS i4i.metal data nodes: 64 physical cores each (128 vCPU, two sockets), eight local NVMe drives striped RAID0
Cost
$26.33 an hour. scales down to $295 a month and still hold 67,000 durable writes a second at p99 165ms, replicated and mTLS the whole way
Network
ap-southeast-2, single availability zone. Expect higher costs and latency for cross-AZ
Security
mTLS on client connections and on cluster replication
Durability
every write is fdatasync'd to disk on both nodes through Direct I/O, replicated to the follower, and acknowledged only after both succeed

Every latency is end-to-end, including replication and both fsyncs, over mTLS on the client and replication paths. That is encrypted, durable, replicated throughput. Reproduce it yourself for a few dollars.

CONFORMING TO

Apache-2.0. The server runs on Linux. Clients exist for .NET and Rust.

NERDY STUFF

The parts that aren't obvious from the feature list.

S3 conditional writes instead of Raft

No Raft, no Paxos, no ZooKeeper. Leader election is a compare-and-swap on one S3 object, and a leader whose lease expires fences itself. S3 doubles as the backup replica, so losing the follower doesn't stop writes. Nothing is acked until it sits on two storage systems.

kTLS on the io_uring path

Most io_uring databases skip TLS. The rest wrap the stream in userspace and hand back the zero-copy win.

Celeriant handshakes with rustls, then passes the session secrets to the kernel via setsockopt(SOL_TLS). The kernel does the crypto and io_uring never touches ciphertext.

Metablocks grow up, datablocks grow down

Fixed 1024-byte metablocks grow from the top of the WAL file, variable datablocks grow backwards from the bottom, and the file rotates when they meet. Free space is the gap between two cursors. No fragmentation, nothing to manage, every metablock at a predictable offset.

Small batches sit inline in the metablock. One less seek.

Bloom filters over reverse WAL scanning

Every segment carries a 256KB bloom filter of its aggregate keys, so a cold read scanning backwards skips whole segments on one check. Ten million aggregates, and no ten million index entries in RAM.

Connections hand off between cores

A TCP stream belongs to one executor. But a client lands on whichever core accepted it, and its first request may be for an aggregate another shard owns. So the connection moves. Unbound, sent across the mesh channel, rebound on the target core. If the mesh is full the client gets SERVER_BUSY rather than stalling a WAL write.

Glommio couldn't do this. You could route a raw fd, but not after reading from the stream, and you have to read the request to know where it goes. Upstreamed as TcpStream::into_accepted, merged June 2026.

Speculate aggressively, roll back safely

Writes hit disk before replication confirms. Every segment carries two cursors, so the write path sees data straight after fsync and OCC stays correct, while readers wait for replication. Nobody reads a write that could still be rolled back.

When replication fails the cursor rewinds, caches clear, and segment headers are physically rewritten back.

Tested by pulling the plug

Speed is measured on AWS. Correctness is measured on two Raspberry Pi 5s, because slow hardware opens races an i4i.metal never will.

Thirty-one scenarios kill processes, sever replication, stop S3, skew both clocks and fill the disk, against a real cluster over SSH, from a seed that replays exactly.

Home lab at night. A wall-mounted shelf of networking gear and single-board computers sits above a power rack, lit by RGB from a desktop machine beside it.
The chaos rig, mounted above a squat rack.

PROVENANCE

Celeriant is not a vibe-coded project. Built by one veteran dev over a period of 3 years.

Opus & Fable are used. Mostly around the boring parts; scaffolding test harnesses, long running autonomous testing and finding bugs. Any production code written by LLMs is done in a sandbox, discarded and re-written by a human. See build-method and human-replay.

Building Celeriant has been a form of artistic expression for me. Painters paint, poets write poems, devs write code.

CONTACT

Celeriant is released under Apache-2.0. The OSS core will be fully functional; no crippled community edition.

The database is new and still somewhat experimental. Try it in non-prod first.

Email me at [email protected] or add me on LinkedIn. Or just grab the code on GitHub and give it a go.