Corsair setup
Multi-tenant OAuth, CORSAIR_KEK, and integration initialization
Command Inbox uses the Corsair SDK as the integration layer for Gmail and Google Calendar. Each Better Auth user becomes a Corsair tenant with isolated OAuth tokens.
CORSAIR_KEK
The key encryption key protects OAuth tokens at rest in Postgres.
openssl rand -base64 32Set as CORSAIR_KEK in env. Rules:
- Generate once per environment (local vs production use different keys)
- Never rotate casually — existing tokens become unreadable
- If you must rotate, run
bun run corsair:resetand have all users reconnect
corsair:setup
After migrations:
bun run corsair:setupThis script (scripts/corsair-setup.ts):
- Initializes Corsair database schema
- Registers Gmail and Google Calendar plugin configurations
- Persists integration keys (including Pub/Sub topic if
GMAIL_PUBSUB_TOPICis set)
OAuth flow
App sign-in (Better Auth)
Standard Google OAuth for app authentication → /api/auth/callback/google
Gmail + Calendar connect (Corsair)
- User clicks Connect on
/onboarding/connect GET /api/connect/googlestarts Gmail OAuth- Callback at
/api/auth/callback/corsairexchanges code - If Calendar not connected, chains to Calendar OAuth
- On full connect + backfill → redirect to
/inbox
Production redirect URIs must include:
https://<app-domain>/api/auth/callback/corsairTenant model
| Concept | Value |
|---|---|
| Better Auth user ID | Primary user identifier |
| Corsair tenant ID | Same as user ID |
| Webhook URL | {APP_URL}/api/webhooks?tenantId={userId} |
Smoke test
Verify Corsair can reach Google APIs:
bun run smoke:corsairRequires a connected user with valid tokens in the database.
Gmail watch
Register push notifications after connect:
bun run gmail:watchOr via Gmail API users.watch — see Webhooks & realtime.
Watches expire after ~7 days and must be renewed.
KEK mismatch error
If /api/connect/google redirects with ?error=kek_mismatch, the CORSAIR_KEK changed since tokens were encrypted. Run corsair:reset and reconnect.