Call setupCorsair({ tenantId }) on signup — no deploy per tenant. Idempotent.
corsair.ts
import { createCorsair } from 'corsair';
import { github, slack, linear } from 'corsair';
export const corsair = createCorsair({
multiTenancy: true,
plugins: [github(), slack(), linear()],
database: db,
kek: process.env.CORSAIR_KEK!,
});
On signup
import { setupCorsair } from 'corsair';
import { corsair } from '@/server/corsair';
export async function onUserCreated(userId: string) {
await setupCorsair(corsair, { tenantId: userId });
}
Use any stable ID — user ID, org ID, workspace slug.
Connect credentials
API key:
const tenant = corsair.withTenant(userId);
await tenant.linear.keys.set_api_key(apiKey);
// account row must exist — setupCorsair creates it
OAuth:
import { processOAuthCallback } from 'corsair/oauth';
await processOAuthCallback(corsair, { code, state, redirectUri });
// creates account row for that plugin if missing
Full routes: OAuth process.
Runtime:
const tenant = corsair.withTenant(userId);
await tenant.github.api.repositories.list({ type: 'owner' });
Webhooks: ?tenantId=user_abc123 on the URL.
What setupCorsair creates
await setupCorsair(corsair, { tenantId: 'user_abc123' });
| ✓ | ✗ |
|---|
corsair_accounts per auth-type plugin | New plugins (code change) |
| Account DEKs | OAuth tokens |
| Integration rows if missing | Integration creds (set on corsair.keys) |
New plugin → redeploy
Plugins come from createCorsair({ plugins: [...] }). Adding one requires a code change and deploy.
After deploy:
# integration row (+ default account on single-tenant)
pnpm corsair setup
# set OAuth app creds if needed
pnpm corsair setup --gmail client_id=... client_secret=...
Multi-tenant — provision existing tenants:
for (const tenantId of await listActiveTenantIds()) {
await setupCorsair(corsair, { tenantId });
}
Each tenant still authorizes the new plugin separately — rows are not copied.
Removing a plugin from code does not delete DB rows.
Lazy provisioning
OAuth-only path — skip setupCorsair({ tenantId }) on signup:
# once: integration rows + OAuth app creds
pnpm corsair setup --gmail client_id=... client_secret=...
// per user, per plugin — creates account row
await processOAuthCallback(corsair, { code, state, redirectUri });
// or: pnpm corsair auth --plugin=gmail --tenant=userId
API keys require setupCorsair({ tenantId }) first — keys.set_*() throws without an account row.
CLI vs code
| |
|---|
setupCorsair in backend | Production |
pnpm corsair setup --tenant=<id> | Local / ops |
pnpm corsair ui | Dev exploration |
Basics: Setup.