Keychain 3.0.1 continues to improve macOS --confirm UI dialog support. When --confirm is used on macOS, it now implies --no-inherit, to ensure that Keychain is able to initialize its own ssh-agent that is properly configured to use the macOS native Keychain confirm dialog (addresses #227).
In addition, cancelling GPG signing key warming no longer results in control characters being displayed on the terminal (fixes #228).
Added --immediate to skip "Press Enter to initialize keys" prompt (addresses #230). When requested keys are missing, the first Keychain to acquire the lock will run ssh-add immediately instead of first requiring Enter. This is technically safe, but no longer Keychain's default behavior since it's sub-optimal for some user scenarios. This can be enabled persistently via the [agent] immediate = true ~/.keychainrc configuration option.
Corrected keychain man pager integration (addresses #231). Color will be enabled when less is specifically detected, and -R will be enabled when not the default. Otherwise, keychain man output will not have color sequences. This fixes man page output on several systems.
GnuPG Scope Adjustment
Keychain 3.0.1 also includes an important change -- the scope of its GnuPG integration has been deliberately narrowed, for using gpg-agent as a drop-in replacement for ssh-agent has been removed (addresses #164).
While this may seem counterintuitive, this decision was made to improve security. When loading an encrypted SSH key with this feature enabled, ssh-add, invoked by Keychain, prompted for the key's original passphrase. If the key was not already present in GnuPG's private-key store, GnuPG then requested a new passphrase through Pinentry and stored a persistent copy. A user unfamiliar with this behavior who simply wanted to use gpg-agent in place of ssh-agent may not have understood why GnuPG was requesting another passphrase, and not realize that this new passphrase would be used to re-encrypt their private key in GnuPG's on-disk persistent key store, thus duplicating it.
Even more unfortunate, the GnuPG passphrase request for the re-encryption happens right after the user supplied a passphrase for decryption, not as a separate flow, adding to the potential confusion. It's very possible that the user might hit Enter and submit an empty passphrase for the second unexpected prompt, potentially leaving the imported GnuPG copy without passphrase protection on disk.
The conclusion I came to is that GnuPG's ssh-agent protocol compatibility functions are more of an SSH private key importer/bridge which exclusively uses GnuPG's own key store, rather than a drop-in replacement for ssh-agent -- so we shouldn't treat it as if it is a drop-in replacement. While I could instead have tried to smooth over the rough edges with GnuPG, I would be fighting against GnuPG's intended architecture too much, so it's best to simply define a clear boundary of what it makes sense to support and not support.
Keychain will continue to support gpgs:, gpge:, and gpga: for proving and warming native GPG signing and decryption capabilities. You can still use keychain wipe --gpg to flush its in-memory secret cache. This remains supported and does not remove persistent key material. Keychain will invoke GnuPG for those operations, but it will no longer start, configure, adopt, or otherwise manage the gpg-agent lifecycle. Consider Keychain an orchestrator of ssh-agent's lifecycle, and a helpful utility for GnuPG key warming, but no longer responsible for gpg-agent's lifecycle.
This gives Keychain a clear boundary:
- Keychain manages
ssh-agentand SSH keys. - Keychain supports warming native GPG signing and decryption capabilities.
- GnuPG remains responsible for
gpg-agent, Pinentry, configuration, and lifecycle. gpg-agentis not supported as a substitute forssh-agent.
Upgrading will not remove any SSH private keys that were previously imported into GnuPG's persistent key store. Users who previously enabled this behavior should review their GnuPG key storage separately. You can do this by looking in ~/.gnupg/sshcontrol for imported keygrips (40 character hex), and then looking for equivalent ~/.gnupg/private-keys-v1.d/<keygrip>.key files. If gpg-connect-agent 'KEYINFO --ssh-list --ssh-fpr=sha256' /bye lists any keys with "C" in the protection field, it means the key is not protected with a passphrase. It is recommended that you remove these keys via gpg-connect-agent "DELETE_KEY <keygrip>" /bye and then remove the corresponding entry in ~/.gnupg/sshcontrol, making sure you are not deleting any private keys that might be associated with a native OpenPGP key.
The following GnuPG-related changes were made:
- Removed the behavior previously selected by
--ssh-allow-gpgand--ssh-spawn-gpg. The command-line spellings remain accepted as deprecated, warning no-ops so existing scripts continue to run. Inherited GnuPG SSH sockets are ignored, and conventional SSH keys are handled only byssh-agent. - Left
gpg-agentstartup, configuration, pinentry, and lifecycle entirely to GnuPG. Keychain no longer launches or validates the daemon and no longer exposesgpg_argsorKEYCHAIN_GPG_AGENT_ARGS. - Changed bare
keychain wipeto clear SSH-agent identities only. Flushinggpg-agent's entire in-memory secret cache now requires an explicit--gpg; legacy--wipe allretains its original both-target behavior. No persistent key material is removed. - Separated GPG credential warm-up from SSH multi-terminal coordination. Keychain now performs each requested signing or decryption proof exactly once instead of using real signing operations as repeated status probes.
- Restored
--quickas a deterministic SSH-only compatibility shortcut. GPG key arguments are ignored under--quick; Keychain does not invoke GnuPG, while still establishing the SSH-agent environment. - Made
wipe --gpgauthoritative and idempotent. An absent agent is a successful no-op, while missing tooling, timeouts, transport failures, agent errors, and unconfirmed responses now fail with actionable diagnostics. - Made GPG credential warm-up authoritative. Signing and decryption failures now identify the affected key and operation, decryption stops immediately when its encryption proof cannot be prepared, and successful decryption is verified against the original plaintext.