Vercel deployment checklist
Environment, callback, migration, and verification checklist for deploying the active Miner auth and dashboard backend on Vercel.
Sources
Runtime and project settings
Deploy the Next app on Vercel with the Node runtime for backend auth, signup rate limits, hotkey, and database routes. Do not move these API routes to an edge runtime because they use server-only env validation, Better Auth, Neon, database-backed rate limits, and Substrate signature utilities.
Keep local, preview, and production URLs separate. Every URL used by Better Auth, app URLs, and trusted origins must match the environment that receives traffic.
| Environment | URL checklist |
|---|---|
| Local | APP_URL and BETTER_AUTH_URL use http://localhost:3000. BETTER_AUTH_TRUSTED_ORIGINS includes http://localhost:3000. |
| Preview | APP_URL and BETTER_AUTH_URL use the active Vercel preview URL. Add that preview origin to BETTER_AUTH_TRUSTED_ORIGINS before testing signup. |
| Production | APP_URL and BETTER_AUTH_URL use https://platform.network. BETTER_AUTH_TRUSTED_ORIGINS includes https://platform.network and any canonical www alias that can start auth. |
Required environment variables
Vercel project settings must define the same names as .env.example. Use placeholders in docs and examples only, never committed real values.
| Group | Variables |
|---|---|
| App and Better Auth | APP_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, BETTER_AUTH_TRUSTED_ORIGINS |
| Neon and Drizzle | DATABASE_URL |
| Signup rate limits | RATE_LIMIT_WINDOW_SECONDS, RATE_LIMIT_MAX_REQUESTS, AUTH_RATE_LIMIT_MAX_REQUESTS; signup registration is additionally fixed at 10 attempts per IP per hour in the database-backed rate limit table |
| Hotkey proof | HOTKEY_NONCE_TTL_SECONDS, HOTKEY_SIGNATURE_NETWORK |
Better Auth username signup
Generate a fresh BETTER_AUTH_SECRET for each real environment and store it only in the Vercel secret UI or local ignored env files. BETTER_AUTH_URL should be the exact public origin for that environment.
Signup uses username and password from the browser. The server checks the signup IP rate limit, validates the shared username policy, then forwards name, username, displayUsername, and an internal non-contact .invalid compatibility email to Better Auth.
| Surface | Requirement |
|---|---|
| POST /api/auth/sign-up/email | Browser sends username and password. The server enforces 10 attempts per IP per hour and injects the Better Auth compatibility email. |
| Username policy | Lowercase 3 to 32 characters, letters and numbers, optional single internal hyphen or underscore, and reserved names rejected. |
| Dashboard access | Requires a signed-in credential account with a username. Contact email is not part of access. |
Neon database and Drizzle migrations
Create a Neon Postgres database for each environment and set DATABASE_URL with sslmode=require. Use least-privilege credentials for deployed apps when Neon roles are available.
Generate migrations from schema changes before deployment, check them locally, then run migrations against the target Neon database from a controlled shell with that environment's DATABASE_URL loaded.
npm run db:generate npm run db:check npm run db:migrate
Signup rate limit
Username registration does not use reCAPTCHA. The backend rate-limits POST /api/auth/sign-up/email to 10 attempts per IP per hour before reading the signup body.
The limit uses the shared database-backed rate_limits table and returns 429 with Retry-After and X-RateLimit headers when exhausted.
| Check | Server behavior |
|---|---|
| First 10 attempts | Allowed within a one-hour IP window before username validation and Better Auth signup. |
| 11th attempt | Returns 429 RATE_LIMITED with retry metadata. |
| Missing IP headers | Falls into the shared unknown-IP bucket instead of bypassing the limit. |
Secret handling and deployment verification
Keep all real secrets out of git. Commit only placeholders in .env.example and docs. Review Vercel environment scopes before promoting preview values to production.
Before shipping, run the same checks locally and in CI where possible. Backend auth tests avoid live Upstash, Neon, and other external provider calls, but they still verify route gates and active auth controls.
npm run typecheck npm run build npm run test:backend-auth npm run test:browser-auth