jevstrudel.git / worker / migrations / 0007_site_address.sql

The site's move from jevstrudel.deizel.workers.dev to strudel.lmjtfy.fun (2026-10-05). Owner: src/accounts-store.ts, for src/auth.ts and src/moved.ts.

A passkey belongs to the host it was made on (its WebAuthn relying party ID): the browser offers it there and nowhere else. So each credential records its host, and the site can tell a signed-in listener whether they have a passkey for the address they are on. Every row older than this migration was made on the workers.dev host in production. A local dev database's older rows were made on localhost and are tagged with the wrong host by this default: its owner is asked once to add a passkey for localhost, which is allowed, since only passkeys of the same host are excluded from a new one.

14ALTER TABLE credentials ADD COLUMN rp_id TEXT NOT NULL DEFAULT 'jevstrudel.deizel.workers.dev'
15  CHECK (length(rp_id) BETWEEN 1 AND 253);

A session carried from the old address to the new one (src/moved.ts): the SHA-256 (hex) of a random token the old host put in its redirect, the account, and the old host's session, which ends when the token is taken. Taken with DELETE … RETURNING, so once, and within a minute.

What is identifying here (migrations/CLAUDE.md): the account's id, for a minute. Never the token, so a read of this table carries no session anywhere.

25CREATE TABLE session_handoffs (
26  token_hash TEXT PRIMARY KEY CHECK (length(token_hash) = 64 AND token_hash NOT GLOB '*[^0-9a-f]*'),
27  user_id TEXT NOT NULL REFERENCES users (id) ON DELETE CASCADE,
28  from_session_hash TEXT NOT NULL CHECK (length(from_session_hash) = 64 AND from_session_hash NOT GLOB '*[^0-9a-f]*'),
29  created_at INTEGER NOT NULL CHECK (created_at > 0),
30  expires_at INTEGER NOT NULL CHECK (expires_at > created_at)
31) STRICT;
32CREATE INDEX session_handoffs_by_expiry ON session_handoffs (expires_at);