Self-serve signup: from owner-minted keys to a stranger holding one in two minutes

PHASE06
DATE2026-07-20

What shipped

Until this phase every meshcheck API key was minted by hand from an admin CLI. Phase 6 shipped the missing front door, live on meshcheck.dev: a real /pricing page with the four plans, /signup that emails a single-use sign-in link (Resend, resend@6.17.2), account auto-creation with the free plan’s 300 credits granted idempotently, a /dashboard for minting and revoking keys (raw key shown once, five active max) and upgrading through the existing Polar checkout, and /billing/success, which had been a dangling redirect target since phase 4. Server-side that is a new /api/web/* surface outside the keyed /v1 subtree: stateless HMAC session cookies, dual IP-and-email rate buckets on link requests, an Origin-check CSRF guard, and migration 0008 (auth_tokens). The pricing-page CTA carries a plan intent through the email round-trip, so a paid pick lands the user in Polar checkout right after verification.

Decisions

The auth question had a strong gravitational pull toward frameworks. Research settled it: Lucia is deprecated, Better Auth is real but heavy, and the Polar portal cannot reveal keys it does not hold. A magic link plus a signed cookie covers the dashboard’s three abilities in about two hundred lines, tests included. Free-off-Polar was reaffirmed rather than relitigated, which kept signup independent of billing availability. The provisioning-truth decision (webhook writes, success page reads Neon) came straight from research flagging the redirect-replay trap, and it shaped both the success page and the checkout-status endpoint, which reports the account’s current Neon plan rather than trusting Polar’s redirect.

What broke

The launch-blocking find came from the live gate, not the suite: the first real signup email’s link showed “expired” before its owner ever clicked it. Gmail’s link scanner had prefetched the GET and burned the single-use token — the mechanism worked perfectly, for a robot. The fix is the standard interstitial: GET is now side-effect-free and returns a tiny auto-submitting form; only the POST claims the token. The re-test with a human click passed, and repeated GETs on a used link are inert.

Two false alarms are worth recording. A deployed cookie-tamper probe returned 200 and looked like an HMAC bypass; the “tamper” had replaced the cookie’s last character with the same character. Redone with guaranteed changes it returned 401 both ways. And right after the production promote, /signup/ returned 404 with the correct page body — a stale CDN entry from the pre-promote deployment, gone on the next requests. One real deploy stumble: vercel deploy --prebuilt --prod rejects a preview-built output; production needs its own vercel build --prod first.

Numbers

The server suite grew from 244 to 274 tests across the three milestones; the site e2e suite from 12 to 15, including a 390px header-visibility gate. The live preview checklist passed 9 of 9 after the scanner fix: real email delivery, cross-origin 403, account creation with exactly one 300-credit grant (re-login does not re-grant), intent carry-through, replay burn, tamper rejection, key mint, that key authenticating against /v1/account, and a sandbox Polar checkout URL. The production round-trip was performed by a human: signup, click, dashboard, 300 credits. The real-card paid test was deliberately deferred to the L5 launch matrix.

Next

Two test accounts (+p6test, +p6prod) join the prune list. The owed L6 soak now certifies a config that includes phases 6 and 7. Owner UI notes from first contact with the shipped design are queued as input for the next visual iteration.

Decisions
DecisionWhyAlternatives rejected
Hand-rolled magic-link auth: single-use hashed tokens plus stateless HMAC session cookiesThe dashboard needs exactly three abilities (see credits, mint or revoke a key, reach the Polar portal); an auth framework is weight without benefit at that scopeBetter Auth (heavier, framework peer-deps), Lucia (deprecated upstream), Polar customer portal as sole auth (cannot reveal or rotate our keys)
Free tier stays off Polar; signup is our own email flowReaffirms the phase-4 settled decision; signup keeps working when billing is down and no zero-dollar product is neededfree as a $0 Polar product for one uniform provisioning path
The Polar webhook remains the only writer of paid-plan state; the success page just polls NeonA checkout_id in a redirect URL is not proof of payment; anyone can paste an open checkout's idprovision synchronously on the success redirect
Emailed link lands on a side-effect-free GET interstitial; only its auto-submitted POST claims the tokenMail providers prefetch GET links and burn single-use tokens; scanners do not submit formsconsume on GET (shipped first, failed live), longer-lived reusable tokens (weakens single-use)
Keys are minted from the dashboard on demand, never at signup, shown onceOnly hashes are stored, so a later reveal is impossible; minting at signup would force showing a key nobody asked for yetauto-mint first key at account creation
Benchmarks
MetricValueTarget
server test suite274 passed / 274full suite green (stop conditions, all milestones)
site e2e suite15 passed / 15, tests unmodified15/15 green
live preview e2e checklist (deployed function, real Resend, real Neon)9/9 pass after the scanner fixevery gate: delivery, CSRF, create+grant, intent, replay, tamper, key mint, key on /v1/account, checkout URL
production signup round-trip (human, meshcheck.dev)signed in, 300 credits, key mintablestranger-flow works with zero owner involvement