# Auth simplification — deployment notes

The code-based login flow (email → 6-digit code → session) has been
replaced by a pure email-based lookup against the invite list. This is a
one-way change — after deploy, the server no longer reads `CODE_PEPPER`,
no longer talks to SMTP, and the `codes` table is dropped on first boot.

Invites now carry an optional first name, used on the welcome screen:
*"Thanks for your interest, Erik."* (or *"Thank you for your interest."*
when no name is on file).

## Files changed

### Replace (drop in over the existing ones)

| File | What changed |
|---|---|
| `server.js` | Drops `initMail()` call, drops the `CODE_PEPPER` fatal check at boot. |
| `src/db.js` | Adds `first_name` column to `invites` (idempotent migration). Drops the `codes` table + its index on startup (idempotent). Updates prepared statements — `getInvite` and `listInvites` return `first_name`; `upsertInvite` takes it as an argument; `deleteInvite` is unchanged. Removes all `codes`-related statements. Cleanup sweep no longer touches `codes`. |
| `src/auth.js` | `POST /auth/request-code` and `POST /auth/verify-code` removed. Single `POST /auth/login` endpoint — returns `200 {ok, firstName}` on success, `403 {error: "not_invited"}` otherwise. Rate limit: 30 per IP per hour. `GET /auth/me` now returns `{email, firstName}`. |
| `src/sessions.js` | Removes `CODE_TTL_MS`, `randomCode`, `hashCode`, `constantTimeEqual`. Keeps `SESSION_TTL_MS`, `COOKIE_NAME`, `randomSessionId`, `issueSession`, `clearSession`, `currentSession`. |
| `bin/invite.js` | `add` accepts an optional first name as a third argument: `node bin/invite.js add erik@example.com Erik`. `list` shows the name in square brackets next to each entry. `remove` unchanged. Re-running `add` on an existing email updates the first name only — `invited_at` and `invited_by` are preserved. |
| `public/entrance.html` | Removes the six code-digit input block and all associated styles (`.code-row`, `.code-cell`, `.quiet`, "use a different email"). Removes step 3's duplicate welcome title markup — the welcome title is now set by JS on step-enter. |
| `public/entrance.js` | Two-step state machine (email → welcome) instead of three. On submit: `POST /auth/login`. Success → set welcome title, advance. `403` → inline "not invited" message in-place. Removes `submitCode()`, code-cell handling, paste/arrow-key logic, the "use different email" button, and the `rememberedEmail` / `submitting` state. |
| `package.json` | Removes `nodemailer` dependency. Bumps version to `0.2.0`. |
| `.env.example` | Removes `CODE_PEPPER`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `MAIL_FROM`. Keeps `PORT`, `PUBLIC_ORIGIN`, `NODE_ENV`. |

### Delete

| File | Why |
|---|---|
| `src/mail.js` | SMTP transport and `sendCode()` — no longer used. |

### Update (minor — you do this yourself)

| File | Action |
|---|---|
| `.env` (on the VPS, at `/etc/fenja/env`) | Remove `CODE_PEPPER=…`, `SMTP_HOST=…`, `SMTP_PORT=…`, `SMTP_USER=…`, `SMTP_PASS=…`, `MAIL_FROM=…`. Keep the rest. |
| `.env` (on your laptop) | Same. |

## Doc updates

I've not touched these — update them when you next edit them:
- `PROJECT.md` — "How auth works" section, "Non-negotiable properties" (drop CODE_PEPPER and SMTP)
- `INSTALL.md` — step 5 (env file), step 0 prerequisites (SMTP relay no longer needed)
- `OPERATIONS.md` — "Editing secrets" section
- `CHECKLIST.md` — section C (email + code form)
- `CLAUDE.md` — references to `CODE_PEPPER`, SMTP, `codes` table, `/auth/request-code`, `/auth/verify-code`

## Deploy sequence

Local first:

```powershell
# 1. Replace the files (see the list above). Delete src/mail.js.

# 2. Update your local .env — remove CODE_PEPPER, SMTP_*, MAIL_FROM lines.

# 3. Clean up node_modules to drop nodemailer:
npm install

# 4. Add first names to existing invites, or invite fresh with names:
node bin/invite.js add you@yourdomain.com Erik

# 5. Walk the flow:
npm run dev
# → http://127.0.0.1:3000
# → type your email → should land on welcome with personalised title
# → click "Learn more" → timeline
# → open a private window, type an unknown email → "not on invite list"
```

Then the VPS (once you're happy):

```bash
# On your laptop — rsync with the usual excludes:
rsync -avz --delete \
  --exclude node_modules --exclude data --exclude .env --exclude .git \
  ./ user@project-bifrost.fenja.ai:/tmp/fenja-upload/

# On the VPS:
sudo rsync -a --delete /tmp/fenja-upload/ /opt/fenja/
sudo rm -f /opt/fenja/src/mail.js     # make sure it's gone after rsync
sudo chown -R fenja:fenja /opt/fenja
rm -rf /tmp/fenja-upload

cd /opt/fenja
sudo -u fenja npm ci --omit=dev       # drops nodemailer from node_modules

# Edit the VPS .env to remove CODE_PEPPER and SMTP_*:
sudo nano /etc/fenja/env

sudo systemctl restart fenja
sudo journalctl -u fenja -n 20
# Expect: just "[bifrost] listening on 127.0.0.1:3000"
# No more "[mail] SMTP relay reachable" line — the whole SMTP stack is gone.
```

First time the server boots, the migration runs automatically:
- adds `first_name` column to `invites`
- drops the `codes` table

Both are idempotent — if you roll back and deploy again it's a no-op.

## Spot-check after deploy

- [ ] `curl -I https://project-bifrost.fenja.ai/` still returns 200 with all security headers.
- [ ] A non-existent session cookie → entrance page with the email field.
- [ ] Type an invited email → advances to welcome with *"Thanks for your interest, [Name]."* if the invite has a name, or *"Thank you for your interest."* if not.
- [ ] Type an uninvited email → inline red "This email is not on the invite list." on the email input.
- [ ] Click "Learn more about Project Bifrost" → `/timeline`.
- [ ] Refresh `/` while logged in → land directly on the welcome step with the personalised title (served from `/auth/me`).
- [ ] `node bin/invite.js list` on the VPS shows the existing `quka93@gmail.com` with no name in square brackets (NULL is fine — legacy row).
- [ ] `node bin/invite.js add someone@example.com Alice` shows `Invited someone@example.com (Alice)`.
- [ ] DevTools network tab: no 404s, no CSP violations.

## Rollback, if needed

The migration is not trivially reversible — `codes` was dropped. If you
need to roll back, restore from the nightly SQLite backup at
`/opt/fenja/data/backup-YYYY-MM-DD.sqlite`, and redeploy the previous
code. Invites with `first_name` values stay in the backup but will be
ignored by the old `src/db.js` that doesn't select that column — no harm
done.

## What I deliberately did NOT do

- Didn't remove the `rate_limits` table — still used by the login endpoint.
- Didn't change `src/middleware.js` — `rateLimit()` and `requireAuth()` behaviour is unchanged.
- Didn't change anything inside `protected/` — the timeline, archive, and Project Bifrost scenes are all untouched.
- Didn't change Nginx config or the systemd service unit — no operational changes.
- Didn't add an "admin web UI" or `/auth/invite` HTTP endpoint — invites still happen only via the CLI, as before.
