Skip to content

Move credential storage to OS keychain (macOS Keychain / Secret Service / Credential Vault) #3

@jimmyshiau

Description

@jimmyshiau

Severity

Low

Summary

Track switching credential storage from machine-derived AES-256-GCM (current) to native OS keychain integration (macOS Keychain via security, Linux Secret Service via secret-tool, Windows Credential Vault).

Why

The current implementation in src/credentials.ts encrypts the credentials file with a key derived from username + hostname + app salt:

function deriveKey(): Buffer {
  const { username } = userInfo();
  return createHash("sha256")
    .update(`${username} ${hostname()} ${APP_SALT}`)
    .digest();
}

This protects against accidental disclosure (backup tools copying $HOME, file shared by mistake, etc.) — a copied credentials file is unusable on a different machine or under a different user. But anyone who has the file and can guess (username, hostname) can reconstruct the key offline. For typical username and hostname patterns, the effective entropy is on the order of 30-40 bits.

OS keychain integration would:

  • Enforce per-app ACLs (other apps must prompt the user before reading).
  • Require an unlocked user session on macOS.
  • Eliminate the ability to brute-force key derivation from leaked file + low-entropy identifiers.

Constraints

  • Single-file SEA binaries can't ship native modules like keytar. The implementation must shell out to platform CLIs (security, secret-tool, cmdkey/PowerShell).
  • Headless / CI environments may have no keychain available — must keep the encrypted-file path as fallback.
  • Existing encrypted credentials files should migrate forward on first read.

Acceptance criteria

  • macOS: security add-generic-password writes the credentials; security find-generic-password -w reads them.
  • Linux: detect secret-tool presence; use it when available, fall back to encrypted file otherwise.
  • Windows: cmdkey or PowerShell-based equivalent.
  • Existing encrypted file is migrated to keychain on first read, then deleted.
  • A "logout" command clears both keychain entry and any leftover file.

Tracked by

Pre-0.1.1 security review. Not a regression from current behavior — current encrypted-file scheme is strictly better than the pre-0.1.0 plain-JSON approach. Tracking as future-work follow-up.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecuritySecurity-related issue

    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