Architecture
How Worker, ShardDO, SchedulerDO, D1, R2 and Queues fit together.
Last updated:
A Lunora deployment is a small set of Cloudflare primitives wired together by
@lunora/runtime. Each piece is single-purpose and replaceable.
┌────────────────────────────────────────────────┐
│ Worker (Edge) │
│ @lunora/runtime · createWorker({ ... }) │
└────────────────────────┬───────────────────────┘
│
┌────────────────┬───────────┼────────────┬───────────────┐
▼ ▼ ▼ ▼ ▼
ShardDO ×N SchedulerDO D1 R2 Queues
(SQLite) (alarms) (.global()) (files) (background)Tier 1 — Shard-local
ShardDO is the workhorse. Every non-global table lives inside it. Default
sharding routes to a single __root__ DO; .shardBy(field) partitions the
namespace so each value of field gets its own DO with its own SQLite,
its own CPU budget and its own hibernation timer.
Reasoning rules:
- Real-time subscriptions stay in-process. A delta never crosses DO boundaries; the mutation that wrote the row is also the broadcast source.
- Single-region writes. A DO is pinned to a region near its creator, and writes are strongly consistent within that region.
- Account-unlimited. You can have millions of
ShardDOinstances per account.
Tier 2 — Global (D1)
.global() tables move to Cloudflare D1. Use this for identities, billing,
cross-tenant audit logs, anything that needs to be queryable across shards.
Reads can be served by regional replicas; writes route to the primary.
@lunora/d1 threads the withSession(bookmark) API so your client gets
read-your-writes without a sticky-session router.
Tier 3 — Blob + async
- R2 —
@lunora/storageissues signed URLs; the browser uploads directly without proxying through the Worker. - Queues — for fire-and-forget work; Lunora emits jobs from a mutation
via
ctx.queue.send(...). Idempotency is on you. - KV — useful for edge-cached config and feature flags. Lunora does not ship a wrapper; use the binding directly.
Why this beats single-DO
Zeroback's single-DO model caps out at 10 GB SQLite and ~1 000 req/s.
Lunora's opt-in sharding moves both ceilings to N × 10 GB and
N × 1 000 req/s without changing your call sites. The cost is tooling:
codegen has to know how to address shards, and the docs have to be honest about
cross-shard queries being fan-out.