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.
What’s Stored in Keychain
Section titled “What’s Stored in Keychain”| Data | Keychain Key Pattern |
|---|---|
| Identity passwords | identity_password_{id} |
| Identity private keys | identity_key_{id} |
| Security PIN hash | security_pin_hash |
| Lock method | security_lock_method |
| Auto-lock timeout | security_auto_lock_timeout |
| PIN failed attempts | pin_failed_attempts |
| PIN lockout timer | pin_lockout_until |
| Auto-backup password | auto_backup_password |
| Sync state data | Various 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
Section titled “FlutterSecureStorage on macOS”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.
Entitlements
Section titled “Entitlements”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
Section titled “Hardened Runtime”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.
Keychain Behavior
Section titled “Keychain Behavior”On Install
Section titled “On Install”The first time ZestSSH writes to the keychain, macOS may prompt the user to allow keychain access. Once approved, subsequent access is automatic.
On Uninstall
Section titled “On Uninstall”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.
Multiple macOS Accounts
Section titled “Multiple macOS Accounts”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.
iCloud Keychain
Section titled “iCloud 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.
Security Considerations
Section titled “Security Considerations”- 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.