Skip to content
Helper to interface with Windows ssh-agent.exe service from Windows Subsystem for Linux (WSL)
Go CMake Shell
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
citrus Added debug output to lemonade backend Sep 14, 2019
cmake Initial commit Sep 5, 2019
static A bit of cleanup Jan 25, 2020
systray Switching to local systray implementation and adding -nolock argument Sep 29, 2019
.gitignore Update .gitginore to prevent ignoring vendor files Jan 23, 2020
COPYING Initial commit Sep 5, 2019 A bit of cleanup Jan 25, 2020 Initial commit Sep 5, 2019
go.mod Switching to local systray implementation and adding -nolock argument Sep 29, 2019
go.sum Switching to local systray implementation and adding -nolock argument Sep 29, 2019
gui.go Switching to local systray implementation and adding -nolock argument Sep 29, 2019


Helper to interface with Windows ssh-agent.exe service from WSL, replacement for ssh-agent-wsl.

GoDoc Go Report Card

Windows 10 has very convenient ssh-agent service (with support for persistence and Windows security). Unfortunately it is not accessible from WSL. This project aims to correct this situation by enabling access to SSH keys held by Windows own ssh-agent service from inside the Windows Subsystem for Linux.

My first attempt - ssh-agent-wsl was successful, but due to Windows interop restrictions it required elaborate life-time management on the WSL side. Starting with build 17063 (which was many updates ago) Windows implemented AF_UNIX sockets. This makes it possible to remove all trickery from WSL side greatly simplifying everything.

wsl-ssh-agent-gui.exe is a simple "notification tray" applet which maintains AF_UNIX ssh-agent compatible socket on Windows end. It proxes all requests from this socket to ssh-agent.exe via named pipe. The only thing required on WSL end for it to work is to make sure that WSL SSH_AGENT_SOCK points to proper socket path. The same socket could be shared by any/all WSL sessions.

As an additional bonus wsl-ssh-agent-gui.exe could work as lemonade server so you could send your clipboard from tmux or neovim remote session back to your windows box over SSH secured connection easily. Running lemonade.exe console application in the background on Windows was always a bit tedious. Please, read lemonade documentation for details on how this works and parameters description.

SECURITY NOTICE: All the usual security caveats applicable to WSL apply. Most importantly, all interaction with the Win32 world happens with the credentials of the user who started the WSL environment. In practice, if you allow someone else to log in to your WSL environment remotely, they may be able to access the SSH keys stored in your ssh-agent. This is a fundamental feature of WSL; if you are not sure of what you're doing, do not allow remote access to your WSL environment (i.e. by starting an SSH server).

COMPATIBILITY NOTICE: wsl-ssh-agent-gui was tested on Windows 10 1903 with multiple distributions and should work on anything starting with 1809 - beginning with insider build 17063 and would not work on older versions of Windows 10, because it requires AF_UNIX socket support feature.


From binaries

Download from the releases page and unpack it in a convenient location.

From source

It is possible to build sources on Windows - after all it is written in go, however I am building under WSL, so you will need at least go 1.13 installed under WSL along with regular build essentials and recent cmake: sudo apt install build-essential cmake After that just execute ./


  1. Ensure that on Windows side ssh-agent.exe service (OpenSSH Authentication Agent) is started and has your keys. (After adding keys to Windows ssh-agent.exe you may remove them from your wsl home .ssh directory - just do not forget to adjust IdentitiesOnly directive in your ssh config accordingly. Keys are securely persisted in Windows registry, available for your account only). You may also want to switch its startup mode to "automatic". Using powershell with elevated privileges (admin mode):
	Start-Service ssh-agent
	Set-Service -StartupType Automatic ssh-agent
  1. Run wsl-ssh-agent-gui.exe with arguments which make sense for your usage. Basically there are several ways:

    • Using -socket option specify "well known" path on Windows side and then properly specify the same path in every WSL session:

      Windows: wsl-ssh-agent-gui.exe -socket c:\wsl-ssh-agent\ssh-agent.sock

      WSL: export SSH_AUTH_SOCK=/mnt/c/wsl-ssh-agent/ssh-agent.sock

    • You could avoid any actions on WSL side by manually setting SSH_AUTH_SOCK and WSLENV=SSH_AUTH_SOCK/up on Windows side (see note below).

    • Using -setenv option allows application automatically modify user environment, so every WSL session started while wsl-ssh-agent-gui.exe is running will have proper SSH_AUTH_SOCK available to it (using WSLENV). By default socket path points to user temporary directory. Usual Windows user environment modification rules are applicable here (see note below).

