diff --git a/Makefile b/Makefile
index 709bdb16a6f6c9ec919c6912bd33e8b947e83f8b..7f12624bb89c6824331b9b5ab2ea72ee8c8a5722 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 #########################################################################
 # Multiplatform build and packaging recipes for BitmaskVPN
-# (c) LEAP Encryption Access Project, 2019
+# (c) LEAP Encryption Access Project, 2019-2020
 #########################################################################
 
 .PHONY: all get build build_bitmaskd icon locales generate_locales clean
@@ -150,8 +150,8 @@ TGZ_NAME = bitmask-vpn_${VERSION}-src
 TGZ_PATH = $(shell pwd)/build/${TGZ_NAME}
 tgz:
 	@mkdir -p $(TGZ_PATH)
-	@git archive HEAD | tar -x -C $(TGZ_PATH)
-	@cd build/ && tar cvzf bitmask-vpn_$(VERSION).tgz ${TGZ_NAME}
+	git archive HEAD | tar -x -C $(TGZ_PATH)
+	@cd build/ && tar czf bitmask-vpn_$(VERSION).tgz ${TGZ_NAME}
 	@rm -rf $(TGZ_PATH)
 
 gen_pkg_win:
@@ -209,8 +209,11 @@ package_snap:
 package_deb:
 	@make -C build/${PROVIDER} pkg_deb
 
-package_win:
-	@make -C build/${PROVIDER} pkg_win
+package_win_stage_1:
+	@make -C build/${PROVIDER} pkg_win_stage_1
+
+package_win_stage_2:
+	@make -C build/${PROVIDER} pkg_win_stage_2
 
 package_osx:
 	@make -C build/${PROVIDER} pkg_osx
diff --git a/branding/scripts/provider.py b/branding/scripts/provider.py
index 745555694ba78b952669607a77cae60715d52694..b1beab9154a05ba432bae4aaca0644251674da07 100644
--- a/branding/scripts/provider.py
+++ b/branding/scripts/provider.py
@@ -5,7 +5,7 @@ import os
 def getDefaultProvider(config):
     provider = os.environ.get('PROVIDER')
     if provider:
-        print('[+] Got provider {} from environemnt'.format(provider))
+        print('[+] Got provider {} from environment'.format(provider))
     else:
         print('[+] Using default provider from config file')
         provider = config['default']['provider']
diff --git a/branding/templates/makefile/Makefile b/branding/templates/makefile/Makefile
index 5994e19e0ceffdabf3b66c742e750d364fe1707a..c042d27909d97695ce5ea4ee64bff0c6ae64d74a 100755
--- a/branding/templates/makefile/Makefile
+++ b/branding/templates/makefile/Makefile
@@ -34,11 +34,19 @@ pkg_win: staging\openvpn\openvpn.exe
 	copy ..\bin\bitmask-helper staging\bitmask_helper.exe
 	"C:\Program Files (x86)\NSIS\makensis.exe" windows/$(APPNAME)-installer.nsi
 else
-pkg_win: staging/openvpn/openvpn.exe
-	echo "[+] building windows"
+pkg_win_stage_1: staging/openvpn/openvpn.exe
+	echo "[+] building windows [stage1]"
+	cp ../bin/windows/bitmask-vpn staging/bitmask-vpn.exe
+	cp ../bin/windows/bitmask-helper-go staging/helper.exe
+	touch windows/uninstall.exe
+	makensis -DUNINSTALLER windows/$(APPNAME)-installer.nsi
+	mv dist/produce-bitmask-uninstaller.exe ../../deploy/
+
+pkg_win_stage_2: staging/openvpn/openvpn.exe
+	echo "[+] building windows [stage2]"
 	cp ../bin/windows/bitmask-vpn staging/bitmask-vpn.exe
-	#cp ../bin/windows/bitmask-helper staging/bitmask_helper.exe
 	cp ../bin/windows/bitmask-helper-go staging/helper.exe
+	cp ../windows/staging/uninstall-signed.exe windows/uninstall.exe
 	makensis windows/$(APPNAME)-installer.nsi
 	mv dist/$(APPNAME)-$(VERSION).exe ../../deploy/
 endif
