diff --git a/Makefile b/Makefile
index 4d3b01d6bb7fb6edc7df38575a23f763e12ec85b..5aea1a2c1a95c45934c9420a5f4768123dc85aae 100755
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,10 @@ SYSTRAY := 0xacab.org/leap/bitmask-systray.git
 STAGING := staging
 SYSTRAY_BIN := bitmask-systray
 HELPER_BIN := bitmask_helper
+APP_NAME := RiseupVPN
+BUILD_RELEASE?=no
+OSX_CERT = "Developer ID Installer: LEAP Encryption Access Project"
+VERSION = $(shell cat version)
 
 # -----------------------------------------------------------------------------
 # Windows 
@@ -50,10 +54,19 @@ systray_osx:
 	go get -tags "standalone" -u $(SYSTRAY)
 	go build -tags "standalone" -o $(STAGING)/$(SYSTRAY_BIN) $(SYSTRAY)
 	upx $(STAGING)/$(SYSTRAY_BIN)
-build_osx: helper_osx systray_osx
-	echo "[+] building osx..."
+bundle_osx:
 	mkdir -p dist
 	make -C osx
+pkg_osx:
+	osx/quickpkg --output dist/$(APP_NAME)-$(VERSION)_unsigned.pkg --scripts osx/scripts/ dist/$(APP_NAME).app/
+	@if [ $(BUILD_RELEASE) = no ]; then\
+		echo "[!] BUILD_RELEASE=no, skipping signature";\
+	else\
+		echo "[+] Signing the bundle";\
+		productsign --sign $(OSX_CERT) dist/$(APP_NAME)-$(VERSION)_unsigned.pkg dist/$(APP_NAME)-$(VERSION).pkg;\
+	fi
+build_osx: helper_osx systray_osx bundle_osx pkg_osx
+	echo "[+] building osx..."
 
 # -----------------------------------------------------------------------------
 # Linux 
@@ -69,5 +82,8 @@ build_snap:
 # Utils
 # -----------------------------------------------------------------------------
 
+clean:
+	rm -rf dist/
+
 staging\nssm.exe:
 	xcopy /y "C:\ProgramData\chocolatey\lib\NSSM\tools\nssm.exe" $(STAGING)
diff --git a/osx/generate.py b/osx/generate.py
index 57aa11ede13b9911930a43faffa37fd770eccacc..b8a6bece59c5b34c5fcecc17f0bcc770791fdf68 100644
--- a/osx/generate.py
+++ b/osx/generate.py
@@ -9,50 +9,104 @@ import stat
 from string import Template
 
 # Variables ----------------------------
-# TODO consolidate version string for all builds.
 
-VERSION = "0.0.1"
 APP_NAME = "RiseupVPN"
 BUNDLE_IDENTIFIER = "se.leap.riseupvpn"
+
 # Do not edit below --------------------
 
-TEMPLATE = 'template-info.plist'
+here = os.path.split(os.path.abspath(__file__))[0]
+
 ENTRYPOINT = 'bitmask-systray'
 HELPER = 'bitmask_helper'
+TEMPLATE_INFO = 'template-info.plist'
+TEMPLATE_HELPER = 'template-helper.plist'
+TEMPLATE_PREINSTALL = 'template-preinstall'
+TEMPLATE_POSTINSTALL = 'template-postinstall'
 
+VERSION = open(os.path.join(here, '..', 'version')).read().strip()
 
-here = os.path.split(os.path.abspath(__file__))[0]
 APP_PATH = os.path.abspath(here + '/../dist/' + APP_NAME + ".app")
 STAGING = os.path.abspath(here + '/../staging/')
+ASSETS = os.path.abspath(here + '/../assets/')
+ICON = os.path.join(ASSETS, APP_NAME.lower() + '.icns')
+SCRIPTS = os.path.join(os.path.abspath(here), 'scripts')
+INFO_PLIST = APP_PATH + '/Contents/Info.plist'
+HELPER_PLIST = os.path.join(SCRIPTS, 'se.leap.bitmask-helper.plist')
+PREINSTALL = os.path.join(SCRIPTS, 'preinstall')
+POSTINSTALL = os.path.join(SCRIPTS, 'postinstall')
 
 os.makedirs(APP_PATH + "/Contents/MacOS", exist_ok=True)
-
-INFO_PLIST = APP_PATH + '/Contents/Info.plist'
+os.makedirs(APP_PATH + "/Contents/Resources", exist_ok=True)
+os.makedirs(APP_PATH + "/Contents/helper", exist_ok=True)
 
 vardict = {
     'entrypoint': ENTRYPOINT,
     'info_string': APP_NAME + " " + VERSION,
     'bundle_identifier': BUNDLE_IDENTIFIER,
     'bundle_name': APP_NAME,
-    'version': VERSION
+    'version': VERSION,
+    'app_name': APP_NAME
 }
 
