Skip to content

Store all app secrets as a single encrypted keychain blob, not per-key items #263

@czxtm

Description

@czxtm

Context

Raised in #nixmac on 2026-05-29 by Cooper:

"I don't think we should store every API key as an individual keychain item… sometimes crypto wallets will Face ID you 3 times to send 1 tx, this is why. We should just store an encrypted blob somewhere with all the sensitive data encrypted by a single data key, and then that key should be in the keychain. The security is equivalent in either implementation. If you type 'safe storage' in keychain you can see this is what pretty much every app does."

ENG-438 (Done) migrated from plaintext settings.json to the OS keychain. However the current implementation stores each API key as a separate keychain item, causing multiple OS-level password/biometric prompts per session — especially painful in dev builds.

The correct pattern is:

  1. Generate a single symmetric data key and store it in keychain (one prompt, once)
  2. Encrypt all sensitive fields (API keys, tokens, etc.) into a single encrypted blob stored in the app's data directory
  3. On launch, unlock the blob with the single keychain key

Acceptance Criteria / Gherkin Specs

Scenario: App requests keychain access only once per session
  Given the user launches nixmac for the first time after this change
  When the app initialises and loads secrets
  Then the OS keychain prompt appears exactly once
  And all API keys are accessible without further keychain prompts in that session

Scenario: Encrypted blob stores all sensitive fields
  Given the user has configured an Anthropic key, an OpenRouter key, and a vLLM API key
  When the secrets are persisted
  Then all three keys are stored in a single encrypted blob on disk
  And the encryption key for that blob is the only item stored in the OS keychain

Scenario: Blob is regenerated when the data key rotates
  Given a data key rotation is triggered (e.g., on first migration)
  When the rotation completes
  Then the old per-key keychain items are removed
  And the new single-key + blob layout is in place
  And the user can still access all their previously configured secrets

Scenario: Dev build does not prompt repeatedly
  Given the app is running as an unsigned or dev build
  When secrets are read multiple times in one session (e.g., Analyze button clicked repeatedly)
  Then the OS prompt appears at most once per session
  And subsequent reads use the in-memory decrypted secrets

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions