The site's address. It is https://strudel.lmjtfy.fun (wrangler.json's custom domain, SITE_HOST); it was the Worker's own workers.dev host until 2026-10-05, and that host still reaches this Worker, which answers there with nothing but the way to the new address:
everything 301 (GET, HEAD) or 308 to the same path and query
on SITE_HOST
/sw.js a service worker that removes itself. A returning
visitor's browser holds the old site in its cache
and would never ask for a page, so never see the
redirect; it does ask for /sw.js, and a redirect
there is refused by browsers, which keeps the old
worker. This one unregisters, empties the caches
and reloads its tabs, which then meet the 301.
a signed-in page 302 through /jev/auth/moved on SITE_HOST with a
one-time token: the session cookie is this host's
only, and the account's passkeys were made here and
sign in here only (auth.ts), so without it an
account from before the move could never be
reached again. The token is taken there once,
within a minute, for a session on the new host,
and this host's session ends with it. The page
then asks for a passkey for the new address
(passkeyHere in /jev/auth/me).
Nothing else is answered on the old host: no relay, no sign-in, no MCP.
The host the site moved from: this Worker's workers.dev name, and any preview of it. Dev (localhost) and the site's own host are neither.
38export const isOldHost = (hostname: string) => hostname.endsWith('.workers.dev');
40const REMOVE_SERVICE_WORKER = `// jevstrudel moved to https://${SITE_HOST}/ 41self.addEventListener('install', () => self.skipWaiting()); 42self.addEventListener('activate', (event) => { 43 event.waitUntil( 44 (async () => { 45 await self.registration.unregister(); 46 for (const key of await caches.keys()) await caches.delete(key); 47 for (const client of await self.clients.matchAll({ type: 'window' })) client.navigate(client.url); 48 })(), 49 ); 50}); 51`;
A request to the old host: where it went.
54export async function moved(request: Request, env: Env, accounts: Accounts = d1Accounts(env.DB)): Promise<Response> { 55 const url = new URL(request.url); 56 const page = request.method === 'GET' || request.method === 'HEAD'; 57 if (page && url.pathname === '/sw.js') { 58 return new Response(REMOVE_SERVICE_WORKER, { 59 headers: { 'Content-Type': 'text/javascript; charset=utf-8', 'Cache-Control': 'no-store' }, 60 }); 61 } 62 const there = `https://${SITE_HOST}`; 63 const token = page ? sessionToken(request) : null; 64 // only a navigation carries a session across: a page's own fetches would 65 // each spend a token nothing follows 66 if (token && request.headers.get('Sec-Fetch-Mode') === 'navigate') { 67 const user = await accounts.sessionUser(token); 68 if (user) { 69 const handoff = newSessionToken(); 70 await accounts.putHandoff(user.id, handoff, token, HANDOFF_TTL_MS); 71 const to = new URL(MOVED_PATH, there); 72 to.searchParams.set('token', handoff); 73 to.searchParams.set('to', url.pathname + url.search); 74 return new Response(null, { status: 302, headers: { Location: to.href, 'Cache-Control': 'no-store' } }); 75 } 76 } 77 return new Response(null, { 78 status: page ? 301 : 308, 79 headers: { Location: there + url.pathname + url.search }, 80 }); 81}
A path on this site, and nothing that leaves it: "/x", never "//host" or "/\host", which browsers read as another site.
85const sitePath = (to: string | null) => (to && /^\/(?![/\\])/.test(to) ? to : '/');
GET /jev/auth/moved?token=…&to=…, on the site: the handoff taken for a session here. A token that is unknown, used or expired signs nobody in; either way the visitor lands on the page they asked for.
90export async function arrived(request: Request, env: Env, accounts: Accounts = d1Accounts(env.DB)): Promise<Response> { 91 const url = new URL(request.url); 92 const headers = new Headers({ Location: sitePath(url.searchParams.get('to')), 'Cache-Control': 'no-store', 'Referrer-Policy': 'no-referrer' }); 93 if (request.method !== 'GET') return new Response('GET only', { status: 405, headers: { Allow: 'GET' } }); 94 const handoff = url.searchParams.get('token'); 95 if (handoff && /^[A-Za-z0-9_-]{43}$/.test(handoff)) { 96 const session = newSessionToken(); 97 if (await accounts.takeHandoff(handoff, session, SESSION_TTL_MS)) headers.set('Set-Cookie', sessionCookie(session)); 98 } 99 return new Response(null, { status: 302, headers }); 100}