01
Server-authoritative
Queries and mutations are the source of truth. The client never writes to tables. Re-check ownership in every mutation. The runtime preserves ordinary control flow in the server sandbox, so the contract stays the contract.
Luminaweb is an agent-native runtime for full-stack TypeScript apps called
capsules. One directory, one port, one command.
No docker. No yaml. No 3am deploys.
$ npm i -g luminaweb
$ luminaweb new my-app --template todo
$ cd my-app && luminaweb dev
▌ ready on http://localhost:3000 · 14ms cold start
You write a capsule() with a schema, queries, mutations, and a Preact UI.
Luminaweb handles the rest: server authority, routing, persistence, deploys, the boring parts.
The runtime is small enough to read in a sitting. It stays out of your way. No framework churn, no opinion on your folder structure.
The capsule lives in one directory. A server contract, a Preact client, a
shared/ folder for types, and an optional .env.luminaweb.server
for secrets. No build config. No router config. No bundler config.
my-app/
├── server/
│ └── index.ts // capsule, schema, queries, mutations
├── client/
│ └── index.tsx // Preact UI
├── shared/
│ └── todo.ts // pure types & helpers
├── .env.luminaweb.server // server-only secrets
└── package.jsonimport { boolean, capsule, mutation, query, string, table } from "@luminaweb/runtime/server";
export default capsule({
schema: {
todos: table({
text: string(),
done: boolean(),
ownerId: string(),
}),
},
queries: {
todos: query((ctx) =>
ctx.db.todos.where("ownerId", ctx.auth.userId).all(),
),
},
mutations: {
addTodo: mutation((ctx, text: string) => {
ctx.db.todos.insert({ text, done: false, ownerId: ctx.auth.userId });
}),
},
});
why it works
01
Queries and mutations are the source of truth. The client never writes to tables. Re-check ownership in every mutation. The runtime preserves ordinary control flow in the server sandbox, so the contract stays the contract.
02
Local dev writes to a real SQLite file. Edge deploys get a managed SQLite. No in-memory resets. The data outlives the process.
03
The client, the server, and the static SPA all live on the same origin. One deploy artifact, one process, one URL. No CORS archaeology.
04
useQuery<Todo[]>("todos") is end-to-end typed.
Rename a query and the client errors in the editor, not in production.
05
luminaweb deploy pushes your capsule to the Luminaweb Edge.
Anonymous deploys go through in seconds. Claim for stable subdomains.
06
luminaweb db dump, luminaweb logs, luminaweb inspect.
The CLI reads .luminaweb/deploy.json and sends the claim token
automatically. No portal, no click-ops.
deploy
my-app/luminaweb build --target edge
bundle server.mjs + client/bundle.js
luminaweb deploy
push to edge.luminaweb.app
luminaweb claim
opt in for env, fetch, and subdomains
https://my-app.luminaweb.app