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:
No App Sandbox
Section titled “No App Sandbox”ZestSSH’s macOS build is not sandboxed — there is no com.apple.security.app-sandbox entitlement. It is distributed outside the Mac App Store as a developer/sysadmin tool. Running unsandboxed is the standard approach for macOS SSH clients (Termius, iTerm2, Royal TSX): it gives the app access to the user’s existing ~/.ssh/config and known_hosts files and avoids the errSecMissingEntitlement keychain errors that sandboxing caused on non-App-Store signing.
Keychain Access Group
Section titled “Keychain Access Group”The macOS build does not declare a com.apple.security.keychain-access-groups entitlement. Because the app is not sandboxed, Keychain Services automatically uses the app’s own bundle identifier as the access group, scoping keychain items to ZestSSH without any explicit $(AppIdentifierPrefix) configuration.
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).
File Access
Section titled “File Access”Because the app is not sandboxed, it does not declare the com.apple.security.files.user-selected.read-write entitlement (unnecessary without the sandbox). File operations still work normally, including:
- 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.
The macOS build is not sandboxed, so Hardened Runtime — not the App Sandbox — provides this process 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 Juiced 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).
- Keychain items are scoped to ZestSSH’s own access group (its bundle identifier), so other apps cannot read them.
- FlutterSecureStorage does not cache decrypted values in memory — each read is a fresh keychain query.