Public: anyone can read this; the crew can change it.

A board link does not end up in a search index: x-robots-tag, a robots.txt, and a sentence in the README #117

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

A board's id is its access control, so a board must not end up in a search index. New `src/robots.ts` holds the one list of paths crawlers are asked to stay out of — `/b/` and `/mcp` — and three places read it: `src/server.ts` sets `x-robots-tag: noindex, nofollow, noarchive` on every response under those paths, right beside the security floor (so the board page, its `/events` stream and its JSON…

shipped production is serving it deploy fc047f8

opened this

A board's id is its access control, so a board must not end up in a search index. New src/robots.ts holds the one list of paths crawlers are asked to stay out of — /b/ and /mcp — and three places read it: src/server.ts sets x-robots-tag: noindex, nofollow, noarchive on every response under those paths, right beside the security floor (so the board page, its /events stream and its JSON all carry it, and a new route under /b/ carries it the day it's written); src/routes.ts answers GET /robots.txt from the same list as text/plain with a day-long cache, prefixes only and never a board id; src/pages/board.ts repeats it as <meta name="robots"> for the crawler that reads HTML and not headers. / and /static/* are left indexable on purpose — the front door names no board — and the comment next to the header says so.

This is the accident closed, not the hole, and nothing in the code, comments or README pretends otherwise: a crawler that ignores the request, and anyone who has the link, still has the board. The stronger thing (a board that requires being signed in and named on it) would be a different app and is not half-built here. The README's "What's simulated" gains a paragraph saying exactly that.

Worth a look: withFloor in src/server.ts now takes the request path, which touched every call site including the 403/413 early returns and the error handler (which has no path and passes ""). And newBoard() in src/server.test.ts now resets the process-wide creation bucket and retries once on a 429 — that file already created boards right up to the 30-a-minute limit, so the two tests added here tipped two unrelated ones over; the refusal itself is still tested in src/limits.ts's own tests, untouched. New tests: a board page, its stream, a board JSON write and a /b/ 404 all carry the header; / and /static/* don't; /robots.txt has both Disallow lines and no board id. bun test: 196 pass. No new dependencies. Try it in the preview with curl -sI <preview>/b/<id> and curl -s <preview>/robots.txt.

🤖 Generated with Claude Code

Closes #58

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs