# Get Started

Rindle is in private beta. You need access to the [rocicorp/rindle](https://github.com/rocicorp/rindle) repo to follow these instructions. Email
[aa@roci.dev](mailto:aa@roci.dev?subject=Rindle%20beta%20access) to get access.

## With Agents

Paste a prompt like this into your agent:

Prompt for your agent (Polls):

```text
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.
```

Prompt for your agent (Chess):

```text
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.
```

Prompt for your agent (Team chat):

```text
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.
```

Prompt for your agent (Auction):

```text
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.
```

Prompt for your agent (Trivia):

```text
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](https://rindle-docs.preview.rocicorp.dev/agents.md).

## By Hand

Download the `rindle` binary:

```bash
gh release download dev -R rocicorp/rindle -p install.sh -O - | sh
```

Start the database. It writes its `RINDLE_URL` to `.env`:

```bash
./rindle up
```

In another terminal, install the JS client:

```bash
gh release download dev -R rocicorp/rindle -p install.sh -O - | sh -s -- client
```

Then, from any app:

```typescript
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:

```bash
node --env-file=.env app.ts
```

The second copy sees the first one's writes live, and its view never holds more than ten comments.

Next, see the [demos](https://rindle-docs.preview.rocicorp.dev/demos.md).
