env/headers are sealed into secretCipher with the BYOK AES-GCM/HKDF box.
The admin GET returns masked values; a mask posted back for the same
server and key keeps the stored value, any other mask is a 400. The chat
and the test button use the stored values. upgradeAiData seals plaintext
v1 rows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0002_ai_mentor_prefs.sql adds ai_mentor_prefs(user_id, course_id, off).
upgradeAiData copies v1 config_json.mentorOff toggles into it once
(marked ai_data_v11 in schema_migrations); the settings repo awaits the
upgrade before first use and aiFeature starts it at construction.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New packages/feature-ai/migrations/0001_ai_message_citations.sql adds
ai_messages.citations_json and folds v1 'sources' rows into the assistant
row before them. The feature writes and reads the column directly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A user's own key goes only to https://api.anthropic.com, https://api.openai.com/v1
or an origin listed in STC_AI_ALLOWED_BASE_URLS / the server config's
allowedBaseUrls; anything else is a 400, and a stored key whose origin was
later removed stops being used.
- User-scope provider calls never follow redirects, and their errors reach the
user only as the HTTP status plus a fixed message; the full text is logged.
- The admin's server-scope provider may still use any base URL.
- Admin UI: allowed origins field; MCP stdio env editable, HTTP headers kept.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>