fssh is a macOS-only SSH key management tool that solves two common pain points:
- Entering private key passphrase every SSH login → fssh lets you unlock with Touch ID or One-Time Password (OTP)
- Forgetting server aliases in
~/.ssh/config→ fssh provides an interactive shell with Tab completion for quick connections
If you are looking for a more secure alternative to ssh-agent on macOS, fssh provides: Biometric SSH Authentication: Use Touch ID to unlock SSH keys instead of typing long passphrases. Two-Factor Authentication (2FA) for SSH: Add an extra layer of security with TOTP/OTP for private keys. SSH Alias Discovery: Never forget your ~/.ssh/config aliases again with an interactive shell and tab-completion.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ SSH Client │ ──▶ │ fssh Agent │ ──▶ │Remote Server│
└─────────────┘ └─────────────┘ └─────────────┘
│
Touch ID or OTP
unlocks encrypted
private keys
Your SSH private keys are stored encrypted. They can only be decrypted after Touch ID fingerprint or OTP verification.
Touch ID prompt during SSH login:
Interactive shell for viewing and connecting to servers:
The easiest way to get started - one command does everything:
# Build the project
go build ./cmd/fssh
# Run interactive setup wizard
./fssh initThe wizard will guide you through:
- Choose authentication mode - Touch ID or OTP
- Install binary - Automatically copies fssh to /usr/local/bin
- Import SSH keys - Scans and imports keys from ~/.ssh/
- Configure auto-start - Sets up LaunchAgent for automatic startup
- Start agent - Launches fssh agent immediately
- Configure SSH client - Updates ~/.ssh/config automatically
After completion, you're ready to use SSH with Touch ID/OTP authentication!
For users who prefer step-by-step control:
# After downloading the source code, build it
go build ./cmd/fssh
# Install to system directory (requires admin privileges)
sudo cp fssh /usr/local/bin/Choose an authentication mode based on your device:
If your Mac has Touch ID (MacBook Pro/Air 2016+, iMac with Magic Keyboard, etc.):
fssh init --mode touchidIf your Mac doesn't have Touch ID (Mac Mini, older Macs, VMs, etc.):
fssh init --mode otpDuring OTP mode initialization:
- You'll set a password (at least 12 characters)
- A TOTP secret will be displayed - add it to an authenticator app (e.g., Google Authenticator, Authy)
- 10 recovery codes will be shown - save them securely
# Import your SSH private key (you'll be prompted for passphrase if the key has one)
fssh import --alias mykey --file ~/.ssh/id_rsa --ask-passphraseParameters:
--alias: A name for this key for easy reference--file: Path to the private key file--ask-passphrase: Add this if the private key is passphrase-protected
fssh agentOnce started, the Agent runs in the background, listening on ~/.fssh/agent.sock.
Edit ~/.ssh/config and add at the very beginning:
Host *
IdentityAgent ~/.fssh/agent.sock
This routes all SSH connections through fssh Agent.
# Use SSH normally - Touch ID or OTP prompt will appear automatically
ssh user@yourserver.comThe interactive wizard (fssh init) performs the following steps:
Step 1: Welcome & Initialization Check
- Displays welcome banner
- Checks if fssh is already initialized
- Prompts for confirmation if reinitializing
Step 2: Choose Authentication Mode
- Auto-detects Touch ID availability on your Mac
- Prompts you to choose between:
- Touch ID (recommended for supported devices)
- OTP (for devices without Touch ID or additional security)
Step 3: Initialize Authentication
- Executes the selected authentication mode setup
- For Touch ID: Generates and stores master key in macOS Keychain
- For OTP: Sets up password + TOTP with recovery codes
Step 4: Binary Installation
- Detects current executable location
- Copies fssh to
/usr/local/bin/(requires sudo) - Sets proper permissions (755)
Step 5: Import SSH Keys
- Scans
~/.ssh/directory for private keys - Detects standard key files:
id_rsa,id_ed25519,id_ecdsa, etc. - Identifies encrypted vs unencrypted keys
- Prompts for each key:
- Suggested alias (e.g.,
id_ed25519→ed25519) - Passphrase if key is encrypted
- Suggested alias (e.g.,
- Imports keys with encryption protection
Step 6: Configure LaunchAgent
- Creates plist file at
~/Library/LaunchAgents/com.fssh.agent.plist - Loads LaunchAgent for auto-start on login
- Configures agent to keep running
Step 7: Start Agent
- Waits for agent to start (up to 10 seconds)
- Verifies socket connection at
~/.fssh/agent.sock
Step 8: Configure SSH Client
- Prompts to update
~/.ssh/config - Creates backup before modification
- Prepends
IdentityAgentconfiguration for automatic SSH integration
For automation, scripts, or CI/CD:
# Skip all interactive prompts
fssh init --non-interactive --mode touchid
# Or specify mode directly
fssh init --mode otpTired of manually starting the Agent after each reboot? Set up auto-start:
# Copy the launch configuration file
cp contrib/com.fssh.agent.plist ~/Library/LaunchAgents/
# Load the service
launchctl load -w ~/Library/LaunchAgents/com.fssh.agent.plistCheck service status:
launchctl list | grep fsshNormal output looks like: - 0 com.fssh.agent (0 means running)
To restart the service:
launchctl kickstart -k gui/$(id -u)/com.fssh.agentTo stop the service:
launchctl unload ~/Library/LaunchAgents/com.fssh.agent.plistRun fssh or fssh shell to enter interactive mode:
$ fssh
fssh> list # List all hosts from ~/.ssh/config
fssh> search prod # Search for hosts containing "prod"
fssh> connect myserver # Connect to myserver
fssh> myserver # Or just type the hostname to connect
fssh> exit # Exit the shellTab completion is supported - type partial hostname and press Tab to autocomplete.
| Command | Description |
|---|---|
fssh init |
Interactive setup wizard (recommended) |
fssh init --interactive |
Explicitly run interactive wizard |
fssh init --mode touchid |
Initialize with Touch ID (non-interactive) |
fssh init --mode otp |
Initialize with OTP (non-interactive) |
fssh init --non-interactive --mode touchid |
Non-interactive mode for scripts/CI |
| Command | Description |
|---|---|
fssh import --alias name --file path --ask-passphrase |
Import a private key |
fssh list |
List imported keys |
fssh export --alias name --out path |
Export a key (backup) |
fssh remove --alias name |
Remove a key |
| Command | Description |
|---|---|
fssh agent |
Start the Agent |
fssh status |
Check status |
fssh shell |
Enter interactive shell |
Configuration file location: ~/.fssh/config.json
{
"socket": "~/.fssh/agent.sock",
"require_touch_id_per_sign": true,
"unlock_ttl_seconds": 600,
"log_level": "info",
"log_format": "plain"
}Configuration options:
| Option | Description | Default |
|---|---|---|
socket |
Agent socket path | ~/.fssh/agent.sock |
require_touch_id_per_sign |
Require verification for each SSH signature (secure mode) | true |
unlock_ttl_seconds |
Cache duration after verification (seconds) - no re-verification needed within this period | 600 (10 min) |
log_level |
Log level: debug/info/warn/error |
info |
log_format |
Log format: plain (readable) / json (structured) |
plain |
Secure Mode vs Convenience Mode:
require_touch_id_per_sign: true(Secure): Verification required for each SSH connection (or within TTL cache period)require_touch_id_per_sign: false(Convenience): All keys decrypted once at startup, no further verification needed
Cause: SSH client isn't using fssh Agent, or server doesn't support RSA-SHA2.
Solution:
- Verify Agent is running:
launchctl list | grep fssh - Verify
~/.ssh/confighasIdentityAgent ~/.fssh/agent.sock - Or set environment variable:
export SSH_AUTH_SOCK=~/.fssh/agent.sock
Cause: Terminal control issue.
Solution: Use ssh -tt to force TTY allocation:
ssh -tt user@serverCause: Service already loaded, or initialization incomplete.
Solution:
# Unload first
launchctl unload ~/Library/LaunchAgents/com.fssh.agent.plist
# Ensure initialization is complete
fssh init --mode touchid # or otp
# Reload
launchctl load -w ~/Library/LaunchAgents/com.fssh.agent.plistCause: Phone time out of sync, or TOTP was added incorrectly.
Solution:
- Ensure phone time is correct (enable automatic time setting)
- Delete the old entry in authenticator app and re-add
- If recovery is impossible, use recovery codes
If you saved recovery codes, use them to reset. Without recovery codes, you must reinitialize (losing imported keys):
# Warning: This deletes all imported keys!
rm -rf ~/.fssh
fssh init --mode otp
# Then re-import your keys- Encrypted key storage: Imported private keys are encrypted with AES-256-GCM - even if your computer is stolen, keys can't be decrypted without Touch ID/OTP
- Enable FileVault: macOS full-disk encryption provides additional protection
- Protect recovery codes: OTP recovery codes are like master keys - store them securely
- Regular backups: Use
fssh exportto backup important keys
- Encryption: AES-256-GCM + HKDF (independent salt/nonce per key file)
- Key derivation: PBKDF2 (100,000 iterations)
- TOTP standard: RFC 6238
- Compatibility: Fully compatible with OpenSSH ssh-agent protocol
This project was developed with assistance from TRAE AI software.

