# Setting it up

From nothing to your own live site.
About twenty minutes, most of it waiting for things to finish.

Want your own name, logo and domain? Run `npm run rebrand` first (Part G).

You need:

- A Mac or PC with **Node 20 or newer** ([nodejs.org](https://nodejs.org), the LTS button).
- A **Cloudflare account**. Free to make.
- A card for Cloudflare. R2 storage asks for one even on its free tier.

Every command below runs in a terminal, inside the project folder.

---

## Part A. Try it on your own laptop first

Five minutes. Nothing goes online.

**1. Install.**

```bash
npm install
```

**2. Tell it who the owner is.** Copy the example settings file:

```bash
cp .dev.vars.example .dev.vars
```

Open `.dev.vars` and put your email after `OWNER_EMAIL=`.

**3. Build the local database.**

```bash
npm run db:migrate:local
```

**4. Start it.**

```bash
npm run dev
```

**5. Open** [http://localhost:8787/app/](http://localhost:8787/app/).
Make your account with the email from step 2.
Press **Take the tour**, then **Present**.
Open the join link on your phone (same wifi) or in another browser window.

Stop it with `Ctrl` + `C`.

---

## Part B. Put it online

### B1. Make a Cloudflare account

Go to [dash.cloudflare.com/sign-up](https://dash.cloudflare.com/sign-up).
Confirm your email.

### B2. Switch on R2

R2 is where slides, pictures and the room's photos live.
Cloudflare asks you to switch it on once.

1. In the [Cloudflare dashboard](https://dash.cloudflare.com), find **R2 Object Storage** in the left menu.
   It may be inside **Storage & databases**.
2. Press **Purchase R2 Plan**. The free tier is 0 dollars.
3. Add a card if asked, and confirm.

Nothing is charged unless you go far past the free tier.
At the time of writing that is 10 GB stored, and millions of reads a month.

### B3. Run the setup

```bash
npm run setup
```

What happens:

1. It installs what it needs, if Part A did not already.
2. A browser window opens. Sign in to Cloudflare and press **Allow**.
   If your login has more than one Cloudflare account, it asks which one.
3. It makes the database, the storage bucket and the tables.
4. It publishes the site.
5. It asks for **your email**. That account becomes the owner.

It remembers the site in `sites.json`, so later commands know where it is.
At the end it prints your address. It looks like:

```
https://{{slug}}.your-name.workers.dev
```

If anything stops it, it says why and what to do.
Fix that one thing and run `npm run setup` again.
It skips whatever is already done.

On a brand new Cloudflare account it may stop at publishing and ask you to
pick a **workers.dev subdomain** first. That is one screen:
dashboard, **Workers & Pages**, type any name you like, confirm.
Then run `npm run setup` again.

### B4. Make your account

Open `https://YOUR-ADDRESS/app/#/signup`.
Sign up with the **same email** you gave the setup.
You are the owner.

### B5. Check everything is connected

In the app, open **Admin** (top bar).
The first three lines must be green:

| Line | If it is not green |
| --- | --- |
| Database | Run `npm run db:migrate`, then reload. |
| File storage | Check R2 is switched on (B2), then `npm run setup` again. |
| Live rooms | Run `npm run deploy` again. |

The others are optional.

### B6. A real test

1. Press **Take the tour** on your Presentations page.
2. Press **Present**. The stage opens in a new window.
3. Scan the join code with your phone.
4. Press the right arrow on the stage. Your phone follows.
5. Answer on your phone. The screen counts it.

That is the whole thing working.

---

## Part C. Before your first real talk

### C1. Upgrade to Workers Paid (recommended)

The free plan is fine for trying things.
For a real audience, switch to **Workers Paid**: 5 dollars a month.

Why: the free plan allows each request only a few milliseconds of work.
Signing in (the password check) and saving a big deck can go past that.
Paid also raises every other limit a full room might touch.

How: dashboard, **Workers & Pages**, **Plans**, choose **Paid**.

> On the free plan, if signing in fails with **error 1102**:
> open `wrangler.toml`, find `# PBKDF2_ITERATIONS = "20000"`, delete the `#`,
> and run `npm run deploy`. Upgrading is the better fix.

### C2. Your own address (optional)

Want `su.mr` or `talk.yourname.com` instead of `workers.dev`? See Part H,
"Your domain". In short: put the domain on Cloudflare, in the same account
as the site, and the setup attaches it for you.

### C3. Password reset emails (optional)

Without email, a locked-out presenter asks you, and you make them a reset
link in **Admin**, under **Presenters**, then the menu next to their name.

To send reset emails on their own:

1. Make a free account at [resend.com](https://resend.com).
2. Add and verify your domain there (it shows the DNS records to add).
3. Make an API key.
4. Store it, and the sender address:

```bash
npx wrangler secret put RESEND_API_KEY
npx wrangler secret put MAIL_FROM
```

For `MAIL_FROM` type something like `{{name}} <hello@yourname.com>`.

### C4. Invite other presenters

New sites only let in people with an invite.
**Admin**, **Invites**, **Make an invite link**. Send the link.
Or switch **Who can sign up** to **Anybody**.

---

## Part D. Updating later

When there is a new version of the code:

```bash
npm install
npm run deploy
```

It asks which site, if you have more than one, brings that site's tables up
to date (only adding what is new, never touching your decks), then publishes.
`npm run deploy -- --all` updates every site, one after another.

---

## Part E. What it costs

For one talk to a hundred people, a few cents at most, usually nothing:

- Slides, pictures and the app itself come from Cloudflare's edge for free.
- Each phone holds one connection to the live room. Answers and reactions
  are small messages on it, counted twenty to one by Cloudflare.
- Phones that cannot hold that connection fall back to asking once every
  couple of seconds, and those answers are cached at the edge for a second,
  so a whole room costs about one request a second.

Workers Paid (5 dollars a month) covers far more than one presenter will use.

---

## Part F. Running a beta

1. Keep sign ups on **invite**. In the app, **Admin**, **Invites**: make one
   link per tester, or one link with several uses for a group.
2. Ask each tester to try the things that differ from talk to talk:
   - their own deck: a PDF, a Keynote HTML export, or a deck exported as a video;
   - a few questions, presented to real phones on a real wifi;
   - their own AI, with a key from **Your AI**.
3. Watch it live while they test:

   ```bash
   npm run tail
   ```

   Every request is one line. If anything breaks on a screen (the stage, a
   phone, the console, the app), the screen reports it, and it shows up here
   as a `client.error` line saying what failed and on which page.
4. **Admin**, **Is everything connected** says whether the database, files
   and live rooms are all answering.

Nothing a tester does is lost: every save is a version, deleted decks wait in
the trash, and only a person signed in to the app can delete anything for good.

---

## Part G. Your own name, logo and domain

Everything your audience and your presenters see takes its name from one
file, `brand.json`. To change it:

```bash
npm run rebrand
```

It asks for each thing, showing what it is now. Press Enter to keep one.

1. **The name**, written the way you want it seen. For example `sumr`.
2. **A short name** for web addresses and Cloudflare. Lower case, no spaces.
3. **The tagline**. For example `add sunshine to your presentations`.
4. **Your domain**, if you have one. For example `su.mr`.
5. **The logo**: drag an SVG or PNG file into the terminal. Any size: the
   bigger, the crisper.
6. **The tab icon**: a square picture, or Enter to use the logo. A wide logo
   squeezed into a tab is hard to read, so a square icon is better.
7. **Does the logo spell the name?** Say yes for a wordmark (the name,
   drawn), and pages show the logo alone. Say no for a symbol, and pages
   write the name next to it.

It puts the new name in page titles and link previews, the logo on every
page, the guides, what your AI reads, and the names a new site gets.

The pictures are made ready for the web: trimmed to their edges, made small
(a few megabytes from a designer become about 60 KB), every icon size
browsers and phones ask for, and a picture for link previews when somebody
shares a join link in a chat. Then:

- see it on your laptop: `npm run dev`
- show it on your site: `npm run deploy`

Run it again whenever you like. It always starts from `brand.json`, so it
knows the current name, whatever it is now.

Keys your AI already has keep working; new keys start with the new short
name. A few things keep the old name inside, where nobody sees them (a
header the app sends itself, the deck file format, cookie names): changing
those would break saved decks, keys and sign ins.

---

## Part H. More than one site

The same code can run several sites: a test site and the real one, each with
its own presenters, decks and address. Each is its own Cloudflare Worker,
database, file storage and live rooms; sites on one account share only that
account's plan. `sites.json` lists them, and each command asks which one.

### A new site, on another Cloudflare account

For the launch, say, on an account of its own:

1. Rebrand first (Part G), so it starts with the right name.
2. Make the new Cloudflare account and switch on R2 there (B1, B2).
3. Run `npm run setup` and choose **Set up a new site**.
4. Choose **another Cloudflare login**. A browser opens: sign in as the new
   account.
5. It asks for the site's name (Enter takes your short name), and whether to
   put it at your domain.
6. It makes the database, storage and tables there, publishes the site, and
   asks for the owner email. Then B4 to B6, on the new address.

Your test site carries on as it was. From then on `npm run deploy` asks
which site, and each command checks you are signed in as the login that owns
it, offering to switch if not.

Tip: in the new account, invite your usual login as a member (Manage
Account, then Members). One login then reaches both, and nothing asks you to
switch.

If it stops halfway (R2 not switched on, say), fix what it says and run
`npm run setup` again. Choose **Finish setting up**: it carries on from
where it stopped.

### Your domain

To put a site at `su.mr`, the domain has to be a website in the same
Cloudflare account as that site:

1. In the dashboard, **Add a domain**, type `su.mr`, choose the free plan.
2. Where you bought the domain, change its **nameservers** to the two
   Cloudflare shows you.
3. Wait until Cloudflare says **Active**. Minutes, sometimes a few hours.
4. Run `npm run setup`. A new site offers to go at your domain. For a site
   you already have, choose it and say yes when it offers the domain.

It sets up the address and its HTTPS for you. Not Active yet? Setup offers
to publish without the domain for now. Run it again once it is Active,
choose the site, and it attaches the domain. Phones then join at
`su.mr/K7M4P`.

---

## Part I. The offline app

**{{name}} Offline** runs a room on a presenter's own computer when the
venue has no internet ([Offline mode](OFFLINE.md)). It needs your site
to have offline mode, so deploy first (Part D).

### Try it on this Mac

```bash
npm run desktop
```

It builds the app and opens it. Link it to your site, copy a deck, start
the room. Nothing is signed: it is for this Mac.

### Make it for other people

So it opens on any Mac without warnings, it is signed with your Apple
Developer ID and notarized by Apple. Once:

1. In Xcode, **Settings**, **Accounts**, **Manage Certificates**, add a
   **Developer ID Application** certificate (or make one at
   developer.apple.com). Check it is there:
   `security find-identity -v -p codesigning`.
2. At [account.apple.com](https://account.apple.com), **Sign-In and
   Security**, **App-Specific Passwords**, make one called "notarize".
3. Find your Team ID at developer.apple.com, **Membership**.

Then, with your own values:

```bash
APPLE_ID=you@example.com APPLE_APP_SPECIFIC_PASSWORD=abcd-efgh-ijkl-mnop APPLE_TEAM_ID=ABCDE12345 npm run desktop:dist
```

The first time, the Mac asks whether codesign may use your key:
**Always Allow**. Signing and notarizing take a few minutes. The disk
image lands in `desktop/dist/`, about 185 MB.

### Windows

```bash
npm run desktop:dist -- --win
```

It makes a Windows installer (x64) in `desktop/dist/`. Unsigned, Windows
warns before it runs; a code signing certificate (or Microsoft's Trusted
Signing) removes the warning. Test it on a real Windows computer before
handing it out.

---

## When something is wrong

**The site shows "Nothing here."** You opened a path that does not exist. The app is at `/app/`.

**Sign up says "You need an invite link".** Sign up with the owner email first. Then make invites.

**Sign up says "The owner has to sign up first".** Someone tried before you did. Sign up with the owner email.

**Error 1102 when signing in.** See C1.

**Setup could not attach your domain.** The domain is not a website in that
Cloudflare account yet, or its nameservers are still changing. Say yes to
publishing without it for now, then see Part H.

**"Already has externally managed DNS records" (code 100117).** The domain
is on Cloudflare, but an old address record is using the name, usually left
by the company you bought it from. In the dashboard, open the domain, then
**DNS**, **Records**. Delete the A, AAAA and CNAME records named after the
domain (shown as the domain or `@`). Keep MX and TXT records: they are for
email. Then run `npm run setup` again and pick your site. Your site stays up
on its workers.dev address meanwhile.

**Setup stopped halfway.** Fix what it says, run `npm run setup` again and
choose **Finish setting up**.

**Deploy or setup is waiting a long time.** Each step shows Cloudflare's own
lines as they come, and "still waiting for Cloudflare" every 15 seconds. A
connection can go silent: after a minute (tables) or three (publishing) it
is stopped and tried once more. If that also goes quiet, it stops and says
so. Check your internet connection and run it again; every step checks
before it acts.

**Setup says it needs a workers.dev subdomain.** See B3.

**Admin shows "File storage" red.** R2 is not switched on, or the bucket is missing. B2, then `npm run setup`.

**Phones say "There is no live session with that code".** The session ended, or the code is mistyped. Press **Present** again.

**Locked out, no email set up.** From your laptop:

```bash
npm run owner:reset-link -- you@example.com
```

It asks which site, if you have several, and prints a link that works once,
for an hour.

**Watching what the site does, live:**

```bash
npm run tail
```

Every request and every problem prints one line.
