Type-safe, real-time backend on your own Cloudflare account. Vite-first.
Documentation · Website · Packages · Quick start
Daniel Bannert's open source work is supported by the community on GitHub Sponsors
Lunora gives you Convex-style DX on your own Cloudflare account. You write type-safe queries, mutations, and actions in TypeScript, and Lunora deploys them as Cloudflare Workers. Durable Objects hold real-time state, D1 handles SQL, R2 stores blobs, and Queues run jobs. Nothing runs on servers we operate; everything lives in the Cloudflare account you already pay for.
Lunora is Vite-first. The dev loop, codegen, and client bindings plug into a Vite project through @cloudflare/vite-plugin, so local dev runs on workerd, the same runtime as production. Rsbuild and Rspack projects get the same loop through @lunora/rspack, and a standalone CLI covers everything else.
pnpm dlx lunorash@alpha init my-app
cd my-app
pnpm install
pnpm devAlpha. The npm package is
lunorashbecause the unscopedlunoraname is taken; the CLI it installs is still calledlunora. Install from the@alphadist-tag and expect breaking changes until the first stable release (see Status).
Prefer managed hosting, or just want one email when v1 is stable? Join the Lunora Cloud waitlist →
A fresh app has three files you edit:
// lunora/schema.ts
import { defineSchema, defineTable, v } from "lunorash/server";
export default defineSchema({
messages: defineTable({
author: v.string(),
body: v.string(),
ts: v.number(),
}),
});// lunora/messages.ts
import { mutation, query, v } from "./_generated/server";
export const list = query.query(async ({ ctx }) => ctx.db.query("messages").order("desc").take(50));
export const send = mutation.input({ author: v.string(), body: v.string() }).mutation(async ({ ctx, args }) => {
await ctx.db.insert("messages", { ...args, ts: Date.now() });
});// src/App.tsx
import { useQuery, useMutation } from "@lunora/react";
import { api } from "../lunora/_generated/api";
export default function App() {
const messages = useQuery(api.messages.list, {}) ?? [];
const { mutate: send } = useMutation(api.messages.send);
return (
<>
<button onClick={() => send({ author: "me", body: "hello" })}>Send</button>
<ul>
{messages.map((m) => (
<li key={m._id}>
{m.author}: {m.body}
</li>
))}
</ul>
</>
);
}pnpm dev boots workerd, generates the client types, starts the Vite dev server, and reloads on every save. Click the button and the list updates in every open tab; the query re-runs on the server and pushes the new result.
lunora init asks which framework to use and defaults to a React SPA. Pass -t to pick one directly: next, nuxt, sveltekit, astro, analog, react-router, tanstack-start-react, tanstack-start-solid, solid-v2, expo, vinext, rspack-react, standalone, and a few more (lunora init --help lists them). Use --vite <framework> for a plain React, Vue, Solid, or Svelte SPA, and lunora init --here to add Lunora to an app you already have.
Types run end to end. The schema, validators, query results, and client hooks come from one source of truth. The dev server regenerates client types on save, so there is no codegen step to forget.
Queries are live. Clients subscribe over WebSocket, and a mutation pushes the change to every subscriber. You don't write cache invalidation.
It runs on your account. Workers, Durable Objects, D1, R2, Queues, and KV all live in your Cloudflare account. The only lock-in is Cloudflare itself.
It grows past one Durable Object. Every app starts on a single Durable Object. When one tenant needs its own, add .shardBy(key) to the function; for geo-replicated reads, add .global(). The rest of the app stays the same.
Features come as add-ons. Auth, mail, storage, scheduling, queues and pub/sub topics, workflows, AI agents, payments, feature flags, rate limiting, and notifications ship as @lunora/* packages or as items you copy in with lunora registry add. Other packages add typed access to Cloudflare Containers, Browser Rendering, Hyperdrive, AI Search, and service bindings to sibling Workers.
Clients aren't limited to JavaScript. There are adapters for React, Vue, Solid, Svelte, Angular, and React Native, and lunora sdk generate writes typed clients for Python, Go, Ruby, Rust, Swift, Java, Kotlin, and Dart (see sdks/).
The core doesn't depend on Cloudflare. The reactive engine (@lunora/shard-engine) talks to a small set of host contracts in @lunora/platform, not to Cloudflare APIs. @lunora/platform-cloudflare implements those contracts for production and @lunora/platform-node for dev and test. A capability matrix marks each feature as native, emulated, or unsupported per host; codegen leaves out what a host can't run and tells you why.
pnpm dev also serves Lunora Studio, a local admin UI for your app. In it you can browse and edit data, run SQL, watch live connections and function metrics, follow logs and traces, and see errors grouped into Issues. Time Travel replays past state, the architecture view draws a diagram from your code's call graph, and the advisors flag security and performance problems in your schema.
Read the Studio deep dive →
| Lunora | Convex | Firebase | Plain Cloudflare | |
|---|---|---|---|---|
| Type-safe end-to-end | Yes | Yes | Partial | DIY |
| Real-time subscriptions | Yes (WS, reactive) | Yes | Yes | DIY |
| Runs on your account | Yes (Cloudflare) | No (managed SaaS) | No (managed SaaS) | Yes |
| Scales past single DO | Yes (.shardBy()) |
n/a | n/a | DIY (manual) |
| Vite-first DX | Yes | n/a | n/a | DIY |
| Feature breadth (auth, mail, storage, scheduler) | Add-ons (alpha) | Broad (built-in) | Broad (built-in) | DIY |
| Cost at idle | ≈ $0 (CF free tier) | Paid | ≈ $0 (Spark tier) | ≈ $0 |
Convex still has more built in than Lunora. What you get in exchange is ownership of the infrastructure and its cost: an idle Lunora app costs nothing, and at scale you pay Cloudflare's prices rather than a SaaS markup.
┌────────────────────────────────────┐
│ Browser / Node / RN client │
│ @lunora/client · @lunora/react │
└─────────────────┬──────────────────┘
│ HTTPS + WebSocket (RPC envelope)
▼
┌────────────────────────────────────┐
│ Vite / Rspack dev (workerd) │
│ @lunora/vite · @lunora/rspack │
│ or standalone: @lunora/cli │
└─────────────────┬──────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ Cloudflare Worker — @lunora/runtime │
│ · parses RPC · auth · routing │
│ · upgrades WS to ShardDO via idFromName(key) │
└───┬──────────┬───────────┬───────────┬──────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌───────┐ ┌────────┐ ┌──────┐ ┌──────────┐
│ Shard │ │Session │ │ D1 │ │R2/Queues │
│ DO │ │ DO │ │ SQL │ │ KV │
│(state)│ │ (auth) │ │ │ │ │
└───────┘ └────────┘ └──────┘ └──────────┘
│
├── SQLite-backed, WebSocket Hibernation API,
│ subscription registry
│
└── @lunora/shard-engine (host-neutral: state, OCC,
CDC, reactivity) over @lunora/platform contracts,
implemented for this target by
@lunora/platform-cloudflare
All packages are published under the @lunora npm scope (except the unscoped umbrella lunorash) and live under packages/.
This table is generated from each package's
package.jsonandproject.json. Runpnpm run generate:packages-listto refresh it.
| Package | Version | Description |
|---|---|---|
@lunora/d1 |
D1 adapter for Lunora .global() tables, wrapping the Sessions API for read-your-writes | |
@lunora/do |
Lunora Durable Objects: ShardDO (SQLite, OCC, hibernated WebSocket subscriptions) and SessionDO | |
@lunora/errors |
Unified error layer for Lunora: one LunoraError base + a central catalog of codes, statuses, and actionable hints, rendered across CLI, overlay, Studio, and the client | |
@lunora/fingerprint |
Zero-dependency, cross-runtime error fingerprinting for Lunora: a stable grouping hash that collapses noisy errors into Issues across the local Studio and the Cloud | |
@lunora/runtime |
Lunora Worker runtime: the RPC router, shard resolver, and query coordinator | |
@lunora/server |
Server primitives for Lunora: defineSchema, defineTable, query, mutation, and action | |
@lunora/sql-store |
Internal dialect-parameterized SQL store core for Lunora .global() backends (D1, PlanetScale) | |
@lunora/values |
Validators for Lunora: the v.* validator suite with end-to-end return-type inference | |
lunorash |
The Lunora umbrella package: one install for the server authoring API, worker runtime, Durable Objects, and the lunora CLI |
| Package | Version | Description |
|---|---|---|
@lunora/angular |
Angular reactive adapter for Lunora — signal-based live queries and mutations | |
@lunora/astro |
Astro integration for Lunora — single-worker composition plus reactive-loader server helpers | |
@lunora/client |
Lunora browser SDK: WebSocket transport, optimistic updates, and an offline mutation queue | |
@lunora/db |
TanStack DB binding: typed, live-synced collections and a durable offline outbox over the Lunora client | |
@lunora/nuxt |
Nuxt module for Lunora — single-worker composition (mounts /_lunora/* into Nitro) plus reactive-loader server helpers | |
@lunora/react |
React hooks for Lunora: useQuery, useMutation, useSubscription, and useAuth | |
@lunora/react-native |
React Native / Expo integration for Lunora: an AsyncStorage-backed client factory, the useQuery/useMutation/useSubscription hooks, and a one-call better-auth Expo client | |
@lunora/solid |
SolidJS adapter for Lunora — live queries, optimistic mutations, and reactive loaders | |
@lunora/studio |
The Lunora Studio: a local admin UI for your schema, data, logs, and advisors | |
@lunora/svelte |
Svelte adapter for Lunora — live stores, optimistic mutations, and reactive loaders | |
@lunora/vue |
Vue adapter for Lunora — live composables, optimistic mutations, and reactive loaders |
| Package | Version | Description |
|---|---|---|
@lunora/cli |
The Lunora CLI: init, dev, deploy, codegen, migrate, seed, doctor, insights, logs, registry, and the rest of the project commands |
| Package | Version | Description |
|---|---|---|
@lunora/codegen |
Code generator for Lunora: emits _generated/{api,server,dataModel}.ts from your schema |
| Package | Version | Description |
|---|---|---|
@lunora/rspack |
The Lunora Rspack plugin: codegen, binding provisioning, and wrangler validation for Rspack projects | |
@lunora/vite |
The Lunora Vite plugin: codegen, type sync, wrangler validation, and an error overlay over @cloudflare/vite-plugin |
| Package | Version | Description |
|---|---|---|
@lunora/config |
Internal shared CLI + Vite config layer for Lunora: wrangler.jsonc validation, binding inference, and .dev.vars scaffolding | |
@lunora/testing |
Testing toolkit for Lunora: an in-memory harness for queries, mutations, and actions |
| Package | Version | Description |
|---|---|---|
@lunora/advisor |
Schema & query lints (splinter-style advisors) for Lunora, feeding the Studio Advisors view |
| Package | Version | Description |
|---|---|---|
@lunora/agent |
Durable AI agents for Lunora: defineAgent compiles a replay-safe tool-loop onto Cloudflare Workflows, with DO SQLite threads and live message subscriptions | |
@lunora/ai |
Workers AI helper for Lunora: provider-agnostic AI SDK access from functions, Workers AI by default | |
@lunora/auth |
Auth for Lunora — a thin better-auth wrapper: email/password, OAuth, plugins, D1-backed | |
@lunora/bindings |
Lightweight Cloudflare binding helpers for Lunora — ctx.kv, ctx.images, ctx.analytics, ctx.pipelines, ctx.vectors, ctx.r2sql, ctx.artifacts, ctx.aiSearch, ctx.analyticsSql — one install, per-binding subpaths | |
@lunora/browser |
Cloudflare Browser Rendering for Lunora: ctx.browser screenshots, PDF, and scraping in actions | |
@lunora/cloudflare-access |
Cloudflare Access (Zero Trust) identity for Lunora — verify the Cf-Access-Jwt-Assertion JWT against your team JWKS and feed the verified identity into ctx.auth / RLS via a resolveIdentity adapter | |
@lunora/container |
Cloudflare Containers for Lunora: defineContainer, generated Container DO classes, and the ctx.containers action surface | |
@lunora/flags |
OpenFeature-based feature flags for Lunora — ctx.flags, useFlag, and a first-class Cloudflare Flagship provider with any OpenFeature provider pluggable | |
@lunora/hyperdrive |
Bring-your-own Postgres/MySQL for Lunora via Cloudflare Hyperdrive: a driver-agnostic, action-only ctx.sql | |
@lunora/mail |
Email for Lunora: Resend adapter, TSX templates, and queue-backed sends | |
@lunora/mcp |
Model Context Protocol server exposing a Lunora deployment to AI agents | |
@lunora/notify |
Multi-channel notifications for Lunora — ctx.notify / ctx.push over @visulima/notification: edge-safe Web Push + FCM, plus chat, in-app inbox and webhook channels, with subscription storage and queue-backed fan-out | |
@lunora/payment |
Provider-agnostic payments for Lunora: Stripe-first adapter, webhook sync, and subscription/payment state machine | |
@lunora/queue |
Cloudflare Queues for Lunora: defineQueue producers + consumers, the ctx.queues surface, and the generated queue() worker handler | |
@lunora/ratelimit |
Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, pluggable stores, and procedure middleware | |
@lunora/replica |
Local-first replica runtime + local SQLite mirror for Lunora | |
@lunora/scheduler |
Scheduling for Lunora: runAfter / runAt and Cron Triggers via SchedulerDO | |
@lunora/seed |
Schema-driven, deterministic database seeding for Lunora: realistic fake data from defineSchema | |
@lunora/storage |
R2-backed storage for Lunora: typed buckets and signed URLs | |
@lunora/workflow |
Durable workflows for Lunora: defineWorkflow over Cloudflare Workflows, generated WorkflowEntrypoint classes, and the ctx.workflows surface |
| Package | Version | Description |
|---|---|---|
@lunora/observability |
Host-neutral telemetry storage and read models for Lunora, backing the Studio's Logs, Traces, Metrics and Issues views |
| Package | Version | Description |
|---|---|---|
@lunora/platform |
Provider-neutral host contracts for Lunora: shard/socket/directory/scheduler interfaces, binding projections, and the platform capability matrix | |
@lunora/platform-celld |
celld implementation of the Lunora platform contracts: the Cloudflare host adapters recomposed for celld (self-hosted distributed Durable Objects) under an honest capability matrix | |
@lunora/platform-cloudflare |
Cloudflare implementation of the Lunora platform contracts: ShardHost, SocketHost, ShardDirectory, ShardKvStore over Durable Objects | |
@lunora/platform-node |
Node implementation of the Lunora platform contracts: ShardHost, SocketHost, ShardDirectory, ShardKvStore, SchedulerHost over better-sqlite3 and an in-process socket registry (dev/test target) | |
@lunora/shard-engine |
Host-neutral reactive engine for Lunora: per-shard state, OCC, CDC, reactive subscriptions, and the poke protocol. Consumes @lunora/platform host contracts and can be mounted on any platform host. |
| Package | Version | Description |
|---|---|---|
@lunora/x402 |
Agentic payments (x402) for Lunora: charge agents per request (charge rail) and let your agents pay x402-gated resources (pay rail) |
Runnable apps live in examples/: a todo app, team chat, a kanban board, realtime cursors, chess, a blog, a feedback board, TanStack Start, Expo, and focused demos for auth, payments, notifications, offline rejections, and service bindings.
The full docs are at lunora.sh/docs: guides, concepts, framework adapters, and a reference for each package. Good places to start:
- Getting started: scaffold an app and run the dev loop.
- Queries, mutations & actions: how you write backend code.
- Real-time, sharding, and RLS: how data reaches clients, scales out, and stays scoped to the right user.
- Architecture: how the Worker, Durable Objects, and storage fit together.
- Design boundaries: what Lunora deliberately doesn't do, and the escape hatch for each.
- Deployment: shipping to your own Cloudflare account.
- Packages: every
@lunora/*adapter and add-on.
Lunora is in alpha, and the APIs will break. Every package publishes continuously to the alpha dist-tag on npm (pnpm add lunorash@alpha). Each package has its own version, so the alpha numbers don't line up across packages. The latest tag only holds a 0.0.x placeholder; run npm view <pkg> dist-tags to see the real version. The API, the package boundaries, and the on-disk layout will all change before the first stable release.
New versions ship often enough to trip pnpm's minimumReleaseAge. If you use that setting, add "@lunora/*" and lunorash to minimumReleaseAgeExclude; otherwise pnpm can resolve a mix of packages that no longer match.
Read the code, file issues, and open PRs against the alpha branch. Just don't build a production system on it yet.
Start with .github/CONTRIBUTING.md. The default branch is alpha, and PRs go there unless you are cutting a release.
For security reports, see SECURITY.md. For community guidelines, see .github/CODE_OF_CONDUCT.md. For brand assets and usage rules, see marketing/BRAND.md.
FSL-1.1-Apache-2.0 © 2026 anolilab and contributors. The source is available now, and each release becomes Apache-2.0 two years after it ships.