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

The link you send a friend: a shared world is shared by pasting a URL #35

Merged opened this

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

Merged

Pasted into a chat, drift now previews as the sea instead of a bare domain. `static/icon.svg` is a real icon (a wake, a mote, the bloom it carries) linked by content through `asset()`, the empty `data:,` is gone, and `GET /icon-180.png` gives a phone home screen a sized bitmap. `GET /card.png` is a 1200×630 card the app draws at boot in `src/card.ts` — the arena square, three wakes braided into on…

shipped production is serving it deploy feb7d92

opened this

Pasted into a chat, drift now previews as the sea instead of a bare domain. static/icon.svg is a real icon (a wake, a mote, the bloom it carries) linked by content through asset(), the empty data:, is gone, and GET /icon-180.png gives a phone home screen a sized bitmap. GET /card.png is a 1200×630 card the app draws at boot in src/card.ts — the arena square, three wakes braided into one lane, the bloom going through the gate on the rim — so it can never contain a player's name; both images are addressed by the hash of their own bytes (/card.png?v=0f2c8dd2) and immutable for a year there. src/png.ts turns pixels into a PNG in about a hundred lines (signature, IHDR, one deflated IDAT, adaptive row filters) on node:zlib, which Bun ships: no dependency was added.

src/meta.ts owns the head's social half and robots.txt. Every address it builds — canonical, og:url, og:image — comes from publicOrigin, and Host, X-Forwarded-Host and Forwarded are never read; a test drives / with five spoofed host headers and asserts evil.example appears nowhere in the page. robots.txt leaves the page crawlable and closes /events, /mcp and /__forge/, the other half of joining being a socket event. The head grew ~700 bytes and nothing in it blocks rendering: / is 3.7 KB and answers in under a millisecond warm, and the card is drawn just after the socket opens (~220 ms, once) so no request waits for pixels.

Worth a look in the preview: /card.png itself, and the page source's head order — stylesheet first, the card tags after. Two things I chose not to do: the card carries no text, because rendering the rule sentence into a bitmap means shipping a font or a pixel alphabet, and every unfurler already shows og:title and og:description as real text beside the image; and there is no live render of the current round, which would leak who is playing and be blank most of the time. bun test is green (28 tests, including a round trip through the encoder checked against Bun's own CRC-32).

🤖 Generated with Claude Code

Closes #28

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs