The synced-app quickstart builds a Rindle app by
hand so you can see every seam. create-rindle is the shortcut after that: it
generates the same three-tier shape as a small, running TanStack Start
app.
Use it when you want a project to edit instead of copy-pasting the quickstart. The template is intentionally small - a rooms-and-messages app with live message counts, optimistic writes, a rejection path, SSR, generated schema, and dev-only Rindle devtools.
Create an app
npm create rindle@latest my-app
# or
pnpm create rindle my-app
# or
npx create-rindle my-app
Then run it:
cd my-app
pnpm dev
pnpm dev runs one lifecycle command:
rindle dev --migrate --gen shared/schema.gen.ts -- vite dev --port 3000
It evaluates the committed rindle.ncl once, then supervises and waits for the write-master,
follower, and fleet edge. It applies migrations and regenerates shared/schema.gen.ts. Then it
starts TanStack Start with RINDLE_URL + RINDLE_DATABASE_TOKEN. Signals and teardown stay
inside that same lifecycle. There is no concurrently, readiness probe, or nested rindle exec.
Open two browser windows and create a room. As you post messages, the room count
updates live in both. Try a room name or message containing "spam" to watch an
optimistic write snap back after the API authority rejects it.
RINDLE_DATABASE_TOKENremains server-only. The browser calls same-origin/api/rindle/*routes. Its first query lease returns the public WebSocket endpoint and an opaque follower-affinity ticket, so no browser topology configuration is needed.
The generated app requires Node >= 22.18.
What it generates
It is the same architecture as the quickstart, packaged as a starter:
| Tier | In the template | What it does |
|---|---|---|
| Browser | src/rindle-client.ts, src/rindle-tanstack.ts, src/components/*.queries.ts, src/routes/* |
Runs createRindleClient, local wasm IVM, optimistic writes, first-class TanStack route readiness/SSR, useRoot/useFragment, and co-located queries/fragments |
| API authority | server/app-api.ts, server/rindle-http.ts, src/routes/api.rindle.*.tsx |
Resolves named queries, runs authoritative SQL mutators through the unified Rindle ingress, and exposes the Rindle API through TanStack Start server routes |
| Data tier | rindle.ncl, migrations/*.sql |
rindle dev renders and supervises the write-master + follower + fleet edge, applies migrations, generates schema, and streams live deltas |
The schema is SQL-first. Edit or add migrations/*.sql. The dev loop applies the
migration to the write master and regenerates shared/schema.gen.ts from the follower’s
introspected schema. A file can be pure DDL (including reviewed destructive drops) or
pure DML for seeds and bounded backfills. Keep the two kinds in separate ordered files.
Keep relationships, normalization, and mutators in shared/app-def.ts.
Why TanStack Start
The starter uses TanStack Start because it gives the template one coherent home for the browser, SSR, and server routes:
src/routes/api.rindle.query.tsx,api.rindle.read.tsx, andapi.rindle.mutate.tsxare the browser-facing API routes.- SSR reads call the same app authority in-process, so first paint and client subscriptions use the same query registry.
@rindle/tanstacklets each file route declare its query once, then owns server preloading, client navigation readiness, cancellation, and the seed-to-live provider handoff.- TanStack Router’s file routes keep app screens and Rindle query modules close without forcing the Rindle APIs themselves to depend on TanStack.
Rindle is not TanStack-specific. The important parts are the three tiers and the contracts: SQL migrations, generated schema, named queries, predicted mutators in the browser, authoritative mutators on the server, and the write-master + follower as the live data tier. TanStack Start is the starter’s application shell.
Where to look first
| File | Why it matters |
|---|---|
migrations/0001_init.sql |
The source-of-truth SQL schema |
shared/schema.gen.ts |
Generated @rindle/client schema; do not hand-edit |
shared/app-def.ts |
Schema re-export, relationships, normalization, and predicted mutators |
src/components/*.queries.ts |
Named root queries and fragments, co-located with UI |
src/rindle-client.ts |
The one browser client setup call |
src/rindle-tanstack.ts |
The shared route-loader/provider integration |
src/ssr.ts |
Server-only first-paint preload helper |
src/devtools.tsx |
Dev-only Rindle devtools panel mount |
server/app-api.ts |
Authoritative query registry, SQL mutators, policy |
rindle.ncl |
The topology rindle dev (local) and rindle deploy (cloud) both read — followers = 1, the colocated pair; loopback in dev |
AGENTS.md |
The rules of the app for coding agents (never edit schema.gen.ts, deterministic mutators, named queries, …) — Claude Code, Cursor, and Codex read it automatically |
Customize it
Treat the generated app as a working baseline:
- Change tables in
migrations/*.sql, then letpnpm devregenerate the typed schema. - Add relationships and mutators in
shared/app-def.ts. - Add named root queries or fragments beside the components that read them.
- Replace the demo auth seam in
shared/auth.tsand the policy inserver/app-api.ts. - If you point the app at Rindle Cloud or a self-hosted fleet, keep
RINDLE_DATABASE_TOKENserver-only.
The same rindle.ncl drives the cloud:
- Run
rindle login. - Run
pnpm rindle:deploy. It provisions (or re-attaches) the Sync plan — a managed master + follower on Rindle’s packed OVH fleet — and records the binding in.rindle/cloud.json. Commit that file. - Run
pnpm rindle:migrate:cloudto pushmigrations/*.sqlthrough the Cloud proxy.
Local dev reads the same authored topology through rindle dev. See
deploy.
For the underlying commands, see @rindle/cli. For the
manual version of the same app shape, keep the quickstart
open beside the generated project.
Next steps
- Synced-app quickstart - the same architecture, built by hand.
- The three-tier architecture - why the browser, API authority, and daemon are separated.
@rindle/cli- therindletoolchain the template uses for the daemon, migrations, and schema generation.- The browser client -
createRindleClient, optimistic mutators, and live reads. - Server rendering - the first-paint preload and live-store handoff.
- Devtools - the dev-only mutation timeline, query inspector, and delta stream.