NOTE: Setting SSH_AUTH_SOCK environment on Windows side may (and probably will) interfere with some of Windows OpenSSH. As far as I could see presently utilities in Windows\System32\OpenSSH expect this environment variable to be either empty or set to proper ssh-agent.exe pipe, otherwise they cannot read socket:

	if (getenv("SSH_AUTH_SOCK") == NULL)

To avoid this and still be able to use -setenv and automatically generated socket path use -envname to specify variable name to set. Later on WSL side you could use:


When wsl-ssh-agent-gui.exe is running you could see what it is connected to by clicking on its icon in notification tray area and selecting About. At the bottom of the message you would see something like:

Socket path:
Pipe name:
Lemonade stand:

For security reasons unless -nolock argument is specified program will refuse access to ssh-agent.exe pipe when user session is locked, so any long running background jobs in WSL which require ssh may fail.


Run wsl-ssh-agent-gui.exe -help

Helper to interface with Windows ssh-agent.exe service from WSL

	<<version>> (<<go version>>)
	<<git hash>>

	wsl-ssh-agent-gui [options]


		Enable verbose debug logging
  -envname name
		Environment variable name to hold socket path (default "SSH_AUTH_SOCK")
		Show help
  -lemonade list
		Semicolon separated list of lemonade "server" options (TCP port, Allow IP Range, Line Endings)
		Provide access to ss-agent.exe even when user session is locked
  -pipe name
		Pipe name used by Windows ssh-agent.exe
		Export environment variable with 'envname' and modify WSLENV.
  -socket path
		Auth socket path (max 108 characters)


Putting it all together nicely - remote here refers to your wsl shell or some other box or virtual machine you could ssh to. Assuming that lemonade is in your path on remote and you installed win32yank somewhere in drvfs location.

I auto-start wsl-ssh-agent-gui.exe on logon on my Windows box using following command line:

wsl-ssh-agent-gui.exe -setenv -envname=WSL_AUTH_SOCK -lemonade=2489;

In my .bashrc I have:


and my .ssh/config entries used to ssh to remote have port forwarding enabled:

RemoteForward 2489

On remote my tmux.conf includes following lines:

# when on WSL always use win32yank
# Do not forget to link win32yank in your path like so: ln -s $USERPROFILE/.wsl/win32yank.exe ~/.local/bin/win32yank
# we are relying on WSLENV being set to "USERPROFILE/up" outside of WSL

set -g set-clipboard off
if-shell 'if [ $(uname -a | grep -c Microsoft) = 1 ]; then true; else false; fi' \
	`bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.local/bin/win32yank -i --crlf" ; bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.local/bin/win32yank -i --crlf"' \
	'bind-key -T copy-mode-vi Enter send-keys -X copy-pipe-and-cancel "~/.local/bin/lemonade copy" ; bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "~/.local/bin/lemonade copy"'

And my neovim configuration file init.vim on remote has following lines:

let s:u_wsl = 1
let _ = system("uname -a | grep -cq Microsoft")
if v:shell_error
	let s:u_wsl = 0

" ----- Clipboard
set clipboard+=unnamedplus
if s:u_wsl
	" ----- on WSL try to use win32yank if available
	" ----- we are relying on WSLENV being set to "USERPROFILE/up" outside of WSL
	let s:win32yank = $USERPROFILE . '/.wsl/win32yank.exe'
	if filereadable(s:win32yank)
		let g:clipboard = {
			\   'name': 'win32yank',
			\   'copy': {
			\      '+': s:win32yank . ' -i --crlf',
			\      '*': s:win32yank . ' -i --crlf',
			\    },
			\   'paste': {
			\      '+': s:win32yank . ' -o --lf',
			\      '*': s:win32yank . ' -o --lf',
			\   },
			\   'cache_enabled': 0,
			\ }
elseif has("unix")
	" ----- on UNIX ask lemonade to translate line-endings
	if executable('lemonade')
		let g:clipboard = {
			\   'name': 'lemonade',
			\   'copy': {
			\      '+': 'lemonade copy',
			\      '*': 'lemonade copy',
			\    },
			\   'paste': {
			\      '+': 'lemonade paste --line-ending lf',
			\      '*': 'lemonade paste --line-ending lf',
			\   },
			\   'cache_enabled': 0,
			\ }

Now you could open your WSL in terminal of your choice - mintty, cmd, Windows terminal, ssh to your remote using keys stored in Windows ssh-agent.exe without entering any additional passwords and have your clipboard content back on Windows transparently.


Licensed under the GNU GPL version 3 or later,

This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

See the COPYING file for license details.

You can’t perform that action at this time.