Hardware-backed SSH key management.
sshenc generates SSH keys inside the Secure Enclave (macOS), TPM 2.0
(Windows/Linux), or a software fallback and serves them via a standard SSH
agent. Private key material never leaves the hardware -- it can't be
exported, copied, or stolen from disk.
Your existing SSH keys in ~/.ssh continue to work alongside hardware-backed
keys. Nothing breaks when you install sshenc.
Download latest release — pre-built binaries for macOS and Windows.
brew tap godaddy/sshenc
brew install sshencPre-built binaries for Apple Silicon and Intel. No Rust toolchain or Apple Developer account needed.
Download sshenc-x86_64-pc-windows-msvc.msi from the
latest release. Double-click
to install. The installer adds sshenc to your PATH and runs sshenc install
automatically. Uninstalling via Windows Settings runs sshenc uninstall.
scoop bucket add sshenc https://github.com/godaddy/scoop-sshenc
scoop install sshenc
sshenc installDownload sshenc-x86_64-unknown-linux-gnu.tar.gz from the
latest release. Extract and
copy the binaries to a directory in your PATH:
tar xzf sshenc-x86_64-unknown-linux-gnu.tar.gz
sudo cp sshenc sshenc-agent sshenc-keygen gitenc /usr/local/bin/
sshenc installRequires Rust 1.75+, Xcode command line tools, and macOS (Apple Silicon or T2 Mac).
git clone https://github.com/godaddy/sshenc.git
cd sshenc
make installNo code signing is required. cargo build produces linker-signed binaries
that macOS trusts for CryptoKit Secure Enclave access out of the box.
Requires Rust 1.75+ and Visual Studio Build Tools.
git clone https://github.com/godaddy/sshenc.git
cd sshenc
cargo build --workspace --release
# Copy binaries to a directory in your PATHRequires Rust 1.75+. For TPM support, install tpm2-tss development
libraries (libtss2-dev on Debian/Ubuntu, tpm2-tss-devel on Fedora).
git clone https://github.com/godaddy/sshenc.git
cd sshenc
cargo build --workspace --release
sudo cp target/release/sshenc target/release/sshenc-agent target/release/sshenc-keygen target/release/gitenc /usr/local/bin/
sshenc installsshenc installThis configures SSH to use the sshenc agent and starts it in the background. If the agent ever stops, it restarts automatically the next time you use SSH.
sshenc keygenThis creates a hardware-bound P-256 key in the Secure Enclave. When no
label is specified, it defaults to default — which writes the public key
to ~/.ssh/id_ecdsa.pub (the standard OpenSSH name for ECDSA keys) and
the agent presents it first. This means ssh, ssh-copy-id, and other
tools find it automatically without any flags.
For additional keys (e.g., separate GitHub accounts), use named labels:
sshenc keygen --label github-worksshenc export-pub | pbcopyPaste into GitHub → Settings → SSH keys → New SSH key.
ssh -T git@github.com
git clone git@github.com:you/repo.gitEverything works. SSH, git, scp, sftp — anything that reads ~/.ssh/config
uses the sshenc agent automatically.
If you only need one Secure Enclave key, the quick start above is all you
need. The default label makes the SE key behave like a standard SSH key —
ssh, scp, sftp, git, and ssh-copy-id all find it automatically.
ssh user@server # uses SE key, falls back to ~/.ssh keys
git push # same — just works
scp file.txt user@server: # same
ssh-copy-id user@server # copies SE public key to serverIf you have separate GitHub accounts (or different keys for different servers), create a key for each:
sshenc keygen --label github-personal
sshenc keygen --label github-work
sshenc keygen --label serversAdd each public key to the appropriate account (sshenc export-pub NAME | pbcopy).
Use gitenc to tell git which key to use:
# Clone with a specific identity
gitenc --label github-work clone git@github.com:mycompany/repo.git
gitenc --label github-personal clone git@github.com:me/my-repo.gitSet a repo to always use a specific key:
cd mycompany-repo
gitenc --config github-work
cd my-personal-repo
gitenc --config github-personalThis configures both SSH auth and commit signing in one command. After that, regular git commands use the right key automatically, and all commits are signed with your Secure Enclave key:
git pull
git push
git commit -m "this commit is signed" # signed automaticallyGitHub shows signed commits as "Verified" — add your key under GitHub → Settings → SSH and GPG keys → New SSH key → Key type: Signing Key.
Without --label, gitenc uses whatever key the agent offers first:
gitenc pull # default key, same as regular git# Specific key
sshenc ssh --label servers user@myserver.com
# Default (agent picks)
ssh user@myserver.comThese all read ~/.ssh/config automatically, so they just work:
scp file.txt user@server: # uses sshenc agent
sftp user@server # uses sshenc agent
ssh-copy-id user@server # copies your SE public key to the serverIf you used --label default, ssh-copy-id finds ~/.ssh/id_ecdsa.pub
automatically. For named keys, specify the key explicitly:
ssh-copy-id -i ~/.ssh/github-work.pub user@serverTo use a specific key with scp/sftp, use the SSH config approach:
sshenc openssh print-config --label servers --host myserver.comThis prints a config block you can add to ~/.ssh/config:
Host myserver.com
IdentityAgent ~/.sshenc/agent.sock
IdentityFile ~/.ssh/servers.pub
IdentitiesOnly yes
Git supports signing commits with SSH keys, and GitHub shows them as
"Verified." gitenc --config NAME sets this up automatically — every commit
in that repo is signed with your Secure Enclave key.
To enable it:
- Run
gitenc --config NAMEin your repo - Add the same public key to GitHub as a Signing Key: GitHub → Settings → SSH and GPG keys → New SSH key → Key type: Signing Key
That's it. Commits are signed automatically:
git commit -m "hardware-signed commit"
git log --show-signature # verify locallygitenc --config NAME sets these git config values locally on the repo:
core.sshCommand = sshenc ssh --label NAME --
gpg.format = ssh
user.signingkey = ~/.ssh/NAME.pub
commit.gpgsign = true
gitenc --config without a label keeps agent-default SSH authentication via
core.sshCommand = sshenc ssh -- and configures commit signing to use the
default public key (~/.ssh/id_ecdsa.pub, or the default key's recorded
public-key path when available).
sshenc runs a background SSH agent that serves your Secure Enclave keys.
sshenc install adds two lines to ~/.ssh/config:
Host *
IdentityAgent ~/.sshenc/agent.sock
PKCS11Provider /opt/homebrew/lib/libsshenc_pkcs11.dylib
IdentityAgent tells SSH to talk to the sshenc agent for key operations.
PKCS11Provider is a lightweight launcher — if the agent isn't running,
SSH loads this library which starts the agent automatically.
Private keys live inside the Secure Enclave hardware and never touch the
filesystem. The ~/.sshenc/keys/ directory stores only references and
public key caches:
~/.sshenc/keys/
github.handle # reference to the SE hardware key (not the key itself)
github.pub # cached public key bytes
github.ssh.pub # SSH-formatted public key (for identity selection)
The .handle files are opaque references — they tell CryptoKit which hardware
key to use, but contain no secret material. Copying them to another machine
won't work because the actual key is bound to this device's Secure Enclave.
No Apple Developer certificate or code signing is required. The project
uses CryptoKit (via a Swift static library), which works with the standard
ad-hoc linker signature that cargo build produces.
sshenc keygen # create SE key (label defaults to "default")
sshenc keygen --label NAME # create SE key with a specific label
sshenc list # list all SE keys
sshenc list --json # machine-readable output
sshenc inspect # detailed info for default key
sshenc inspect NAME # detailed info for a named key
sshenc export-pub # print default public key to stdout
sshenc export-pub | pbcopy # copy to clipboard
sshenc export-pub NAME # export a named key
sshenc delete NAME # delete a key (label required — no accidental deletes)
sshenc delete NAME --delete-pub # also remove the .pub filesOptions for keygen:
-C "comment"— custom comment (default: user@hostname)--write-pub PATH— write public key to a custom path--no-pub-file— don't write a .pub file at all--require-user-presence— require Touch ID / password for every sign
sshenc ssh --label NAME [ssh args...] # ssh with a specific SE key
sshenc ssh [ssh args...] # ssh with default agent keys
gitenc --label NAME [git args...] # git with a specific SE key
gitenc [git args...] # git with default agent keys
gitenc --config NAME # set current repo to always use NAME
gitenc --config # set current repo to use agent-default SSH auth and default commit signing keysshenc install # configure SSH + start agent
sshenc uninstall # revert SSH configsshenc agent # start as daemon (default)
sshenc agent --foreground # stay in terminal
sshenc agent --debug # verbose loggingThe agent starts automatically via sshenc install. You rarely need to
run it manually.
sshenc completions bash > /usr/local/etc/bash_completion.d/sshenc
sshenc completions zsh > /usr/local/share/zsh/site-functions/_sshenc
sshenc completions fish > ~/.config/fish/completions/sshenc.fishsshenc config init # create default config file
sshenc config path # show config file location
sshenc config show # print current configConfig file: ~/Library/Application Support/sshenc/config.toml
| Platform | Backend | Notes |
|---|---|---|
| macOS (Apple Silicon / T2) | Secure Enclave | CryptoKit via libenclaveapp |
| Windows (native) | TPM 2.0 | CNG Platform Crypto Provider |
| Windows (Git Bash) | TPM 2.0 | GIT_SSH_COMMAND bypasses MINGW SSH |
| WSL | Windows TPM via bridge | socat + npiperelay to named pipe |
| Linux (with TPM) | TPM 2.0 | tss-esapi via libenclaveapp |
| Linux (no TPM) | Software P-256 | Fallback, one-time warning |
All platform-specific crypto is provided by libenclaveapp.
- Private keys are generated inside and never leave the hardware security module
- Keys are ECDSA P-256 -- the only curve with hardware support on all platforms
- Keys are device-bound and non-exportable -- cannot be backed up or cloned
- Optional per-key Touch ID / Windows Hello / password for each signing operation
- Agent socket restricted to owner-only permissions (0600)
- Key references in
~/.sshenc/keys/restricted to owner-only (0700/0600) - No Keychain entitlements required on macOS -- uses CryptoKit, not Security.framework
- Software fallback on Linux stores keys on disk with restrictive permissions
See THREAT_MODEL.md for detailed analysis.
- P-256 only -- Ed25519 and RSA can't be created in hardware, but existing
keys in
~/.sshstill work as SSH handles them natively - Non-exportable -- losing the device means losing the hardware keys
- Software fallback -- on Linux without TPM, keys are not hardware-protected
cargo build --workspace
cargo test --workspace # 159 tests
cargo clippy --workspace --all-targets -- -D warnings
cargo fmt --all -- --checkmacOS requires Xcode command line tools (for swiftc).
Windows requires Visual Studio Build Tools (for MSVC linker).
See ARCHITECTURE.md, DEVELOPMENT.md, and CONTRIBUTING.md.
On Windows, sshenc uses the TPM 2.0 chip (present in virtually every modern
PC) via the CNG Microsoft Platform Crypto Provider. The security model is
the same as macOS Secure Enclave — keys are generated inside the TPM hardware
and never leave it.
The agent communicates via a Windows named pipe (\\.\pipe\openssh-ssh-agent)
instead of a Unix socket. sshenc install configures everything automatically.
- Adds
IdentityAgent \\.\pipe\openssh-ssh-agentto~/.ssh/config - Sets
GIT_SSH_COMMAND=C:\Windows\System32\OpenSSH\ssh.exeas a persistent user environment variable - Starts the agent as a detached background process
- Detects WSL distros and configures them (see below)
Step 2 is important: Git for Windows bundles its own MINGW SSH binary that
doesn't understand Windows named pipes. By setting GIT_SSH_COMMAND, all
git operations — including from Git Bash — use the real Windows OpenSSH
that talks to the sshenc agent.
sshenc uninstall reverses all steps including WSL configuration.
| Environment | Status | Notes |
|---|---|---|
| PowerShell | Works | IdentityAgent in ssh config → named pipe → agent |
| CMD | Works | Same as PowerShell |
| Git Bash | Works | GIT_SSH_COMMAND bypasses bundled MINGW SSH |
| VS Code terminal | Works | Uses whichever shell is configured |
| Windows Terminal | Works | Uses whichever shell is configured |
| WSL (git) | Works | GIT_SSH_COMMAND in .bashrc/.zshrc |
| WSL (ssh/scp) | Works with setup | Requires socat + npiperelay (see below) |
| PuTTY / Pageant | Not supported | Different protocol; use Windows OpenSSH |
Unlike macOS where CryptoKit uses opaque .handle files, Windows CNG
persists keys in the TPM's own key hierarchy. Only metadata and cached
public keys are stored on disk:
%APPDATA%\sshenc\keys\
github.pub # cached public key bytes
github.ssh.pub # SSH-formatted public key
github.meta # metadata (label, comment, auth policy, timestamp)
No key material is on disk. The TPM manages key persistence internally.
When generating a key with --require-user-presence or --auth-policy,
sshenc configures the TPM key to require Windows Hello authentication
(PIN, fingerprint, or facial recognition) for each signing operation:
sshenc keygen --label secure --require-user-presenceEach git push or ssh connection using that key will prompt for
Windows Hello verification.
WSL runs a real Linux kernel and can't directly access Windows named pipes.
sshenc install automatically detects your WSL distros and sets up a
bridge so everything works transparently:
# From any WSL shell — all use Windows TPM keys:
ssh user@server
git push
scp file user@server:
sftp user@serverIt installs socat and npiperelay into each distro and adds a bridge
to .bashrc/.zshrc that connects a Unix socket to the Windows named
pipe. The bridge starts automatically when you open a shell.
If installation of socat or npiperelay fails (e.g., no internet or
non-standard distro), it falls back to a git-only mode using
GIT_SSH_COMMAND so at least git push works.
If you create a new WSL distro later, just run sshenc install again —
it's idempotent and will pick up any unconfigured distros.
sshenc uninstall removes the configuration from all WSL distros.
Git for Windows includes a complete MSYS2/MINGW environment with its own
SSH binary at C:\Program Files\Git\usr\bin\ssh.exe. This MINGW SSH:
- Reads
~/.ssh/config(same file as Windows OpenSSH) - Does NOT support
IdentityAgentwith Windows named pipes - Does NOT support the Windows SSH agent
The GIT_SSH_COMMAND environment variable set by sshenc install tells
git to use C:\Windows\System32\OpenSSH\ssh.exe instead, which supports
all Windows-native features.
If you prefer not to set this globally, you can use gitenc instead —
it sets GIT_SSH_COMMAND per-invocation:
gitenc push # uses sshenc automatically
gitenc --label work push # specific keyMIT License. See LICENSE.