Command Inbox
Developer Guide

Deploy to production

Vercel + Neon production deployment guide

Command Inbox targets Vercel (app) + Neon Postgres (database with pgvector). Gmail webhooks use your production URL — no ngrok in prod.

Documentation deploys as a separate Vercel project at docs.command-inbox.sayantanbal.in.

Prerequisites

  • Neon project with Postgres 16+
  • Vercel account linked to this repo
  • Google Cloud project with Gmail + Calendar OAuth (Testing mode OK for judges)
  • GCP Pub/Sub topic for Gmail push
  • Pusher Channels app (optional — 5s poll fallback)
  • At least one AI key: OPENAI_API_KEY and/or GOOGLE_GENERATIVE_AI_API_KEY

1. Neon database

  1. Create a project → copy the pooled connection string
  2. Enable pgvector (Neon console → Extensions → vector, or run migrations)
  3. From your machine with production DATABASE_URL:
bun run db:migrate
bun run corsair:setup

Keep CORSAIR_KEK stable — changing it after setup requires bun run corsair:reset and user reconnect.

2. Vercel app project

  1. Import the GitHub repo in Vercel
  2. Framework preset: Next.js (vercel.json sets bun install / bun run build)
  3. Root directory: repository root (main app)
  4. Add all variables from .env.example

Production values:

VariableProduction value
BETTER_AUTH_URLhttps://command-inbox.sayantanbal.in
APP_URLhttps://command-inbox.sayantanbal.in
DATABASE_URLNeon pooled connection string
GMAIL_PUBSUB_TOPICprojects/<gcp-project>/topics/<topic-name>

Generate once and store securely:

openssl rand -base64 32   # BETTER_AUTH_SECRET
openssl rand -base64 32   # CORSAIR_KEK
openssl rand -base64 32   # CRON_SECRET
  1. Deploy — migrations are not automatic; run db:migrate against Neon before or immediately after first deploy

3. Google OAuth (production)

In Google Cloud Console → APIs & Services → Credentials:

Authorized JavaScript origins

https://command-inbox.sayantanbal.in

Authorized redirect URIs

https://command-inbox.sayantanbal.in/api/auth/callback/google
https://command-inbox.sayantanbal.in/api/auth/callback/corsair

Add judge emails as Test users while the app stays in Testing mode.

4. Gmail Pub/Sub (production webhooks)

  1. Create topic + IAM binding per Webhooks & realtime
  2. Set GMAIL_PUBSUB_TOPIC in Vercel env
  3. Webhook endpoint:
https://command-inbox.sayantanbal.in/api/webhooks?tenantId={userId}

Re-run connect flow or bun run gmail:watch against production DB if watches were created with ngrok URLs.

5. Scheduled jobs

Vercel Hobby cron is daily-only. Use an external pinger every 1 minute:

cron-job.org (free)

  • URL: https://command-inbox.sayantanbal.in/api/cron/process-due
  • Method: POST
  • Header: Authorization: Bearer <CRON_SECRET>

6. Docs site (separate Vercel project)

  1. Create new Vercel project from same repo
  2. Set Root Directory to apps/docs
  3. Add custom domain: docs.command-inbox.sayantanbal.in
  4. Build uses apps/docs/vercel.json (installs from monorepo root)

7. Post-deploy smoke test

  1. Open /sign-in → Google sign-in
  2. Connect Gmail + Calendar on /onboarding/connect
  3. Confirm threads load on /inbox
  4. Send yourself an email → webhook classifies within ~30s
  5. Press / for semantic search after backfill
  6. Press Mod+Shift+F for advanced Gmail search

Troubleshooting

SymptomFix
500 on sign-inBETTER_AUTH_SECRET, BETTER_AUTH_URL, OAuth redirect URIs
Empty inbox after connectRun corsair:setup against prod DB
Webhooks never firePub/Sub IAM; GMAIL_PUBSUB_TOPIC; watch uses prod APP_URL
AI features 503Set AI keys in Vercel
Send-later never sendsConfigure cron-job.org with CRON_SECRET

Optional: deploy from CLI

npm i -g vercel
vercel login
vercel link
vercel env pull .env.production.local
vercel --prod

Migrations always run locally/CI against DATABASE_URL, not inside the Vercel build.