This is a very simple user launch agent for ssh-agent.
This is typically useful if you are using a YubiKey, or otherwise want to use
sk- keys, that the vanilla macOS ssh-agent does not support.
# Install and start updated agent (the tap must be trusted: the formula
# depends on farcloser/brews/openssh)
brew tap farcloser/brews && brew trust farcloser/brews
brew install farcloser/brews/ssh-agent
brew services start ssh-agent
# Get the socket location in profile
printf '. "$HOME/.posh_ssh'"\n" >> ~/.profile
. ~/.profile
# Optional: retire Apple's ssh-agent. Not required — ours listens on its own
# socket, and Apple's is socket-activated: it only ever runs when a client
# connects to its listener, which nothing does once SSH_AUTH_SOCK points at
# ours. `disable` covers every future login and, on macOS 26, is the only
# lever: SIP refuses `bootout` and `kill` of an Apple LaunchAgent, so the
# listener stays until you log out.
launchctl disable gui/$(id -u)/com.openssh.ssh-agentThe agent listens on $HOME/.ssh/agent.sock, and ~/.posh_ssh (written by the
launch script) exports it as SSH_AUTH_SOCK.
It used to be $HOME/.ssh/agent. That path is no longer usable: OpenSSH 10 (and
the macOS 26 system ssh-agent) put ssh-agent's own default socket in a directory
at $HOME/.ssh/agent/s.*, and the system agent creates that directory the first
time anything talks to it — a socket file cannot be bound over it. After upgrading,
open a new shell (or re-source ~/.posh_ssh) so clients pick up the new path.
Be sure to either have brew and the formula openssh installed, or alternatively
that you do have a compatible ssh-agent in your PATH.
Git clone.
Then:
# Install
./install.sh destination_folder
# Get the socket location in profile
printf '. "$HOME/.posh_ssh'"\n" >> ~/.profile
. ~/.profileTo uninstall:
# Remove our service
launchctl remove world.farcloser.ssh_agent
# Re-enable the system service: `enable` covers the next login (SIP refuses
# to bootstrap an Apple LaunchAgent by hand — log out and back in)
launchctl enable gui/$(id -u)/com.openssh.ssh-agent./install.sh will:
- disable (
launchctl disable) the systemssh-agentlaunch agent — hygiene, not a requirement (ours has its own socket): it stays loaded until the next login, which is whendisabletakes effect, since SIP refusesbootoutandkillof an Apple LaunchAgent - copy the run script
farcloser-ssh-agentintodestination_folder - install and start a user launch agent in
~/Library/LaunchAgents/world.farcloser.ssh_agent.plist(the brew formula'sbrew services startdoes the same, minus the system-agent step — hence the twolaunchctllines in the TL;DR):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>world.farcloser.ssh_agent</string>
<key>ProgramArguments</key>
<array>
<string>/Users/dmp/Applications/bin/farcloser-ssh-agent</string>
<string>/Users/dmp/Applications/homebrew/bin/ssh-agent</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/dmp/Applications/homebrew/var/log/world.farcloser.ssh_agent-stdout.log</string>
<key>StandardErrorPath</key>
<string>/Users/dmp/Applications/homebrew/var/log/world.farcloser.ssh_agent-stderr.log</string>
</dict>
</plist>./farcloser-ssh-agent does:
- check if it has been launched already
- clean-up any possible socket remnant (
~/.ssh/agent.sock, and a socket file left at the old~/.ssh/agentpath by earlier versions — never the directory the system agent keeps there) - write
~/.posh_ssh, which exportsSSH_AUTH_SOCKfor your shell - exec the ssh-agent binary that has been passed as an argument, bound to the socket