Skip to content
← All work

Ledgerline

An edge-native event-stream API with exactly-once writes and a tamper-evident hash chain.

Private build Cloudflare WorkersDurable ObjectsD1TypeScriptHono
Ledgerline: An edge-native event-stream API with exactly-once writes and a tamper-evident hash chain.

Code private. 68 tests run inside the real workerd runtime. Ask me for a walkthrough.

The idea

Every ledger, audit trail, and billing system shares the same integrity core: an ordered stream of events that must accept each write exactly once and prove, later, that nothing was altered. Ledgerline is that core, built properly, running on Cloudflare’s edge. There is no product wrapped around it and no UI. The guarantees are the product.

What I built

A Durable Object owns each stream’s write side. Because it is single-threaded, strong per-stream ordering comes structurally, not through locks: monotonic sequence numbers, idempotency records, the hash chain, and per-minute rollups all commit in one atomic batched write. D1 mirrors events best-effort as an eventually consistent read model for pagination and queries. It is a small, honest CQRS split where each side does what it is structurally good at.

Every event is hashed over a canonical JSON form and chained to its predecessor. The verify endpoint recomputes the chain from genesis and points at exactly where it breaks, and it catches tail truncation, not just in-place edits. The canonical form is pinned by known-answer test vectors that match Python’s json.dumps settings, so an external auditor can verify a stream without ever running my code. Auth uses SHA-256-hashed API keys, a foreign stream returns 404 rather than 403 so existence never leaks, and rate limiting is a per-key token bucket living in its own Durable Object.

The hard parts

01

Exactly-once, defined honestly

Most idempotency schemes expire keys after a TTL, which really means “exactly once, for a while.” Ledgerline reserves each idempotency key for the stream’s lifetime, because that footnote is the kind that becomes an incident.

02

Serialization without locks

A Durable Object is single-threaded, but an await inside a handler lets another request interleave. blockConcurrencyWhile closes the window where two writers could read the same head and both append after it.

03

Verification that catches truncation

A hash chain that only detects edited events misses a deleted tail: the chain still verifies, it is just shorter. Keeping an O(1) head record makes “someone cut the end off” as detectable as “someone changed event 42.”

What’s next

A small public read-only demo stream, so the verify endpoint can be poked live.