From 4e6df488fcb153fe5f436779d15ff8f561b36fae Mon Sep 17 00:00:00 2001 From: archipelago Date: Fri, 7 Aug 2026 06:28:51 -0400 Subject: [PATCH] fix(aiui): Claude API key never persisted in plaintext again (S2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The key rode the wholesale settings→localStorage save, sitting at rest readable by any same-origin script, while the AES-256-GCM key-vault built for exactly this sat bypassed. Now: the key lives in a memory-only store ref, persists only into the encrypted vault when a passphrase session is active (migrating into the vault on unlock), and a one-time migration lifts any existing plaintext key out of localStorage and re-saves the scrubbed settings object immediately. Settings UI reports honestly how the key is held. Typecheck clean; test suite unchanged (348 pass, 3 pre-existing fails). Co-Authored-By: Claude --- aiui/packages/app/src/App.vue | 4 ++ .../src/components/settings/SettingsPanel.vue | 32 +++++---- aiui/packages/app/src/composables/useAI.ts | 10 ++- aiui/packages/app/src/stores/settings.ts | 70 +++++++++++++++++-- 4 files changed, 96 insertions(+), 20 deletions(-) diff --git a/aiui/packages/app/src/App.vue b/aiui/packages/app/src/App.vue index e3865c52..b7fd6d51 100644 --- a/aiui/packages/app/src/App.vue +++ b/aiui/packages/app/src/App.vue @@ -30,6 +30,7 @@ import { generateSalt, setSessionKey, } from '@/utils/crypto' +import { useSettingsStore } from '@/stores/settings' const { currentTheme, initTheme, setTheme } = useTheme() const archy = useArchy() @@ -76,6 +77,9 @@ async function handlePassphraseSubmit(passphrase: string) { const key = await deriveKey(passphrase, salt) setSessionKey(key, salt) + // Now that a session key exists: pull the Claude key from the encrypted + // vault, or migrate a just-scrubbed legacy plaintext key INTO the vault. + await useSettingsStore().initClaudeKey() showPassphrase.value = false } catch (err) { console.error('[AIUI] Passphrase error:', err) diff --git a/aiui/packages/app/src/components/settings/SettingsPanel.vue b/aiui/packages/app/src/components/settings/SettingsPanel.vue index f2529dcf..c52d896e 100644 --- a/aiui/packages/app/src/components/settings/SettingsPanel.vue +++ b/aiui/packages/app/src/components/settings/SettingsPanel.vue @@ -244,12 +244,12 @@
- {{ store.settings.useOwnApiKey && store.settings.claudeApiKey + {{ store.settings.useOwnApiKey && store.claudeApiKey ? 'Using your API key' : 'Using server authentication (OAuth)' }} @@ -291,9 +291,9 @@
@@ -311,7 +311,7 @@
  • Create a new key (starts with sk-ant-api03-)
  • Paste it above and enable "Use my own API key"
  • -

    Your key is stored locally on this device only. Without a key, the server's OAuth authentication is used.

    +

    Your key is stored encrypted on this device (AES-256-GCM) once you set an AIUI passphrase; without one it is held in memory for this session only — never written to disk unencrypted. Without a key, the server's OAuth authentication is used.

    @@ -357,7 +357,7 @@