diff --git a/.gitignore b/.gitignore
index 4c21651c3266e977c2fc612e86d13b8bf2662a5f..acc2769980c098fdd151e333138957cee58f3175 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+__pycache__
+build
+dist
 /bitmask-vpn
 cmd/bitmask-vpn/bitmask-vpn
 /bitmask-helper
diff --git a/Makefile b/Makefile
index 6f54daf7536e34c001f011818c583402d3e4b402..8f4d836c55d4ac24163c34f45a8047fe0a7b910a 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ TAGS ?= gtk_3_18
 PROVIDER ?= $(shell grep ^'provider =' branding/config/vendor.conf | cut -d '=' -f 2 | tr -d "[:space:]")
 PROVIDER_CONFIG ?= branding/config/vendor.conf
 DEFAULT_PROVIDER = branding/assets/default/
+VERSION ?= $(shell git describe)
 
 all: icon locales get build
 
@@ -22,13 +23,35 @@ endif
 	cd branding/assets && ln -s ${PROVIDER} default
 
 prepare: generate relink_default
+	mkdir -p build/${PROVIDER}/bin/
 	branding/scripts/check-ca-crt.py ${PROVIDER} ${PROVIDER_CONFIG}
 
+prepare_win:
+	mkdir -p build/${PROVIDER}/windows/
+	cp -r branding/templates/windows build/${PROVIDER}
+	VERSION=${VERSION} PROVIDER_CONFIG=${PROVIDER_CONFIG} branding/scripts/generate-win.py build/${PROVIDER}/windows/data.json
+	cd build/${PROVIDER}/windows && python3 generate.py
+	# TODO create build/PROVIDER/assets/
+	# TODO create build/PROVIDER/staging/
+
+prepare_osx:
+	echo "osx..."
+
+prepare_snap:
+	echo "snap..."
+
+prepare_debian:
+	echo "debian..."
+
+prepare_all: prepare prepare_win prepare_osx prepare_snap
+
 build: $(foreach path,$(wildcard cmd/*),build_$(patsubst cmd/%,%,$(path)))
 
 build_%:
 	go build -tags $(TAGS) -ldflags "-X main.version=`git describe --tags`" -o $* ./cmd/$*
 	strip $*
+	mkdir -p build/bin
+	mv $* build/bin/
 
 test:
 	go test -tags "integration $(TAGS)" ./...
@@ -41,7 +64,7 @@ build_win:
 
 clean:
 	make -C icon clean
-	rm bitmask-vpn
+	rm build/bitmask-vpn
 
 icon:
 	make -C icon
diff --git a/branding/config/vendor.conf b/branding/config/vendor.conf
index 12fca4e51e540998a3a22afe48826f802ad9519c..f0f42c18d0f75f248d14e68e03e9ac14dfeb6a06 100644
--- a/branding/config/vendor.conf
+++ b/branding/config/vendor.conf
@@ -13,6 +13,7 @@ providerURL         = riseup.net
 apiURL              = https://api.black.riseup.net/
 caURL               = https://black.riseup.net/ca.crt
 
+infoURL             = https://riseup.net/vpn
 tosURL              = https://riseup.net/tos
 helpURL             = https://riseup.net/support
 
@@ -27,11 +28,15 @@ donateURL           = https://riseup.net/donate
 name                = Calyx
 applicationName     = CalyxVPN
 binaryName          = calyx-vpn
+
 providerURL         = https://calyx.net
-tosURL              = https://calyx.net/tos
-helpURL             = https://calyx.net/support
 apiURL              = https://api.calyx.net:4430/
 caURL               = https://calyx.net/ca.crt
+
+infoURL             = https://calyx.net/
+tosURL              = https://calyx.net/tos
+helpURL             = https://calyx.net/support
+
 geolocationAPI      = https://api.black.riseup.net:9001/json
 
 askForDonations     = true
diff --git a/branding/scripts/TODO b/branding/scripts/TODO
new file mode 100644
index 0000000000000000000000000000000000000000..95ca3450156f5198cd9ba5494cfa6aa4dfff5eb2
--- /dev/null
+++ b/branding/scripts/TODO
@@ -0,0 +1,2 @@
+-[ ] sanity check scrit
+-[ ] assets convertion script
diff --git a/branding/scripts/__init__.py b/branding/scripts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/branding/scripts/generate-win.py b/branding/scripts/generate-win.py
new file mode 100755
index 0000000000000000000000000000000000000000..fb15f22a161005c4919cd1265cf2009a3bd1e49e
--- /dev/null
+++ b/branding/scripts/generate-win.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+
+import json
+import os
+import sys
+
+import configparser
+
+from provider import getDefaultProvider
+from provider import getProviderData
+
+
+VERSION = os.environ.get('VERSION', 'unknown')
+
+
+def writeOutput(data, outfile):
+
+    with open(outfile, 'w') as outf:
+        outf.write(json.dumps(data))
+
+
+if __name__ == "__main__":
+    env_provider_conf = os.environ.get('PROVIDER_CONFIG')
+    if env_provider_conf:
+        if os.path.isfile(env_provider_conf):
+            print("[+] Overriding provider config per "
+                  "PROVIDER_CONFIG variable")
+            configfile = env_provider_conf
+
+    config = configparser.ConfigParser()
+    config.read(configfile)
+    provider = getDefaultProvider(config)
+    data = getProviderData(provider, config)
+
+    if len(sys.argv) != 2:
+        print('Usage: generate-win.py <output_file>')
+        sys.exit(1)
+
+    outputf = sys.argv[1]
+
+    data['applicationNameLower'] = data.get('applicationName').lower()
+    data['URL'] = data.get('infoURL')
+    data['version'] = VERSION
+    writeOutput(data, outputf)
diff --git a/branding/scripts/provider.py b/branding/scripts/provider.py
new file mode 100644
index 0000000000000000000000000000000000000000..867007e2f28252b1b87b8fdb97aff85dcc807743
--- /dev/null
+++ b/branding/scripts/provider.py
@@ -0,0 +1,32 @@
+import datetime
+import os
+
+
+def getDefaultProvider(config):
+    provider = os.environ.get('PROVIDER')
+    if provider:
+        print('[+] Got provider {} from environemnt'.format(provider))
+    else:
+        print('[+] Using default provider from config file')
+        provider = config['default']['provider']
+    return provider
+
+
+def getProviderData(provider, config):
+    print("[+] Configured provider:", provider)
+
+    c = config[provider]
+    d = dict()
+
+    keys = ('name', 'applicationName', 'binaryName',
+            'providerURL', 'tosURL', 'helpURL',
+            'donateURL', 'apiURL', 'geolocationAPI', 'caCertString')
+
+    for value in keys:
+        d[value] = c.get(value)
+
+    d['timeStamp'] = '{:%Y-%m-%d %H:%M:%S}'.format(
+        datetime.datetime.now())
+
+    return d
+
diff --git a/branding/scripts/vendorize.py b/branding/scripts/vendorize.py
index dc1995252f4108c1228fef87f2fef72b3d192f5f..59edae08f0c6f4567ce6b30f710ad4a87a1f7f2c 100755
--- a/branding/scripts/vendorize.py
+++ b/branding/scripts/vendorize.py
@@ -1,47 +1,20 @@
 #!/usr/bin/env python3
 
-import datetime
 import os
 import sys
 
 from string import Template
 import configparser
 
+from provider import getDefaultProvider
+from provider import getProviderData
+
 OUTFILE = 'config.go'
-INFILE = 'config.go.tmpl'
-CONFIGFILE = 'config/vendor.conf'
+INFILE = '../templates/golang/config.go'
+CONFIGFILE = '../config/vendor.conf'
 SCRIPT_NAME = 'vendorize'
 
 
-def getDefaultProvider(config):
-    provider = os.environ.get('PROVIDER')
-    if provider:
-        print('[+] Got provider {} from environemnt'.format(provider))
-    else:
-        print('[+] Using default provider from config file')
-        provider = config['default']['provider']
-    return provider
-
-
-def getProviderData(provider, config):
-    print("[+] Configured provider:", provider)
-
-    c = config[provider]
-    d = dict()
-
-    keys = ('name', 'applicationName', 'binaryName',
-            'providerURL', 'tosURL', 'helpURL',
-            'donateURL', 'apiURL', 'geolocationAPI', 'caCertString')
-
-    for value in keys:
-        d[value] = c.get(value)
-
-    d['timeStamp'] = '{:%Y-%m-%d %H:%M:%S}'.format(
-        datetime.datetime.now())
-
-    return d
-
-
 def addCaData(data, configfile):
     provider = data.get('name').lower()
     folder, f = os.path.split(configfile)
@@ -91,7 +64,8 @@ if __name__ == "__main__":
     env_provider_conf = os.environ.get('PROVIDER_CONFIG')
     if env_provider_conf:
         if os.path.isfile(env_provider_conf):
-            print("[+] Overriding provider config per PROVIDER_CONFIG variable")
+            print("[+] Overriding provider config per "
+                  "PROVIDER_CONFIG variable")
             configfile = env_provider_conf
 
     if not os.path.isfile(infile):
diff --git a/branding/templates/windows/generate.py b/branding/templates/windows/generate.py
new file mode 100755
index 0000000000000000000000000000000000000000..427b7a8c4b5235de50ff765d8fad8eba0350e2cc
--- /dev/null
+++ b/branding/templates/windows/generate.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+"""
+generate.py
+
+Generate a NSI installer for a given provider.
+"""
+
+import json
+import os
+from string import Template
+
+
+TEMPLATE = 'template.nsi'
+
+
+def get_files(which):
+    files = "\n"
+    if which == 'install':
+        action = "File "
+    elif which == 'uninstall':
+        action = "Delete $INSTDIR\\"
+    else:
+        action = ""
+
+    # TODO get relative path
+    for item in open('payload/' + which).readlines():
+        files += "  {action}{item}".format(
+            action=action, item=item)
+    return files
+
+
+here = os.path.split(os.path.realpath(__file__))[0]
+data = json.load(open(os.path.join(here, 'data.json')))
+data['extra_install_files'] = get_files('install')
+data['extra_uninstall_files'] = get_files('uninstall')
+
+import pprint
+pprint.pprint(data)
+
+INSTALLER = data['applicationName'] + '-installer.nsi'
+
+
+template = Template(open(TEMPLATE).read())
+with open(INSTALLER, 'w') as output:
+    output.write(template.safe_substitute(data))
+
+print("[+] NSIS installer script written to {path}".format(path=INSTALLER))
diff --git a/branding/templates/windows/payload/install b/branding/templates/windows/payload/install
new file mode 100755
index 0000000000000000000000000000000000000000..da9a19506bd7f78094afd81da827ed1ffaf4f619
--- /dev/null
+++ b/branding/templates/windows/payload/install
@@ -0,0 +1,9 @@
+..\staging\bitmask_helper.exe
+..\staging\bitmask-vpn.exe
+..\staging\libcrypto-1_1-x64.dll
+..\staging\liblzo2-2.dll
+..\staging\libpkcs11-helper-1.dll
+..\staging\libssl-1_1-x64.dll
+..\staging\padlock.dll
+..\staging\openvpn\openvpn.exe
+..\staging\openvpn\tap-windows.exe
diff --git a/branding/templates/windows/payload/uninstall b/branding/templates/windows/payload/uninstall
new file mode 100755
index 0000000000000000000000000000000000000000..7c7df24c3c626344328b08a402843b68005a0ed9
--- /dev/null
+++ b/branding/templates/windows/payload/uninstall
@@ -0,0 +1,13 @@
+icon.ico
+openssl.exe
+openvpn.exe
+ssleay32.dll
+libeay32.dll
+liblzo2-2.dll
+libpkcs11-helper-1.dll
+libcrypto-1_1-x64.dll
+libssl-1_1-x64.dll
+padlock.dll
+bitmask_helper.exe
+bitmask-vpn.exe
+tap-windows.exe
diff --git a/branding/templates/windows/sign.py b/branding/templates/windows/sign.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b6b2c6e702f78b98af828cb872c97e0420f3604
--- /dev/null
+++ b/branding/templates/windows/sign.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+"""
+This script is expected to be called from the main makefile, that should pass
+the content of the WIN_CERT_PASS variable as the second argument.
+
+Just make sure that $GOPATH is properly configured.
+"""
+import subprocess
+import os
+import sys
+
+WIN_CERT_PATH = sys.argv[1]
+WIN_CERT_PASS = sys.argv[2]
+SIGNTOOL = "signtool"
+
+GOPATH = os.environ.get('GOPATH')
+VERSION = subprocess.run(
+    'git -C ' + GOPATH +
+    '\\src\\0xacab.org\\leap\\bitmask-vpn describe --tags',
+    stdout=subprocess.PIPE).stdout.strip()
+
+installer = "RiseupVPN-" + str(VERSION, 'utf-8') + '.exe'
+target = str(os.path.join(os.path.abspath('.'), 'dist', installer))
+cmd = [SIGNTOOL, "sign", "/f", WIN_CERT_PATH, "/p", WIN_CERT_PASS, target]
+subprocess.run(cmd)
diff --git a/branding/templates/windows/template.nsi b/branding/templates/windows/template.nsi
new file mode 100755
index 0000000000000000000000000000000000000000..f644b892b8174688966924884ce8ec63e4e4278a
--- /dev/null
+++ b/branding/templates/windows/template.nsi
@@ -0,0 +1,130 @@
+SetCompressor /SOLID lzma
+
+!define PRODUCT_PUBLISHER "LEAP Encryption Access Project"
+!include "MUI2.nsh"
+
+Name "$applicationName"
+Outfile "..\bin\$applicationName-$version.exe"
+;TODO make the installdir configurable - and set it in the registry.
+InstallDir "C:\Program Files\$applicationName\"
+RequestExecutionLevel admin
+
+!include FileFunc.nsh
+!insertmacro GetParameters
+!insertmacro GetOptions
+
+;Macros
+
+!macro SelectByParameter SECT PARAMETER DEFAULT
+	${GetOptions} $R0 "/${PARAMETER}=" $0
+	${If} ${DEFAULT} == 0
+		${If} $0 == 1
+			!insertmacro SelectSection ${SECT}
+		${EndIf}
+	${Else}
+		${If} $0 != 0
+			!insertmacro SelectSection ${SECT}
+		${EndIf}
+	${EndIf}
+!macroend
+
+
+
+!define BITMAP_FILE riseupvpn.bmp
+
+!define MUI_ICON "..\assets\$applicationNameLower.ico"
+!define MUI_UNICON "..\assets\$applicationNameLower.ico"
+
+!define MUI_WELCOMEPAGE_TITLE "$applicationName"
+!define MUI_WELCOMEPAGE_TEXT "This will install $applicationName in your computer. $applicationName is a simple, fast and secure VPN Client, powered by Bitmask. \n This VPN service is run by donations from people like you."
+#!define MUI_WELCOMEFINISHPAGE_BITMAP "riseup.png"
+
+!insertmacro MUI_PAGE_WELCOME
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_PAGE_FINISH
+ 
+ 
+
+Section "InstallFiles"
+  ; first we try to delete the systray, locked by the app.
+  ClearErrors
+  Delete 'C:\Program Files\$applicationName\bitmask-vpn.exe'
+  IfErrors 0 noError
+
+  ; Error handling
+  MessageBox MB_OK|MB_ICONEXCLAMATION "$applicationName is Running. Please close it, and then run this installer again."
+  Abort
+
+  noError:
+  ExecShellWait "runas" "$INSTDIR\nssm.exe" 'stop $applicationNameLower-helper'
+  ExecShellWait "runas" "$INSTDIR\nssm.exe" 'remove $applicationNameLower-helper confirm'
+
+  SetOutPath $INSTDIR 
+  WriteUninstaller $INSTDIR\uninstall.exe
+
+  ; Add ourselves to Add/remove programs
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayName" "$applicationName"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "UninstallString" '"$INSTDIR\uninstall.exe"'
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "InstallLocation" "$INSTDIR"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayIcon" "$INSTDIR\icon.ico"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "Readme" "$INSTDIR\readme.txt"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "DisplayVersion" "$version"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "Publisher" "LEAP Encryption Access Project"
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "NoModify" 1
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower" "NoRepair" 1
+
+  ;Start Menu
+  createDirectory "$SMPROGRAMS\$applicationName\"
+  createShortCut "$SMPROGRAMS\$applicationName\$applicationName.lnk" "$INSTDIR\bitmask-vpn.exe" "" "$INSTDIR\icon.ico"
+
+  File "readme.txt"
+  File "..\staging\nssm.exe"
+  File "/oname=icon.ico" "..\assets\$applicationNameLower.ico"
+
+  $extra_install_files
+
+SectionEnd
+
+Section "InstallService"
+  ; Easy service management thanks to nssm
+  ExecWait '"$INSTDIR\nssm.exe" install $applicationNameLower-helper "$INSTDIR\bitmask_helper.exe"'
+  ExecWait '"$INSTDIR\nssm.exe" set $applicationNameLower-helper AppDirectory "$INSTDIR"'
+  ExecWait '"$INSTDIR\nssm.exe" start $applicationNameLower-helper'
+SectionEnd
+
+Section /o "TAP Virtual Ethernet Adapter" SecTAP
+	; Adapted from the windows nsis installer from OpenVPN (openvpn-build repo).
+	DetailPrint "Installing TAP (may need confirmation)..."
+	; The /S flag make it "silent", remove it if you want it explicit
+  	ExecWait '"$INSTDIR\tap-windows.exe" /S /SELECT_UTILITIES=1'
+	Pop $R0 # return value/error/timeout
+	WriteRegStr HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$applicationName" "tap" "installed"
+	DetailPrint "TAP installed!"
+SectionEnd
+
+Section "Uninstall"
+  ExecShellWait "runas" "$INSTDIR\nssm.exe" 'stop $applicationNameLower-helper'
+  ExecShellWait "runas" "$INSTDIR\nssm.exe" 'remove $applicationNameLower-helper confirm'
+
+  Delete $INSTDIR\readme.txt
+  Delete $INSTDIR\nssm.exe
+  Delete $INSTDIR\helper.log
+  Delete "$SMPROGRAMS\$applicationName\$applicationName.lnk"
+  RMDir "$SMPROGRAMS\$applicationName\"
+
+  $extra_uninstall_files
+
+  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\$applicationNameLower"
+  ; uninstaller must be always the last thing to remove
+  Delete $INSTDIR\uninstall.exe
+  RMDir $INSTDIR
+SectionEnd
+
+Function .onInit
+	!insertmacro SelectByParameter ${SecTAP} SELECT_TAP 1
+FunctionEnd
+
+;----------------------------------------
+;Languages
+ 
+!insertmacro MUI_LANGUAGE "English"
diff --git a/branding/templates/windows/tools/README-cert.txt b/branding/templates/windows/tools/README-cert.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e532997bc1618c710fbcc89c886db986c7b8d733
--- /dev/null
+++ b/branding/templates/windows/tools/README-cert.txt
@@ -0,0 +1 @@
+openssl pkcs12 -inkey privatekey.pem -in signing_cert.pem -export -out LEAP.pfx
diff --git a/branding/templates/windows/tools/README-signtool.txt b/branding/templates/windows/tools/README-signtool.txt
new file mode 100644
index 0000000000000000000000000000000000000000..51ebbf56146f8f7038d7aed84eb8ab3814cdc2cc
--- /dev/null
+++ b/branding/templates/windows/tools/README-signtool.txt
@@ -0,0 +1,17 @@
+Source: https://stackoverflow.com/questions/31869552/how-to-install-signtool-exe-for-windows-10
+-----------------------------------------------------------------------------------------------
+
+If you only want SignTool and really want to minimize the install, here is a way that I just reverse-engineered my way to:
+
+Download the .iso file from https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk (current download link is http://go.microsoft.com/fwlink/p/?LinkID=2022797) The .exe download will not work, since it's an online installer that pulls down its dependencies at runtime.
+Unpack the .iso with a tool such as 7-zip.
+Install the Installers/Windows SDK Signing Tools-x86_en-us.msi file - it's only 388 KiB large. For reference, it pulls in its files from the following .cab files, so these are also needed for a standalone install:
+4c3ef4b2b1dc72149f979f4243d2accf.cab (339 KiB)
+    685f3d4691f444bc382762d603a99afc.cab (1002 KiB)
+    e5c4b31ff9997ac5603f4f28cd7df602.cab (389 KiB)
+    e98fa5eb5fee6ce17a7a69d585870b7c.cab (1.2 MiB)
+    There we go - you will now have the signtool.exe file and companions in C:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64 (replace x64 with x86, arm or arm64 if you need it for another CPU architecture.)
+
+It is also possible to commit  signtool.exe and the other files from this folder into your version control repository if want to use it in e.g. CI scenarios. I have tried and it seems to work fine.
+
+(All files are probably not necessary since there are also some other .exe tools in this folder that might be responsible for these dependencies, but I am not sure which ones could be removed to make the set of files even smaller. Someone else is free to investigate further in this area. :) I tried to just copy signtool.* and that didn't work, so at least some of the other files are needed.)
diff --git a/branding/templates/windows/tools/windows10-signing.zip b/branding/templates/windows/tools/windows10-signing.zip
new file mode 100644
index 0000000000000000000000000000000000000000..2d1858da91277b9cd3b7eac9a9c438c36f336e6e
Binary files /dev/null and b/branding/templates/windows/tools/windows10-signing.zip differ
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 682ce9c342a6cb30bef5095c37dc4db660462796..18f6667eb034233d713507689daaacd431902b03 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -1,6 +1,6 @@
 // Code generated by go generate; DO NOT EDIT.
 // This file was generated by vendorize.py
-// At 2019-07-09 18:27:17
+// At 2019-07-09 18:54:26
 
 package config