How Ramose works
This page shows how a write becomes a read, with one picture. It is for anyone who has read a guide and wants to know what happens after they call db.transact.
Cloudflare words you’ll meet
Section titled “Cloudflare words you’ll meet”- Worker — Cloudflare’s serverless function. Your app’s backend and the Ramose server are Workers.
- Durable Object — a Worker with exactly one running instance and its own storage (glossary). The writer and the read copies are Durable Objects.
- R2 — Cloudflare’s object storage, like S3 (glossary). Every version of every database is kept there.
- D1 — Cloudflare’s SQL database. Ramose never uses it; Reef’s sign-in Worker keeps Better Auth’s accounts in one.
- Service binding — a Worker calling another Worker directly, no URL. How your Worker reaches the Ramose server (glossary).
The picture
Section titled “The picture”The five numbered arrows are the five steps below, in the same order.
A write, step by step
Section titled “A write, step by step”Reef’s “drag an issue to Done” is one write — one all-or-nothing group of changes; Ramose calls it a transaction (glossary). Here is its journey:
- Your app sends the write over HTTPS to the Ramose server — the one Cloudflare Worker that serves all your databases; Ramose’s code calls it the peer (glossary).
- The server verifies the sign-in token, then checks the rules in the policy (the writer checks them again before committing). A viewer moving a card is refused here; nothing else happens.
- The server hands the write to the writer — the one thing per database that commits writes, in order (glossary). The writer stores it before answering, the database’s version number
tgoes up by one (glossary), and your app getsreport.tback. - The read copies learn the new version.
- Every open tab is told within about a second, and every live query on those pages re-runs and hands the screen new rows (glossary). Nobody subscribed to anything.
A read, step by step
Section titled “A read, step by step”- A query runs on a read copy — a copy of the data near your users, kept current by the writer (glossary). It never waits on the writer.
- The rules filter what comes back: a field you may not read is missing, not an error.
- The read happens at a version. Live reads follow the newest one;
db.asOf(t)pins an earlier one.
The four promises
Section titled “The four promises”- One writer per database. All writes to a database go through one place, in order. There are no write conflicts to resolve and no “eventually”.
- Version numbers are dense and in order. Every write gets the next
t; nothing mints, skips or supplies one. - A write is stored before you get an answer. If
db.transactreturns, the write is durable. - Reads never block the writer. Queries run on read copies; the writer’s queue does not compete with them.
What runs where
Section titled “What runs where”| Piece | Runs as | You write it? |
|---|---|---|
| your app | a browser page, or your own Worker | yes |
| the Ramose server | one Cloudflare Worker, all databases | no — you declare it |
| the writer | a Durable Object, one per database | no — you name it |
| read copies | Durable Objects | no — you name them |
| object storage | a Cloudflare R2 bucket | no — you name it |
That table is why the deploy file names three things: a storage bucket where every version is kept, and two Durable Objects — one for the writer, one for the read copy.
const Store = Cloudflare.R2.Bucket("Store");const Transactor = Cloudflare.DurableObject("TransactorDO", { className: "TransactorDO" });const Replica = Cloudflare.DurableObject("QueryReplicaDO", { className: "QueryReplicaDO" });You name them; you never write them. Every version of every database ends up in object storage, and nothing there is rewritten, which is why you can still read yesterday.
When something fails
Section titled “When something fails”- The writer’s storage fails mid-write. Nothing from that write is kept; the client sees a 503 and retries; version numbers continue with no gap.
- A read copy falls behind or disconnects. It reconnects on the next request and catches up from the log.
- The WebSocket connection drops. The client reconnects with backoff; live queries pick up where they left off.
Details, settings and what to look at are in The server.