-template = Template(open(TEMPLATE).read())
-with open(INFO_PLIST, 'w') as output:
-    output.write(template.safe_substitute(vardict))
+# utils
+
+def copy_payload(filename):
+    destfile = APP_PATH + "/Contents/MacOS/" + filename
+    shutil.copyfile(STAGING + '/' + filename, destfile)
+    cmode = os.stat(destfile).st_mode
+    os.chmod(destfile, cmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
+def generate_from_template(template, dest, vardict):
+    print("[+] File written from template to", dest)
+    template = Template(open(template).read())
+    with open(dest, 'w') as output:
+        output.write(template.safe_substitute(vardict))
+
 
+# 1. Generation of the Bundle Info.plist
+# --------------------------------------
 
+generate_from_template(TEMPLATE_INFO, INFO_PLIST, vardict)
+
+
+# 2. Generate PkgInfo
+# -------------------------------------------
+# XXX is this enough?
 f = open(APP_PATH + "/Contents/PkgInfo", "w")
 f.write("APPL????")
 f.close()
 
 
-def copy_payload(filename):
-    destfile = APP_PATH + "/Contents/MacOS/" + filename
-    shutil.copyfile(STAGING + '/' + filename, destfile)
-    cmode = os.stat(destfile).st_mode
-    os.chmod(destfile, cmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+# 3. Copy the binary payloads
+# --------------------------------------------
 
 copy_payload(ENTRYPOINT)
 copy_payload(HELPER)
 
+# 4. Copy the app icon
+# -----------------------------------------------
+shutil.copyfile(ICON, APP_PATH + '/Contents/Resources/app.icns')
+
+
+# 5. Generate the scripts for the installer
+# -----------------------------------------------
+generate_from_template(TEMPLATE_HELPER, HELPER_PLIST, vardict)
+generate_from_template(TEMPLATE_PREINSTALL, PREINSTALL, vardict)
+generate_from_template(TEMPLATE_POSTINSTALL, POSTINSTALL, vardict)
+
+
+# 6. Generate uninstall script
+# -----------------------------------------------
+# TODO copy the uninstaller script from bitmask-dev
+# TODO substitute vars
+# TODO this is a bit weak for now.
+# To begin with, this assumes everything is hardcoded into /Applications/APPNAME.app
+# We could consider moving the helpers into /usr/local/sbin, so that the plist files
+# always reference there.
+ 
+
+# We're all set here.
+print("[+] Output written to dist/{appname}.app".format(appname=APP_NAME))
+
+
diff --git a/osx/scripts/postinstall b/osx/scripts/postinstall
index 93430390f3e775b8374251d7015e4bc56cfff6d6..88ce4af5cb4225e46e6d67a57531f0f65c0eb746 100755
--- a/osx/scripts/postinstall
+++ b/osx/scripts/postinstall
@@ -5,9 +5,9 @@
 
 LOG=/tmp/bitmask-install.log
 
-chmod +x /Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/bitmask-helper
+chmod +x /Applications/RiseupVPN.app/Contents/MacOS/bitmask-helper
 cp se.leap.bitmask-helper.plist /Library/LaunchDaemons/ \
-	&& echo `date` ":: Bitmask post-install: copied bitmask-helper Plist." >> $LOG
-launchctl load /Library/LaunchDaemons/se.leap.bitmask-helper.plist && echo `date` ":: Bitmask post-install: loaded bitmask-helper." >> $LOG
-echo `date` ":: Bitmask post-install: ok." >> $LOG
+	&& echo `date` ":: RiseupVPN post-install: copied bitmask-helper Plist." >> $LOG
+launchctl load /Library/LaunchDaemons/se.leap.bitmask-helper.plist && echo `date` ":: RiseupVPN post-install: loaded bitmask-helper." >> $LOG
+echo `date` ":: RiseupVPN post-install: ok." >> $LOG
 exit 0
diff --git a/osx/scripts/preinstall b/osx/scripts/preinstall
index 1ae68f9c3ebbf48fc06fce048648a8428f45d33b..47a08123c4b1de0189c991c182c6e7f67093c459 100755
--- a/osx/scripts/preinstall
+++ b/osx/scripts/preinstall
@@ -7,6 +7,6 @@ LOG=/tmp/bitmask-install.log
 
 ps aux | grep [b]itmask-helper \
 	&& launchctl unload /Library/LaunchDaemons/se.leap.bitmask-helper.plist \
-	&& echo `date` ":: Bitmask pre-install: unloaded bitmask-helper." >> $LOG
-echo `date` ":: Bitmask pre-install: ok." >> $LOG
+	&& echo `date` ":: RiseupVPN pre-install: unloaded bitmask-helper." >> $LOG
+echo `date` ":: RiseupVPN pre-install: ok." >> $LOG
 exit 0
diff --git a/osx/scripts/se.leap.bitmask-helper.plist b/osx/scripts/se.leap.bitmask-helper.plist
index 8ebfb947a663b204d7c9b3763bdf9cf1f74031b9..9af802b071046eca2193b706a70112bccbe3c16b 100644
--- a/osx/scripts/se.leap.bitmask-helper.plist
+++ b/osx/scripts/se.leap.bitmask-helper.plist
@@ -3,7 +3,7 @@
 <plist version="1.0">
 <dict>
 	<key>WorkingDirectory</key>
-	<string>/Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/</string>
+	<string>/Applications/RiseupVPN.app/Contents/helper/</string>
 	<key>StandardOutPath</key>
 	<string>bitmask-helper.log</string>
 	<key>StandardErrorPath</key>
@@ -21,6 +21,6 @@
     <key>Label</key>
 	<string>se.leap.BitmaskHelper</string>
 	<key>Program</key>
-    <string>/Applications/RiseupVPN.app/Contents/Resources/bitmask-helper/bitmask-helper</string>
+    <string>/Applications/RiseupVPN.app/Contents/MacOS/bitmask-helper</string>
 </dict>
 </plist>
diff --git a/osx/template-helper.plist b/osx/template-helper.plist
new file mode 100644
index 0000000000000000000000000000000000000000..e0f23a110641112de575e639c797ca471f83080a
--- /dev/null
+++ b/osx/template-helper.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>WorkingDirectory</key>
+	<string>/Applications/$app_name.app/Contents/helper/</string>
+	<key>StandardOutPath</key>
+	<string>bitmask-helper.log</string>
+	<key>StandardErrorPath</key>
+	<string>bitmask-helper-err.log</string>
+	<key>GroupName</key>
+	<string>daemon</string>
+	<key>RunAtLoad</key>
+	<true/>
+	<key>SessionCreate</key>
+	<true/>
+	<key>KeepAlive</key>
+    <true/>
+    <key>ThrottleInterval</key>
+    <integer>5</integer>
+    <key>Label</key>
+	<string>se.leap.BitmaskHelper</string>
+	<key>Program</key>
+    <string>/Applications/$app_name.app/Contents/MacOS/bitmask-helper</string>
+</dict>
+</plist>
diff --git a/osx/template-postinstall b/osx/template-postinstall
new file mode 100755
index 0000000000000000000000000000000000000000..aad3cae8faa86599ce064edc86431668fb5705f3
--- /dev/null
+++ b/osx/template-postinstall
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Bitmask Post-Instalation script
+# (c) LEAP Encryption access Project
+# We copy the bitmask-helper plist to the LaunchDaemons folder, and load the bitmask-helper that runs as root.
+
+LOG=/tmp/bitmask-install.log
+
+chmod +x /Applications/$app_name.app/Contents/MacOS/bitmask-helper
+cp se.leap.bitmask-helper.plist /Library/LaunchDaemons/ \
+	&& echo `date` ":: $app_name post-install: copied bitmask-helper Plist." >> $LOG
+launchctl load /Library/LaunchDaemons/se.leap.bitmask-helper.plist && echo `date` ":: $app_name post-install: loaded bitmask-helper." >> $LOG
+echo `date` ":: $app_name post-install: ok." >> $LOG
+exit 0
diff --git a/osx/template-preinstall b/osx/template-preinstall
new file mode 100755
index 0000000000000000000000000000000000000000..fa2f2b024bab24bc19e8d80672ac3e49b5b8a9b2
--- /dev/null
+++ b/osx/template-preinstall
@@ -0,0 +1,12 @@
+#!/bin/sh
+# Bitmask Pre-Instalation script
+# (c) LEAP Encryption access Project
+# We unload the bitmask-helper if it is running, because we can be installing an upgrade.
+
+LOG=/tmp/bitmask-install.log
+
+ps aux | grep [b]itmask-helper \
+	&& launchctl unload /Library/LaunchDaemons/se.leap.bitmask-helper.plist \
+	&& echo `date` ":: $app_name pre-install: unloaded bitmask-helper." >> $LOG
+echo `date` ":: $app_name pre-install: ok." >> $LOG
+exit 0
diff --git a/version b/version
new file mode 100644
index 0000000000000000000000000000000000000000..51176c7c891c45403eab9e0cf753cbab19dc2442
--- /dev/null
+++ b/version
@@ -0,0 +1 @@
+0.11