@@ -117,10 +125,10 @@ staging\openvpn\openvpn.exe:
 	copy .\staging\openvpn\openvpn.exe .\staging
 	copy .\staging\openvpn\*.dll .\staging
 staging/openvpn/openvpn.exe:
-	mkdir -p staging/openvpn
+	@mkdir -p staging/openvpn
 	wget https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe -O $(STAGING)/openvpn/tap-windows.exe
 	wget https://downloads.leap.se/thirdparty/windows/openvpn-x86_64-w64-mingw32.tar.bz2 -O $(STAGING)/openvpn/openvpn.tar.bz2
-	tar xvjf $(STAGING)/openvpn/openvpn.tar.bz2 -C $(STAGING)/openvpn/
+	@tar xjf $(STAGING)/openvpn/openvpn.tar.bz2 -C $(STAGING)/openvpn/
 	cp $(STAGING)/openvpn/bin/openvpn.exe $(STAGING)/openvpn
 	cp $(STAGING)/openvpn/bin/*.dll $(STAGING)
 	cp $(STAGING)/openvpn/lib/engines-1_1/*.dll $(STAGING)
diff --git a/branding/templates/windows/template.nsi b/branding/templates/windows/template.nsi
index e3b2379f51ea47ec25945b7fd2285bc8f03b0036..6fa189b28f4bb5865a6d403ac9dc5a6520ba434f 100755
--- a/branding/templates/windows/template.nsi
+++ b/branding/templates/windows/template.nsi
@@ -2,10 +2,10 @@
   !echo "Stage 1: building uninstaller"
   ; we don't care about this installer, in this step we just pick the uninstaller
   ; to be able to sign it.
-  OutFile "$%TEMP%\tempinstaller.exe"
-  SetCompressor off
+  OutFile "..\dist\produce-bitmask-uninstaller.exe"
+  SetCompress off
 !else
-  !echo "Stage 2: building installer"
+  !echo "Stage 2: building main installer"
   Outfile "..\dist\$applicationName-$version.exe"
   SetCompressor /SOLID lzma
 !endif
@@ -59,7 +59,6 @@ Section "InstallFiles"
   DetailPrint "Trying to uninstall any older helper..."
   ClearErrors
   Delete 'C:\Program Files\$applicationName\bitmask_helper.exe'
-  IfErrors 0 noErrorHelper
 
   DetailPrint "Trying to uninstall new helper..."
   ClearErrors
@@ -67,14 +66,14 @@ Section "InstallFiles"
   ExecWait '"$INSTDIR\helper.exe" remove'
   ClearErrors
   Delete 'C:\Program Files\$applicationName\helper.exe'
-  IfErrors 0 noErrorHelper
+  ;IfErrors 0 noErrorHelper
 
   ; uninstalling old nssm helper - could fail if it isn't there, or if nssm is not there...
   ClearErrors
   DetailPrint "Trying to uninstall an old style helper..."
   ExecWait '"$INSTDIR\nssm.exe" stop $applicationNameLower-helper'
   ExecWait '"$INSTDIR\nssm.exe" remove $applicationNameLower-helper confirm'
-  IfErrors 0 noErrorHelper
+  ;IfErrors 0 noErrorHelper
   DetailPrint "Failed to stop nssm-style helper, maybe it was not there"
 
   ; let's try to stop it in case it's the new style helper -- we need to do it to be able to overwrite it.
@@ -82,16 +81,10 @@ Section "InstallFiles"
   ClearErrors
   DetailPrint "Trying to uninstall a new style helper..."
   ExecWait '"$INSTDIR\bitmask_helper.exe" stop'
-  IfErrors 0 noErrorHelper
+  ;IfErrors 0 noErrorHelper
   DetailPrint "Failed to stop new-style helper, maybe it was not there"
 
-  ClearErrors
-  DetailPrint "Trying to uninstall a new style helper..."
-  ExecWait '"$INSTDIR\helper.exe" stop'
-  IfErrors 0 noErrorHelper
-  DetailPrint "Failed to stop new-style helper, maybe it was not there"
-
-  noErrorHelper:
+  ;noErrorHelper:
   
   ; now we try to delete the systray, locked by the app - just to know if another instance of FoobarVPN is running.
   ClearErrors
@@ -105,9 +98,7 @@ Section "InstallFiles"
 
   noDelError:
 
-  ; TODO -- write uninstaller in a separate stage, so we can sign it!
   SetOutPath $INSTDIR 
-  WriteUninstaller $INSTDIR\uninstall.exe
 
   ; Add ourselves to Add/remove programs
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayName" "$applicationName"
@@ -125,6 +116,7 @@ Section "InstallFiles"
   createShortCut "$SMPROGRAMS\$applicationName\$applicationName.lnk" "$INSTDIR\bitmask-vpn.exe" "" "$INSTDIR\icon.ico"
 
   File "readme.txt"
+  File "uninstall.exe"
   File "/oname=icon.ico" "..\assets\icon.ico"
 
   $extra_install_files
@@ -156,6 +148,8 @@ Section /o "TAP Virtual Ethernet Adapter" SecTAP
 	DetailPrint "TAP installed!"
 SectionEnd
 
+!ifdef UNINSTALLER
+; this section should not be in the main installer, because it will cause warnings.
 Section "Uninstall"
   ; we uninstall the new-style go helper
   ExecWait '"$INSTDIR\bitmask_helper.exe" stop'
@@ -180,9 +174,17 @@ Section "Uninstall"
   Delete $INSTDIR\uninstall.exe
   RMDir $INSTDIR
 SectionEnd
+!endif
 
 Function .onInit
+!ifdef UNINSTALLER
+        ; If UNINSTALLER is defined, then we don't do anything execpt write out the uninstaller.
+        SetSilent silent
+        WriteUninstaller "c:\bitmask-uninstall.exe"
+        Quit ; bail out quickly when running the UNINSTALLER stage.
+!else
 	!insertmacro SelectByParameter ${SecTAP} SELECT_TAP 1
+!endif
 FunctionEnd
 
 ;----------------------------------------
diff --git a/docker/Makefile b/docker/Makefile
index 80ca9dd73cf54ade1e96c1e3bdb06dca5c429196..ac674cb7d000beb9189527b69ca71f9679db3bbc 100644
--- a/docker/Makefile
+++ b/docker/Makefile
@@ -1,23 +1,86 @@
+#########################################################################
+# Docker build environment for BitmaskVPN
+# (c) LEAP Encryption Access Project, 2019-2020
+#########################################################################
+
 IMAGE=bitmask-vpn
 REGISTRY=registry.0xacab.org/leap/docker
 
+PROVIDER=riseup
+PRODUCT=$(shell echo $(PROVIDER) | sed -e "s/\b\(.\)/\u\1/g")VPN
+
+UNINSTALLER_NSIS="../deploy/produce-bitmask-uninstaller.exe"
+UNINSTALLER_WINE=".wine/drive_c/bitmask-uninstall.exe"
+UNINSTALLER_STAGING="../build/windows/staging/uninstall.exe"
+UNINSTALLER_SIGNED="../build/windows/staging/uninstall-signed.exe"
+SIGNCODE=osslsigncode
+SECRETS="leap/secrets/windoze"
+
 build:
 	mkdir -p mods && cp ../go.mod ../go.sum mods/ && cp -r ../packages mods/
 	docker build -t $(REGISTRY)/$(IMAGE):latest .
+deps:
+	sudo apt install osslsigncode
 push:
 	docker push $(REGISTRY)/$(IMAGE):latest
 prune:
 	docker system prune
 login:
 	docker login $(REGISTRY)
-package_win:
+
+package_win: clean_win package_win_stage_1 sign_win_uninstaller_in_host package_win_stage_2 sign_win_installer_in_host
+
+clean_win:
+	@rm ../deploy/*.exe
+
+package_win_stage_1:
 	docker run \
+	    -e PROVIDER=$(PROVIDER) \
 	    -e XBUILD=win \
-	    -e PROVIDER=riseup \
+	    -e STAGE=1 \
 	    -v `python3 -c 'import os; print(os.path.abspath(".."))'`:/bitmask-vpn.host \
 	    --cpus="4" \
 	    -it --rm \
 	    $(REGISTRY)/$(IMAGE):latest  \
 	    /builder.sh
+
+sign_win_uninstaller_in_host:
+	@rm -f $(UNINSTALLER_STAGING)
+	@rm -f $(UNINSTALLER_WINE)
+	@echo "[+] running nsis uninstaller under wine..."
+	@wine $(UNINSTALLER_NSIS) || echo "[+] wine finished (it exits badly)"
+	@cp ~/$(UNINSTALLER_WINE) $(UNINSTALLER_STAGING) && echo "[+] copied uninstaller to staging dir:" $(UNINSTALLER_STAGING)
+	@$(SIGNCODE) sign \
+	    -certs ~/$(SECRETS)/win.crt \
+	    -key ~/$(SECRETS)/key.pem \
+	    -pass `gpg --decrypt ~/$(SECRETS)/../windoze.gpg` \
+	    -n "$(PRODUCT) Uninstaller" -i "https://leap.se/" \
+	    -in $(UNINSTALLER_STAGING) \
+	    -out $(UNINSTALLER_SIGNED)
+	@echo "[+] signed uninstaller in:" $(UNINSTALLER_SIGNED)
+
+package_win_stage_2:
+	docker run \
+	    -e PROVIDER=$(PROVIDER) \
+	    -e XBUILD=win \
+	    -e STAGE=2 \
+	    -v `python3 -c 'import os; print(os.path.abspath(".."))'`:/bitmask-vpn.host \
+	    --cpus="4" \
+	    -it --rm \
+	    $(REGISTRY)/$(IMAGE):latest  \
+	    /builder.sh
+
+sign_win_installer_in_host:
+	$(eval INSTALLER := $(shell ls -tr ../deploy/$(PRODUCT)*.exe | tail -n 1))
+	$(eval INSTALLER_SIGNED := $(shell echo $(INSTALLER) | sed 's/\.exe/-signed.exe/'))
+	@$(SIGNCODE) sign \
+	    -certs ~/$(SECRETS)/win.crt \
+	    -key ~/$(SECRETS)/key.pem \
+	    -pass `gpg --decrypt ~/$(SECRETS)/../windoze.gpg` \
+	    -n "$(PRODUCT) Installer" -i "https://leap.se/" \
+	    -in $(INSTALLER) \
+	    -out ../deploy/$(INSTALLER_SIGNED)
+	@echo "[+] signed installer in:" $(INSTALLER_SIGNED)
+
 shell:
 	docker run -v `python3 -c 'import os; print(os.path.abspath(".."))'`:/bitmask-vpn.host -it --rm $(REGISTRY)/$(IMAGE):latest  /bin/bash
diff --git a/docker/builder.sh b/docker/builder.sh
index 2767d61fa9a8eb4145e75ebded34fa3559e6e242..a7a2a40d0b6120f8d8d0dbdf8831eeeee9517da0 100755
--- a/docker/builder.sh
+++ b/docker/builder.sh
@@ -10,7 +10,18 @@ make prepare
 make build
 case $XBUILD in
     win)
-        make package_win
+        if [ "$STAGE" = "1" ]; then
+            echo ""
+            echo "[+] Bulding WIN installer >>>>>>>>>>> STAGE 1"
+            make package_win_stage_1
+            echo ""
+        fi
+        if [ "$STAGE" = "2" ]; then
+            echo ""
+            echo "[+] Building WIN installer >>>>>>>>>> STAGE 2"
+            make package_win_stage_2
+            echo ""
+        fi
         ;;
     osx)
         make package_osx
diff --git a/pkg/helper/windows.go b/pkg/helper/windows.go
index 28727028593fb0c6c9e4173adba4e2410eb7684f..5eef863c9f7736302633820afe6605599630eb19 100644
--- a/pkg/helper/windows.go
+++ b/pkg/helper/windows.go
@@ -82,8 +82,6 @@ func parseCliArgs() {
 		runService(svcName, true)
 		return
 	case "install":
-		// TODO get binary name -- or maybe not, since in this way we make sure
-		// that all bitmask-vpn brandings are mutually exclusive.
 		err = installService(svcName, "bitmask-helper service")
 	case "remove":
 		err = removeService(svcName)