← Command Center
Phase 1

Credential Vault

BYOK key management with swappable encryption. Two functions are the entire swap surface.

First Principle: BYOK unlocks team independence and scaffolds the MasteryOS bridge. Derek runs Reveal without asking Jason for keys. One swap point replaces the whole backend later.

Second-Order Effects

Team Independence
Each team member brings their own API keys. No bottleneck on a single person's account.
Cost Transparency
Users see their own API usage. No surprise bills on the platform owner's key.
MasteryOS Bridge
When MasteryOS vault API exists, swap encrypt() and decrypt() — zero other changes needed.
Security Posture
Keys encrypted at rest. Session-only access (no API key access to vault). Zero plaintext storage.

Atomic Parts

1.1
Migration: vault_secrets table

File: supabase/migrations/20260305_vault_secrets.sql

Columns: id, owner_type ('user'|'team'), owner_id, key, encrypted_value, category, description, last_rotated_at, created_at, updated_at. RLS: user manages own secrets. Unique on (owner_type, owner_id, key).

1.2
Encryption Layer

File: lib/vault/crypto.ts

AES-256-GCM via Node.js crypto module. Master key from VAULT_MASTER_KEY env var (32-byte hex). Two functions: encrypt(plaintext) and decrypt(ciphertext). This is the entire MasteryOS swap surface.

1.3
Vault Service

File: lib/vault/service.ts

getSecret, setSecret, deleteSecret, listSecrets (metadata only), resolveSecret (user → team → env var fallback). Uses Supabase admin client + crypto.

1.4
API Route

File: app/api/vault/route.ts

GET (list metadata), GET ?key=X&reveal=true (decrypt), POST (encrypt + store), DELETE. Session-only auth — no API key access to vault.

1.5
Types

File: types/database.ts

VaultSecret interface matching table schema.

1.6
VaultManager UI

File: components/VaultManager.tsx

Accordion by category (LLM, Infrastructure, Hosting, Email, General). Each row: key name, description, last rotated, masked value. Show/hide toggle, add/edit/delete. Pre-populated suggestions dropdown.

1.7
Dashboard Integration

File: components/DashboardContent.tsx

Add VaultManager below WebhookManager in left column.

1.8
Reveal Wiring

Update run/analyze routes: resolve API key from vault first, fall back to env var.

const apiKey = await resolveSecret(userId, null, 'ANTHROPIC_API_KEY')
  || process.env.ANTHROPIC_API_KEY
1.9
Master Key Setup

Generate 32-byte hex key, add to .env.local and Vercel env vars.

node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Key Files

FileRole
lib/vault/crypto.tsEncrypt/decrypt — the swap surface
lib/vault/service.tsBusiness logic + BYOK resolution
app/api/vault/route.tsREST API (session auth only)
components/VaultManager.tsxDashboard UI component
lib/auth/credential-vault.tsExisting stub — replaced by vault service
Risk to Mitigate: Don't over-engineer. Two functions (encrypt/decrypt) are the swap surface. No key rotation, no multi-region, no HSM. Ship the simplest thing that's secure.

Verification

  1. npx tsc --noEmit passes
  2. VAULT_MASTER_KEY generated and in .env.local + Vercel
  3. Migration run in Supabase SQL editor
  4. VaultManager visible in dashboard — add/edit/delete/reveal secrets
  5. Add ANTHROPIC_API_KEY to vault → Reveal uses it instead of env var
  6. Remove from vault → falls back to env var