49 lines
2.6 KiB
SQL
49 lines
2.6 KiB
SQL
-- 003_vault.sql — API Key Encryption + User Vault
|
|
-- v0.9.4: Two-tier encryption for API keys
|
|
--
|
|
-- Phase 1 (this migration): Add new columns alongside existing plaintext.
|
|
-- Phase 2 (Go startup): Backfill — encrypt plaintext keys, generate UEKs.
|
|
-- Phase 3 (future): Drop api_key_plain after confirmed stable.
|
|
|
|
-- =========================================
|
|
-- 1. USERS — Vault support
|
|
-- =========================================
|
|
-- Per-user encryption key (UEK) wrapped with password-derived key (Argon2id).
|
|
-- UEK protects personal BYOK API keys. Platform admin cannot recover without
|
|
-- the user's password.
|
|
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS encrypted_uek BYTEA;
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS uek_salt BYTEA;
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS uek_nonce BYTEA;
|
|
ALTER TABLE users ADD COLUMN IF NOT EXISTS vault_set BOOLEAN NOT NULL DEFAULT false;
|
|
|
|
COMMENT ON COLUMN users.encrypted_uek IS 'UEK encrypted with Argon2id(password)-derived key';
|
|
COMMENT ON COLUMN users.uek_salt IS 'Argon2id salt for UEK derivation';
|
|
COMMENT ON COLUMN users.uek_nonce IS 'AES-GCM nonce for UEK wrapping';
|
|
COMMENT ON COLUMN users.vault_set IS 'true once UEK has been generated and wrapped';
|
|
|
|
-- =========================================
|
|
-- 2. PROVIDER_CONFIGS — Encrypted key storage
|
|
-- =========================================
|
|
-- Rename current plaintext column, add encrypted BYTEA columns.
|
|
-- Backfill happens in Go (needs ENCRYPTION_KEY env var).
|
|
|
|
-- Keep plaintext temporarily for backfill; Go startup reads it, encrypts,
|
|
-- writes to new columns, then NULLs it out.
|
|
ALTER TABLE provider_configs RENAME COLUMN api_key_enc TO api_key_plain;
|
|
ALTER TABLE provider_configs ADD COLUMN IF NOT EXISTS api_key_enc BYTEA;
|
|
ALTER TABLE provider_configs ADD COLUMN IF NOT EXISTS key_nonce BYTEA;
|
|
ALTER TABLE provider_configs ADD COLUMN IF NOT EXISTS key_scope TEXT;
|
|
|
|
-- Backfill key_scope from existing scope column (safe default)
|
|
UPDATE provider_configs SET key_scope = scope WHERE key_scope IS NULL;
|
|
|
|
-- Now make it NOT NULL with default
|
|
ALTER TABLE provider_configs ALTER COLUMN key_scope SET NOT NULL;
|
|
ALTER TABLE provider_configs ALTER COLUMN key_scope SET DEFAULT 'global';
|
|
|
|
COMMENT ON COLUMN provider_configs.api_key_plain IS 'DEPRECATED: plaintext key, cleared after vault backfill';
|
|
COMMENT ON COLUMN provider_configs.api_key_enc IS 'AES-256-GCM encrypted API key (BYTEA)';
|
|
COMMENT ON COLUMN provider_configs.key_nonce IS 'AES-GCM nonce for api_key_enc';
|
|
COMMENT ON COLUMN provider_configs.key_scope IS 'Encryption tier: global/team use env key, personal uses UEK';
|