Run Durable Objects with celld.
How does a Durable Object operate, which problems fit it, and what changes when the model runs on infrastructure you choose?
the runtime model
the new implementation
Working implementation: writing.krasnoperov.me
One entity’s behavior is usually spread across infrastructure.
A Durable Object makes that entity the runtime and consistency boundary.
One globally unique instance of a class, reached by ID, running in one place, with private durable storage.
Start with the business key—not a database lookup.
Choose an example. The key deterministically addresses the authority for that thing.
idFromName()returns a stub
piece 7
A Durable Object does not run forever.
Its in-memory instance may disappear. Its logical identity and stored state do not.
One piece of JavaScript runs at a time. But another request may enter while the first waits.
Network wait: interleaving is possible. Storage operation: another request waits.
another request may enterinput and output gates// inside one Durable Object const row = ctx.storage.sql.exec( "SELECT revision FROM documents WHERE name = ?", "draft" ).one(); ctx.storage.transactionSync(() => { saveRevision(); queueJob(); });
Each object gets a private embedded database.
Strong local consistency
Storage methods are atomic and isolated. Synchronous SQL work can share a transaction.
Real local queries
Tables, indexes, JSON, and FTS can live next to the object’s decisions.
Intentional boundary
There is no arbitrary SQL join across all objects. Keep a separate index or shared relational store when the product needs one.
Who wakes the object after the request and process are gone?
The object stores one future wake-up time. That is enough to drive a durable schedule in SQLite.
setAlarm(earliest)alarm()process due rows
re-arm
At least once
The runtime retries a failed alarm. Therefore the handler must tolerate duplicate execution.
Can you name the thing that must decide consistently?
Select an atom of coordination.
And which entities do not fit?
Durable Objects are strongest when correctness belongs to one addressed entity—not many unrelated keys.
A writing coach that supports the writer without replacing them.
The piece has identity, memory, background work that outlives a request, and reasons to wake later.
WRITERChoose a topicSet the direction and questions worth exploring.COACHCollect materialFind useful facts, angles, and questions—not a draft.WRITERWrite the textThe writer owns the argument and prose.COACHCommentAnalyze what was actually written and suggest the next revision.One writing piece becomes one WritingCoach object.
Different texts have independent state, jobs, retries, and alarms. They should not block each other.
The piece list is a separate choice
This demo uses a WritingLibrary object. A relational table would work too; piece-local correctness does not depend on it.
The model call is disposable. The job protocol is durable.
Every asynchronous step is represented as state the object can recover.
Crash after the answer. Before the commit.
The lease expires. The alarm retries. The model may answer again.
One owner ≠ exactly once
Ownership prevents concurrent authorities. It cannot make an external API call and a local commit one transaction.
job_abc is retried
The new instance restores state and reclaims the expired job.
appliedJobs.includes("job_abc")
The append receipt turns duplicate delivery into the already-committed result.
The design lesson
Put an idempotency key at every external-effect seam.
A stable identity can address several independent authorities.
GitHub OAuth authenticates at the edge. The immutable numeric ID becomes routing.
GitHub OAuthUser 4581825Temporary token is used for one profile lookup, then discarded.
github:4581825WritingLibraryPiece discovery and limits.
github:4581825WritingAccountSubscription, allowance, reservations, and audit.
login renamedSame objectsProfile data changes. Durable identity and admin authority do not move.
One account balance is local. Charging work crosses objects.
Select the sequence to see where the transaction boundary ends.
celld makes the stateful core an infrastructure choice.
Nodes are replaceable. The bucket is fleet authority and durable replica store.
Not the whole Cloudflare platform
celld runs the compatible Worker and Durable Object core. Managed ingress, global placement, KV, Cache API, and other services remain outside that boundary.
receives request
active piece SQLite
replaceable compute
SQLite replicas
node leases
deployments
denoland/celld · Apache-2.0 · alpha runtime
Name the long-lived thing. Give it an address, decisions, local data, and time.
Durable Objects provide a different default for entity-local coordination. celld makes that model deployable on infrastructure you choose.