# Safe Storage - On macOS, Electron/Chromium `safeStorage` stores the os_crypt password as a generic Keychain item whose service is `" Safe Storage"` and whose account is `" Key"`. Do not query the account as only the product name; `security find-generic-password -s "dyad Safe Storage" -a "dyad"` misses the real item, which is under `dyad Key`. - The opt-in `safe_storage_keychain_identity.spec.ts` fixture must pre-seed its temporary Keychain with the same account names (`dyad Key` / `Chromium Key`). If it seeds bare `dyad` / `Chromium`, the Layer 1 recovery test fails with `settings.githubAccessToken` undefined even though production lookup is correct. - Settings retain their existing recovery; MCP secrets and `codex-subscription.enc` use `main/safe_storage.ts` for native decrypt plus legacy recovery. OAuth state read failures must throw rather than become `{}`; only explicit Disconnect/Reset may discard unreadable state. Surface `oauthUnreadable` in server DTOs so the UI offers Reset instead of a Connect that cannot succeed. Client secrets survive Disconnect; failed reads must point to re-entering the secret in plugin settings. - Preserve the Linux subscription store's rejection of unavailable encryption and `basic_text`, including its `Precondition` kind and OS-keyring setup guidance through status reads. Simulate denied decrypts with encryption available in platform-independent recovery tests. - The hosted `safe-storage-e2e` job builds a pinned Electron 40 baseline in a second checkout and reuses the PR artifact as candidate. Pass their `out/` directories through `DYAD_E2E_SAFE_STORAGE_BASELINE_BUILD` / `DYAD_E2E_SAFE_STORAGE_CANDIDATE_BUILD`; do not use the retired upgrade-build variable or confuse the candidate with the old build. - Settings writes normally bypass encryption in test builds. Upgrade sessions opt into `DYAD_E2E_REAL_ENCRYPTION=true` and assert ciphertext on disk; the pinned baseline gets a checked test-only source patch. Permissive fixture ACLs cover crypto/recovery, while a small signed beta smoke test separately checks production permissions. - The native recovery path retries negative ciphertext/password caches silently after five seconds on the next read; subscription reads must not permanently cache credential errors. Keep automatic retry disabled for the diagnostic CLI reader, which can prompt. Retain the pinned baseline's old startup race so CI can still produce Chromium-identity ciphertext. - MCP/subscription reads can first need Keychain unlock after the initial renderer-load check. Register the recovery-failure scheduler after renderer load and release its pending flag after every check; retain the recovery module's one-unlock-attempt-per-session limit.