{"record":{"id":"486497ba463ada50","repo":"different-ai/openwork","slug":"a-secure-linux-password-store-is-required-for-open","errorCode":null,"errorMessage":"A secure Linux password store is required for OpenWork-managed OAuth.","messagePattern":"A secure Linux password store is required for OpenWork-managed OAuth\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/secure-vault-key.mjs","lineNumber":77,"sourceCode":" *   loadSafeStorage: () => import(\"electron\").SafeStorage;\n *   platform?: NodeJS.Platform;\n * }} options\n */\nexport function createDesktopVaultKeyProvider({\n  filePath,\n  loadSafeStorage,\n  platform = process.platform,\n}) {\n  /** @type {Promise<Buffer> | null} */\n  let pending = null;\n\n  async function loadKey() {\n    const safeStorage = loadSafeStorage();\n    if (!safeStorage || !(await safeStorage.isAsyncEncryptionAvailable())) {\n      throw new Error(\"Operating-system secure storage is unavailable for OpenWork-managed OAuth.\");\n    }\n    if (platform === \"linux\" && safeStorage.getSelectedStorageBackend() === \"basic_text\") {\n      throw new Error(\"A secure Linux password store is required for OpenWork-managed OAuth.\");\n    }\n\n    /** @type {Buffer | undefined} */\n    let encrypted;\n    try {\n      encrypted = await readFile(filePath);\n    } catch (error) {\n      if (error?.code !== \"ENOENT\") throw error;\n    }\n\n    if (encrypted) {\n      /** @type {Awaited<ReturnType<typeof safeStorage.decryptStringAsync>> | undefined} */\n      let decrypted;\n      /** @type {Buffer | undefined} */\n      let key;\n      try {\n        decrypted = await safeStorage.decryptStringAsync(encrypted);\n        key = decodeKey(decrypted.result);","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/desktop/electron/secure-vault-key.mjs#L59-L95","documentation":"Thrown by `loadKey` on Linux when safeStorage's selected storage backend is `basic_text`, meaning Electron is falling back to plain obfuscated text on disk instead of a real OS password store. OpenWork rejects this because the OAuth credential key would not be genuinely encrypted, so it demands a secure keyring backend.","triggerScenarios":"On Linux, `safeStorage.getSelectedStorageBackend()` returns `basic_text` — typically because gnome-keyring/KWallet is absent or no keyring is unlocked in the session (headless, SSH, minimal window managers).","commonSituations":"Running the app in a minimal Linux desktop or WSL without gnome-keyring; headless/CI sessions; Docker containers; distros without a default keyring daemon; session started before keyring service.","solutions":["Install and unlock gnome-keyring (or KWallet) and ensure it runs in your desktop session","Launch the app with a proper dbus session so Electron can talk to the keyring","On headless/CI, start a virtual keyring (gnome-keyring-daemon) before launching","Confirm via safeStorage.getSelectedStorageBackend() that the backend is no longer basic_text"],"exampleFix":"// before\n// electron .  -> backend is basic_text\n// after\n# start a keyring and dbus session first\neval $(dbus-launch)\ngnome-keyring-daemon --start --components=secrets\nelectron .","handlingStrategy":"validation","validationCode":"function linuxBackendIsSecure(safeStorage, platform) {\n  return platform !== 'linux' || safeStorage.getSelectedStorageBackend() !== 'basic_text';\n}","typeGuard":null,"tryCatchPattern":"try {\n  const key = await getKey();\n} catch (e) {\n  if (e.message.startsWith('A secure Linux password store is required')) {\n    showLinuxKeyringInstructions();\n  } else throw e;\n}","preventionTips":["Ensure gnome-keyring/KWallet is installed and unlocked before launch","Start the app inside a dbus session","On headless CI, start gnome-keyring-daemon first","Log getSelectedStorageBackend() at startup to catch basic_text early"],"tags":["linux","keyring","safe-storage","electron","oauth"],"backgroundTag":"linux-keyring-basic-text-backend","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}