Files
amethyst/commons/src/jvmTest
mandClaude ae3218249a fix(desktop): cache OS Keyring handle so startup only prompts once
`SecureKeyStorage`'s three keyring paths (save/get/delete) each called
`Keyring.create()` on every invocation. Each call opens a fresh backend
session:

- macOS: a new Security Framework session against `login.keychain`.
  Depending on the user's keychain policy (short access window, ACL on
  the amethyst-desktop item, or first-touch after unlock timeout), this
  surfaces a Keychain Access prompt every time.
- Linux Secret Service / KWallet: a fresh session may re-trigger the
  wallet-unlock prompt if the daemon closed the previous session.
- Windows Credential Manager: less user-visible but still redundant.

Amethyst's cold-boot touches the store at least twice — once for
`DesktopAccountStorage`'s AES-256-GCM metadata key
(`account-metadata-key`), then again for the active account's nsec —
so the user was seeing the OS keychain unlock prompt twice in a row
before the UI was reachable.

Fix: memoise the `Keyring` handle for the lifetime of the process. The
`Keyring` object is thread-safe for the three ops we call, so a
double-checked lazy singleton behind `keyringLock` is sufficient. The
NPE hit path is a proper lazy: any `BackendNotSupportedException` bubbles
up on the first call and is caught by the existing outer try/catch,
which flips `keyringAvailable=false` and falls back to the encrypted
file path (unchanged).

Includes a small package-private `KeyringHandle` interface + real
delegator so `SecureKeyStorageKeyringCacheTest` can substitute an
in-memory handle and count backend-open invocations without touching
the OS keychain. Three cases:

1. Cold-boot storm (save/get/delete across metadata + account keys)
   opens the Keyring exactly once.
2. Repeated `hasPrivateKey` reuses the cache.
3. Concurrent first-touches from 16 threads still open the Keyring
   exactly once (double-checked locking is race-free).

No behavioural change beyond the prompt-count fix.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-29 08:06:02 +10:00
..