Phase 2 of secrets at rest (#213), same AES-256-GCM scheme and key.
- llm_profiles.proxy joins the secret columns (AAD llm_profiles.proxy);
scanProfile decrypts it for both column lists, SaveProfile encrypts
it, an empty proxy stays NULL
- brave_search_api_key, tavily_search_api_key, web_search_proxy and
global_proxy are stored encrypted (AAD settings.<key>) through
GetSetting, SetSetting and InsertSettingIfAbsent; other settings stay
in clear
- the startup backfill, the no-key refusal and the wrong-key check
cover the new values; legacy plaintext is read as-is until backfill
API: the proxies are returned masked as a whole (placeholder plus a
*_set flag) by GET /api/settings, GET /api/llm/profiles and GET /api/llm.
On write the placeholder keeps the stored value, an explicit value
replaces it and "" clears it; the placeholder is never stored. The test
and model-list endpoints resolve the placeholder to the stored proxy.
Comments, doc comments, UI labels, log and error messages, LLM prompt
strings, markdown docs, test data and assertions. Identifiers, enum
keys, routes, placeholders and code structure are unchanged.
Notes:
- docs/漏洞流量证据.md renamed to docs/vulnerability-traffic-evidence.md
- tests that exercised CJK specific handling (GBK zip names, Chinese
full text search) now use ASCII equivalents; go build passes and the
test suite matches the pre translation baseline (remaining failures
need a PostgreSQL DSN and fail identically on the original)
- frontend mention syntax now uses English kinds (finding, asset, ...)
matching the backend, chat mentions tests pass