Skip to content

macOS Keychain Integration

On macOS, ZestSSH uses the system Keychain to store sensitive data securely. This is handled transparently through FlutterSecureStorage, which maps to the macOS Keychain Services API.

DataKeychain Key Pattern
Identity passwordsidentity_password_{id}
Identity private keysidentity_key_{id}
Security PIN hashsecurity_pin_hash
Lock methodsecurity_lock_method
Auto-lock timeoutsecurity_auto_lock_timeout
PIN failed attemptspin_failed_attempts
PIN lockout timerpin_lockout_until
Auto-backup passwordauto_backup_password
Sync state dataVarious sync-related keys

All of this data is encrypted by the macOS Keychain using the user’s login keychain password, which is typically the same as their macOS user password.

FlutterSecureStorage on macOS uses the Security framework’s Keychain Services API:

  • Items are stored as generic passwords (kSecClassGenericPassword).
  • Each item is scoped to the app’s keychain access group, preventing other apps from reading ZestSSH secrets.
  • The keychain is locked when the user logs out and unlocked on login.

ZestSSH’s macOS build includes the following entitlements:

App Sandbox (com.apple.security.app-sandbox)

Section titled “App Sandbox (com.apple.security.app-sandbox)”

The app runs in Apple’s sandbox, which restricts file system access, network access, and hardware access to only what is explicitly permitted. This is required for Mac App Store distribution and provides defense-in-depth.

Keychain Access Groups (com.apple.security.keychain-access-groups)

Section titled “Keychain Access Groups (com.apple.security.keychain-access-groups)”
<key>com.apple.security.keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>

This grants ZestSSH access to its own keychain group. The $(AppIdentifierPrefix) is the team ID, and $(CFBundleIdentifier) is the app’s bundle identifier. Together, they scope keychain items to ZestSSH only.

Network Client (com.apple.security.network.client)

Section titled “Network Client (com.apple.security.network.client)”

Allows outbound network connections — required for SSH, SFTP, Mosh, cloud sync, and backup destinations.

Network Server (com.apple.security.network.server)

Section titled “Network Server (com.apple.security.network.server)”

Allows inbound network connections — required for local port forwarding (the app acts as a local server that forwards traffic through the SSH tunnel).

User-Selected File Access (com.apple.security.files.user-selected.read-write)

Section titled “User-Selected File Access (com.apple.security.files.user-selected.read-write)”

Allows read/write access to files the user explicitly selects via file picker dialogs. Required for:

  • Importing SSH keys
  • Importing/exporting backup files
  • Importing SSH config files
  • Exporting terminal transcripts

JIT Compilation (com.apple.security.cs.allow-jit, Debug/Profile only)

Section titled “JIT Compilation (com.apple.security.cs.allow-jit, Debug/Profile only)”

Present only in the debug/profile entitlements, not in release builds. Allows Just-In-Time compilation for Flutter’s debug mode.

Hardened Runtime is enabled in the Xcode project settings. macOS apps distributed outside the Mac App Store must use Hardened Runtime for notarization. ZestSSH enables Hardened Runtime, which provides:

  • Code signing enforcement — Only signed code can be loaded.
  • Library validation — Only Apple-signed or same-team-signed libraries can be loaded.
  • Memory protection — Restricts access to other processes’ memory.

Combined with the App Sandbox, this creates two layers of isolation.

The first time ZestSSH writes to the keychain, macOS may prompt the user to allow keychain access. Once approved, subsequent access is automatic.

Keychain items are not automatically removed when the app is deleted. If you reinstall ZestSSH, previously stored secrets may still be available in the keychain. To fully remove all ZestSSH data, manually delete keychain items via Keychain Access.app.

Keychain items are scoped to the macOS user account. If multiple users share a Mac, each user’s ZestSSH secrets are isolated in their own login keychain.

FlutterSecureStorage items are stored in the local keychain, not iCloud Keychain. SSH credentials are not synced across devices via iCloud. Use ZestSSH Cloud Sync for cross-device synchronization.

  • The macOS Keychain encrypts items at rest using AES-256.
  • Items are only accessible when the keychain is unlocked (user is logged in).
  • The App Sandbox prevents other apps from accessing ZestSSH’s keychain group.
  • FlutterSecureStorage does not cache decrypted values in memory — each read is a fresh keychain query.