Guest portal
A mobile-first guest-facing site at my.stayonthesnow.com. Each
booking has its own private URL of the form /p/{token} where the
token is a 32-byte opaque string baked into every guest email.
Why a portal
Before P12, guest interactions lived in scattered emails with bare
?b={bookingId} links pointing at individual form endpoints. That
broke the moment a guest wanted to revisit something later (links got
buried in their inbox), and the ?b= query-string ID was both
guessable and unauthenticated.
The portal:
- Gives each booking ONE link that opens a hub of every action they might need (forms, lock-box code, add-ons, property info)
- Replaces the guessable booking ID with a 256-bit opaque token
- Sets a 2-year cookie so returning visitors land back in the portal without needing to dig out the email again — useful for future re-book nudges
What pages exist
| Path | Purpose | Module |
|---|---|---|
/ | Cookie-root: redirect to portal if signed in, else “look in your email” page | P12.M2 |
/p/{token} | Landing / overview card (property, dates, status, action buttons) | P12.M2 |
/p/{token}/pre-checkin | Existing pre-check-in form, token-verified | P12.M3 |
/p/{token}/check-in | Existing check-in form, token-verified | P12.M3 |
/p/{token}/check-out | Existing check-out form, token-verified | P12.M3 |
/p/{token}/arrival | Lock-box code + arrival instructions (gated to arrival day) — see Arrival screen | P12.M4 |
/p/{token}/addons | Add-on status summary + late-checkout picker entry — see Add-ons screen | P12.M5 (small) |
/p/{token}/info + 3 sub-pages | WiFi, parking, amenities, walk-throughs, rules — see Property info pages | P12.M8 |
/signout | Clear cookie, show goodbye page | P12.M2 |
Design system
- Mobile-first single column, max width 520px
- Deep mountain-blue primary (
#1e3a5f), warm amber CTAs (#d97706) - System font stack — zero web-font load delay on slow trailhead Wi-Fi
- All HTML rendered by the worker; CSS inlined per response (no external assets, no FOUC)
- Shared shell helper lives in
src/lib/portal-shell.ts— every portal page callsrenderPortalShell({ title, body, hero? })
Source
- Routes:
src/routes/portal.ts - Shell:
src/lib/portal-shell.ts - Tokens:
src/lib/guest-token.ts+migrations/0035_guest_tokens.sql - Tests:
test/routes/portal.test.ts,test/lib/guest-token.test.ts
See Token model and Cookie session for the auth details.