You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[F3-04] Claves de proveedor del usuario (BYOK) cifradas #47
Fase: F3 · Tamaño: M · Depende de:#40 (F3-02), #32 (F2-07) Decisiones: D-03 en #1
Objetivo
Que cada usuario guarde sus claves de IA de forma que ni un volcado de la base de datos ni los logs las expongan, dejando preparado el paso a créditos de plataforma.
Alcance
Modelo ProviderCredential: userId, providerId, label, source: 'user' | 'platform' (siempre user en v1), keyVersion, salt, iv, tag, ciphertext, last4, status (valid | invalid | unchecked), lastValidatedAt. Único (userId, providerId, label).
Cifrado AES-256-GCM (node:crypto); clave por registro derivada con HKDF-SHA256 de la clave maestra; AAD = userId|providerId|credentialId.
Claves maestras versionadas en CREDENTIAL_KEYS (JSON { "1": "<base64 32 bytes>" }), leídas del gestor de secretos del hosting.
API: GET /api/v1/credentials, POST /api/v1/credentials (valida con validateKey antes de guardar), POST /api/v1/credentials/:id/validate, DELETE /api/v1/credentials/:id.
La clave en claro nunca se devuelve ni se registra; los trabajos en Redis solo llevan credentialId; el worker descifra justo antes de usarla.
Si un proveedor responde auth durante un trabajo, la credencial pasa a invalid.
Criterios de aceptación
Test: cifrar → descifrar devuelve la clave original.
Test: copiar el ciphertext a otro userId hace fallar el descifrado.
Test: ninguna respuesta de /credentials contiene apiKey, ciphertext, iv ni tag.
Test: guardar una clave que validateKey rechaza → 422 y no se guarda nada.
Test: la clave de prueba no aparece en ninguna línea de log capturada durante la suite.
Activity