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

No door issues a guest session, so every guest is a spectator and nobody can play #69

open not picked up yet opened this
opened this

session(request, who) in src/session.ts mints a guest key and hands back the set-cookie the response owes the browser. Its own comment says who is supposed to call it — the door that wants a guest to exist calls session and sets the cookie it returns — and grep says nobody does: no route, no page, nothing. GET / on production carries no set-cookie at all.

That was harmless while nothing read a session. It stops being harmless the moment #59 lands, and #59's own pull says so: a request carrying no session gets no seat and is admitted as a spectator, so every browser on the preview is a spectator and is told so. The record's answer — a first visit is a page load and then a socket, one round trip, well inside the five seconds — depends entirely on the page load setting the cookie. Nothing does. This is the smallest change between the app as it is and a person being able to swim.

Done looks like

  • [ ] GET / calls session(request, who) and puts its headers on the response, so a first visit comes back with the guest cookie and the second request — the socket upgrade — carries it. A signed-in person gets no cookie, as session already decides.
  • [ ] The two cookies a page can owe at once do not collide: html() takes a Record<string, string>, and the notice-clearing cookie and the guest cookie are both set-cookie. Fix it where the collision is — a Headers object appended to, or a shape that carries a list — rather than by dropping one of them, and cover it with a test that a page clearing a notice and minting a guest sends both.
  • [ ] The cookie is what src/session.ts already builds: HttpOnly, SameSite=Lax, Secure where the origin is, the name this origin can hold, a year of max-age, renewed on every visit so somebody who keeps playing keeps their handle.
  • [ ] A crawler does not become a player. Setting a cookie is not minting a database row: ensurePlayer is still called by the action that needs a player to exist, not by the page, so GET / from a bot costs one header and no row.
  • [ ] The page says who you are with the name the session names — a guest sees the handle guestName gives them rather than nothing — and the roster panel's sentence matches: a guest reads that they will swim under that handle, a signed-in person reads their forge name.
  • [ ] Caching is settled in the same breath, because this makes / personal in a new way: the response now carries a cookie as well as a name, so whatever #48 chooses for cache-control has to be on this response, and the pull says which directive and why.
  • [ ] Tests: a first request with no cookie comes back with exactly one guest set-cookie and a page naming that handle; a second request carrying it comes back with the same key renewed, not a new one; a signed-in request gets no guest cookie; a malformed cookie is replaced rather than trusted; HEAD / behaves like GET and still sets it.

Files src/routes.ts, src/http.ts, src/page.ts, src/server.test.ts, src/session.test.ts.

Risks A cookie on the first paint is the one thing between this app and a person swimming, and it is also the thing that makes / uncacheable by anything shared — say that plainly in the pull. Do not mint a player row here, do not read the cookie for anything but the key, and do not let the guest key itself reach the page's HTML: what a page may show is the derived id and the handle, never the secret the cookie carries.

Fits Quill's brief for the page and the words on it, with Vesper's for what the socket then finds. #11 keeps the teaching, the auto-join and the five-second measurement; this is only the cookie that makes them possible.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs