Rindle is in private beta. You need access to the rocicorp/rindle repo to follow these instructions. Email aa@roci.dev to get access.
With Agents
Paste a prompt like this into your agent:
Build me a live polling app with Rindle. Use the skill https://rindle-docs.preview.rocicorp.dev/skill.md.
One page creates a poll with a question and 2–5 options and gives me a share link. Anyone with the link sees the poll, votes once, and watches the counts update live, without refreshing. Render the pages on the server so the poll shows up on first load, then stays live.
Build me multiplayer chess with Rindle. Use the skill https://rindle-docs.preview.rocicorp.dev/skill.md.
I create a game and get a link to send a friend, and we each see the board update the moment the other moves. The server enforces the rules: it accepts a move only if it is legal and it is that player’s turn, so nobody can cheat by calling the API directly. I can also choose to play against an AI instead, which moves through the same server rules. Show check, checkmate, and the move list.
Build me a Slack-style team chat with Rindle. Use the skill https://rindle-docs.preview.rocicorp.dev/skill.md.
Channels, messages, threads, and emoji reactions, all updating live, with an unread count on each channel. Private channels and direct messages are visible only to their members, and the server checks membership on every read and write.
Build me a live auction site with Rindle. Use the skill https://rindle-docs.preview.rocicorp.dev/skill.md.
Sellers list an item with a starting price and an end time, and bidders watch the price and bid history update live. The server decides which bids count: a bid must beat the current high bid and arrive before the auction closes, so two people bidding at once can’t both win.
Build me a live trivia game with Rindle, like Kahoot. Use the skill https://rindle-docs.preview.rocicorp.dev/skill.md.
A host creates a quiz and shares a join code, and players join from their phones. The host moves through the questions, everyone answers against a countdown, and the leaderboard updates live. The server scores the answers, and players never receive the correct answer before the question closes.
Agents can also read these docs as plain Markdown.
By Hand
Download the rindle binary:
gh release download dev -R rocicorp/rindle -p install.sh -O - | shStart the database. It writes its RINDLE_URL to .env:
./rindle upIn another terminal, install the JS client:
gh release download dev -R rocicorp/rindle -p install.sh -O - | sh -s -- clientThen, from any app:
import { createReactiveSqlClient } from "@rindle/reactive-sql-client";
const db = await createReactiveSqlClient({ rindle: { url: process.env.RINDLE_URL } });
// Applied once, by name. Re-running is a no-op; the live engine picks the change up.
await db.migrate("0001_init", `
CREATE TABLE issue (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, closed INTEGER);
CREATE TABLE comment (id INTEGER PRIMARY KEY AUTOINCREMENT, issue_id INTEGER, body TEXT);
`);
// Queries are trees -- `WITH` creates a subquery.
type Issue = { id: number; title: string; comments: { body: string }[] };
const open = await db.materialize<Issue>(`
SELECT * FROM issue WHERE closed = 0 ORDER BY id DESC
WITH comments OF comment ON issue_id = id (ORDER BY id DESC LIMIT 10)
`);
// Fires now, then after every commit that affects it.
// In React, useLiveQuery from @rindle/react does this for you.
open.subscribe((rows) => {
console.clear();
console.table(rows.map(({ id, title, comments }) => ({
id, title, comments: comments.length, newest: comments[0]?.body,
})));
});
// Plain SQL writes. Every copy writes to the same database, so AUTOINCREMENT ids never collide.
const { result } = await db.sql.execute(`INSERT INTO issue (title, closed) VALUES ('first', 0)`);
const issueId = result.lastInsertRowid;
const comments = Array.from({ length: 1000 }, (_, i) => ({
sql: `INSERT INTO comment (issue_id, body) VALUES (?, ?)`,
args: [issueId, `comment ${i}`],
}));
// Twenty to a transaction. Fifty commits land; the view never holds more than ten.
for (let i = 0; i < comments.length; i += 20) await db.sql.batch(comments.slice(i, i + 20));Save that as app.ts and run it in two terminals:
node --env-file=.env app.tsThe second copy sees the first one’s writes live, and its view never holds more than ten comments.
Next, see the demos.