Review fix round 1.
- Secret box v2 tokens: one HKDF key per purpose (byok, server-api-key,
mcp) and AES-GCM AAD bound to the row (user id + provider; server id +
field). MCP env and headers are sealed per field. open() refuses v1
tokens; the data upgrade re-seals v1 keys, v1 plaintext MCP values and
the draft secretCipher, and marks ai_secrets_v2 only when nothing
legacy is left (retried until the previous secret is back).
- Unreadable secrets are reported (admin: unreadableSecrets with server
and field; user: byok.keyUnreadable), logged at startup, and kept on
save unless replaced or cleared (clearApiKey, clearSecrets). The admin
and account pages show the warnings.
- allowedOrigins and provider resolution no longer open MCP secrets.
Co-Authored-By: Claude Opus 5.5 <[email protected]>