Docupdate 0.9.4.1 (#55)

This commit is contained in:
2026-02-24 11:37:21 +00:00
parent 5e416d3726
commit cdfd69bad3
2 changed files with 67 additions and 18 deletions

View File

@@ -2,6 +2,40 @@
All notable changes to Chat Switchboard.
## [0.9.4] — 2026-02-24
### Added
- **API key encryption (vault)** — Two-tier AES-256-GCM encryption for stored
API keys. Global/team keys encrypted with env-var-derived key (HKDF-SHA256);
personal BYOK keys encrypted with per-user encryption key (UEK) derived from
password via Argon2id. Admin cannot recover personal keys without user's
passphrase. New `server/crypto/` package: `vault.go`, `cache.go`,
`resolver.go`, `backfill.go` with 11 round-trip tests.
- **UEK lifecycle** — UEK generated on registration, unwrapped on login
(Argon2id → AES-GCM), cached in `sync.Map` for session duration, evicted
with memory zeroing on logout. Pre-migration users auto-initialize vault
on first login.
- **Migration 003_vault.sql** — Adds `encrypted_uek`, `uek_salt`, `uek_nonce`,
`vault_set` to users table. Adds `api_key_enc` (BYTEA), `key_nonce`,
`key_scope` to provider_configs. Backfills `key_scope` from existing scope.
- **Startup backfill** — `BackfillEncryptedKeys()` encrypts plaintext API keys
on first startup with `ENCRYPTION_KEY` set. `EnforceEncryptionKey()` refuses
startup if encrypted keys exist but env var is missing.
- **CI/CD: encryption secret** — `ENCRYPTION_KEY` Gitea secret synced to k8s
`switchboard-encryption` secret. Backend manifest references it as optional
`secretKeyRef`.
### Fixed
- **HTML code blocks render live in chat (XSS)** — When a model's `</think>`
tag directly abutted a code fence (no newline), the fence wasn't recognized
by marked.js, causing raw HTML to render as live DOM elements (canvas games,
styles, etc.). Three-layer fix: (1) DOMPurify switched from permissive
`ADD_TAGS` (default allows canvas, style, form, etc.) to strict
`ALLOWED_TAGS` allowlist of only markdown-produced elements; (2) think-block
placeholders padded with `\n\n` to ensure adjacent fences start on fresh
lines; (3) unclosed code fences auto-closed before `marked.parse` for
streaming protection.
## [0.9.3] — 2026-02-23
### Changed