diff --git a/packaging/osx/lbry-osx-app/.gitignore b/packaging/osx/lbry-osx-app/.gitignore
new file mode 100644
index 000000000..bc318a057
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/.gitignore
@@ -0,0 +1,12 @@
+
+*.pyc
+
+*.pyo
+
+*.so
+
+*.xml
+
+*.iml
+
+id.conf
diff --git a/packaging/osx/lbry-osx-app/app.icns b/packaging/osx/lbry-osx-app/app.icns
new file mode 100644
index 000000000..b4d00d2f2
Binary files /dev/null and b/packaging/osx/lbry-osx-app/app.icns differ
diff --git a/packaging/osx/lbry-osx-app/build_app.sh b/packaging/osx/lbry-osx-app/build_app.sh
new file mode 100755
index 000000000..59828f6b4
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/build_app.sh
@@ -0,0 +1,63 @@
+dest=`pwd`
+tmp="${dest}/build"
+id=`cat id.conf`
+
+rm -rf build dist LBRY.app
+
+mkdir -p $tmp
+cd $tmp
+
+echo "Updating lbryum"
+git clone --depth 1 http://github.com/lbryio/lbryum.git
+cd lbryum
+python setup.py install &>/dev/null
+cd ..
+echo "Updating lbrynet"
+git clone --depth 1 -b development http://github.com/lbryio/lbry.git
+cd lbry
+python setup.py install &>/dev/null
+
+cd $dest
+echo "Building URI Handler"
+python setup_uri_handler.py py2app &>/dev/null
+
+echo "Signing URI Handler"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "dist/LBRYURIHandler.app/Contents/Frameworks/Python.framework/Versions/2.7"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "dist/LBRYURIHandler.app/Contents/MacOS/python"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "dist/LBRYURIHandler.app/Contents/MacOS/LBRYURIHandler"
+codesign -vvvv "dist/LBRYURIHandler.app"
+mv "dist/LBRYURIHandler.app" "LBRYURIHandler.app"
+rm -rf build dist
+
+echo "Building app"
+python setup_app.py py2app &>/dev/null
+
+echo "Moving in correct libgmp"
+rm "${dest}/dist/LBRY.app/Contents/Frameworks/libgmp.10.dylib"
+cp "${dest}/libgmp.10.dylib" "${dest}/dist/LBRY.app/Contents/Frameworks"
+
+echo "Removing i386 libraries"
+
+remove_arch () {
+    lipo -output build/lipo.tmp -remove "$1" "$2" && mv build/lipo.tmp "$2"
+}
+for i in dist/LBRY.app/Contents/Resources/lib/python2.7/lib-dynload/* ; do
+    #remove_arch ppc ${i}
+    remove_arch i386 ${i}
+done
+
+echo "Moving LBRYURIHandler.app into LBRY.app"
+mv "${dest}/LBRYURIHandler.app" "${dest}/dist/LBRY.app/Contents/Resources"
+
+echo "Signing LBRY.app"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "${dest}/dist/LBRY.app/Contents/Frameworks/Python.framework/Versions/2.7"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "${dest}/dist/LBRY.app/Contents/Frameworks/libgmp.10.dylib"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "${dest}/dist/LBRY.app/Contents/MacOS/python"
+codesign -s "Developer ID Application: LBRY Inc (${id})" -f "${dest}/dist/LBRY.app/Contents/MacOS/LBRY"
+codesign -vvvv "${dest}/dist/LBRY.app"
+
+rm -rf $tmp
+mv dist/LBRY.app LBRY.app
+rm -rf dist
+
+chown -R ${SUDO_USER} LBRY.app
\ No newline at end of file
diff --git a/packaging/osx/lbry-osx-app/lbry_uri_handler/LBRYURIHandler.py b/packaging/osx/lbry-osx-app/lbry_uri_handler/LBRYURIHandler.py
new file mode 100644
index 000000000..f6990cfea
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/lbry_uri_handler/LBRYURIHandler.py
@@ -0,0 +1,60 @@
+import os
+import json
+import webbrowser
+import subprocess
+import sys
+
+from time import sleep
+from jsonrpc.proxy import JSONRPCProxy
+
+API_CONNECTION_STRING = "http://localhost:5279/lbryapi"
+UI_ADDRESS = "http://localhost:5279"
+
+
+class LBRYURIHandler(object):
+    def __init__(self):
+        self.started_daemon = False
+        self.daemon = JSONRPCProxy.from_url(API_CONNECTION_STRING)
+
+    def handle_osx(self, lbry_name):
+        try:
+            status = self.daemon.is_running()
+        except:
+            os.system("open /Applications/LBRY.app")
+            sleep(3)
+
+        if lbry_name == "lbry" or lbry_name == "":
+            webbrowser.open(UI_ADDRESS)
+        else:
+            webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name)
+
+    def handle_linux(self, lbry_name):
+        try:
+            status = self.daemon.is_running()
+        except:
+            cmd = r'DIR = "$( cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd )"' \
+                  r'if [-z "$(pgrep lbrynet-daemon)"]; then' \
+                    r'echo "running lbrynet-daemon..."' \
+                    r'$DIR / lbrynet - daemon &' \
+                    r'sleep 3  # let the daemon load before connecting' \
+                  r'fi'
+            subprocess.Popen(cmd, shell=True)
+
+        if lbry_name == "lbry" or lbry_name == "":
+            webbrowser.open(UI_ADDRESS)
+        else:
+            webbrowser.open(UI_ADDRESS + "/?watch=" + lbry_name)
+
+
+def main(args):
+    if len(args) != 1:
+        args = ['lbry://lbry']
+
+    name = args[0][7:]
+    if sys.platform == "darwin":
+        LBRYURIHandler().handle_osx(lbry_name=name)
+    else:
+        LBRYURIHandler().handle_linux(lbry_name=name)
+
+if __name__ == "__main__":
+   main(sys.argv[1:])
diff --git a/packaging/osx/lbry-osx-app/lbrygui/LBRYApp.py b/packaging/osx/lbry-osx-app/lbrygui/LBRYApp.py
new file mode 100644
index 000000000..56f02846f
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/lbrygui/LBRYApp.py
@@ -0,0 +1,77 @@
+import AppKit
+import webbrowser
+import sys
+import logging
+import socket
+import platform
+
+from PyObjCTools import AppHelper
+
+from twisted.internet import reactor
+from twisted.web import server
+
+from lbrynet.lbrynet_daemon.LBRYDaemonServer import LBRYDaemonServer
+from lbrynet.conf import API_PORT, API_INTERFACE, ICON_PATH, APP_NAME
+from lbrynet.conf import UI_ADDRESS
+
+if platform.mac_ver()[0] >= "10.10":
+    from LBRYNotify import LBRYNotify
+
+log = logging.getLogger(__name__)
+
+REMOTE_SERVER = "www.google.com"
+
+
+def test_internet_connection():
+    try:
+        host = socket.gethostbyname(REMOTE_SERVER)
+        s = socket.create_connection((host, 80), 2)
+        return True
+    except:
+        return False
+
+
+class LBRYDaemonApp(AppKit.NSApplication):
+    def finishLaunching(self):
+        self.connection = False
+        statusbar = AppKit.NSStatusBar.systemStatusBar()
+        self.statusitem = statusbar.statusItemWithLength_(AppKit.NSVariableStatusItemLength)
+        self.icon = AppKit.NSImage.alloc().initByReferencingFile_(ICON_PATH)
+        self.icon.setScalesWhenResized_(True)
+        self.icon.setSize_((20, 20))
+        self.statusitem.setImage_(self.icon)
+        self.menubarMenu = AppKit.NSMenu.alloc().init()
+        self.open = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Open", "openui:", "")
+        self.menubarMenu.addItem_(self.open)
+        self.quit = AppKit.NSMenuItem.alloc().initWithTitle_action_keyEquivalent_("Quit", "replyToApplicationShouldTerminate:", "")
+        self.menubarMenu.addItem_(self.quit)
+        self.statusitem.setMenu_(self.menubarMenu)
+        self.statusitem.setToolTip_(APP_NAME)
+
+
+        if test_internet_connection():
+            if platform.mac_ver()[0] >= "10.10":
+                LBRYNotify("Starting LBRY")
+        else:
+            if platform.mac_ver()[0] >= "10.10":
+                LBRYNotify("LBRY needs an internet connection to start, try again when one is available")
+            sys.exit(0)
+
+        # if not subprocess.check_output("git ls-remote https://github.com/lbryio/lbry-web-ui.git | grep HEAD | cut -f 1",
+        #                                shell=True):
+        #     LBRYNotify(
+        #         "You should have been prompted to install xcode command line tools, please do so and then start LBRY")
+        #     sys.exit(0)
+
+        lbry = LBRYDaemonServer()
+        d = lbry.start()
+        d.addCallback(lambda _: webbrowser.open(UI_ADDRESS))
+        reactor.listenTCP(API_PORT, server.Site(lbry.root), interface=API_INTERFACE)
+
+    def openui_(self, sender):
+        webbrowser.open(UI_ADDRESS)
+
+    def replyToApplicationShouldTerminate_(self, shouldTerminate):
+        if platform.mac_ver()[0] >= "10.10":
+            LBRYNotify("Goodbye!")
+        reactor.stop()
diff --git a/packaging/osx/lbry-osx-app/lbrygui/LBRYNotify.py b/packaging/osx/lbry-osx-app/lbrygui/LBRYNotify.py
new file mode 100644
index 000000000..d4a88e7ce
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/lbrygui/LBRYNotify.py
@@ -0,0 +1,27 @@
+import Foundation
+import objc
+import AppKit
+
+NSUserNotification = objc.lookUpClass('NSUserNotification')
+NSUserNotificationCenter = objc.lookUpClass('NSUserNotificationCenter')
+
+def LBRYNotify(message):
+    notification = NSUserNotification.alloc().init()
+    notification.setTitle_("LBRY")
+    notification.setSubtitle_("")
+    notification.setInformativeText_(message)
+    notification.setUserInfo_({})
+    notification.setSoundName_("NSUserNotificationDefaultSoundName")
+    notification.setDeliveryDate_(Foundation.NSDate.dateWithTimeInterval_sinceDate_(0, Foundation.NSDate.date()))
+    NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(notification)
+
+def notify(title, subtitle, info_text, delay=0, sound=False, userInfo={}):
+    notification = NSUserNotification.alloc().init()
+    notification.setTitle_(title)
+    notification.setSubtitle_(subtitle)
+    notification.setInformativeText_(info_text)
+    notification.setUserInfo_(userInfo)
+    if sound:
+        notification.setSoundName_("NSUserNotificationDefaultSoundName")
+    notification.setDeliveryDate_(Foundation.NSDate.dateWithTimeInterval_sinceDate_(delay, Foundation.NSDate.date()))
+    NSUserNotificationCenter.defaultUserNotificationCenter().scheduleNotification_(notification)
\ No newline at end of file
diff --git a/packaging/osx/lbry-osx-app/lbrygui/__init__.py b/packaging/osx/lbry-osx-app/lbrygui/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/packaging/osx/lbry-osx-app/lbrygui/app.icns b/packaging/osx/lbry-osx-app/lbrygui/app.icns
new file mode 100644
index 000000000..b4d00d2f2
Binary files /dev/null and b/packaging/osx/lbry-osx-app/lbrygui/app.icns differ
diff --git a/packaging/osx/lbry-osx-app/lbrygui/main.py b/packaging/osx/lbry-osx-app/lbrygui/main.py
new file mode 100644
index 000000000..d7ca4dc00
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/lbrygui/main.py
@@ -0,0 +1,35 @@
+from PyObjCTools import AppHelper
+from twisted.internet.cfreactor import install
+install(runner=AppHelper.runEventLoop)
+from twisted.internet import reactor
+
+import logging
+import sys
+import os
+from appdirs import user_data_dir
+
+from LBRYApp import LBRYDaemonApp
+
+if sys.platform != "darwin":
+    log_dir = os.path.join(os.path.expanduser("~"), ".lbrynet")
+else:
+    log_dir = user_data_dir("LBRY")
+
+if not os.path.isdir(log_dir):
+    os.mkdir(log_dir)
+
+LOG_FILENAME = os.path.join(log_dir, 'lbrynet-daemon.log')
+
+log = logging.getLogger(__name__)
+handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=2097152, backupCount=5)
+log.addHandler(handler)
+logging.basicConfig(level=logging.INFO)
+
+
+def main():
+    app = LBRYDaemonApp.sharedApplication()
+    reactor.addSystemEventTrigger("after", "shutdown", AppHelper.stopEventLoop)
+    reactor.run()
+
+if __name__ == "__main__":
+    main()
diff --git a/packaging/osx/lbry-osx-app/libgmp.10.dylib b/packaging/osx/lbry-osx-app/libgmp.10.dylib
new file mode 100755
index 000000000..09f809987
Binary files /dev/null and b/packaging/osx/lbry-osx-app/libgmp.10.dylib differ
diff --git a/packaging/osx/lbry-osx-app/setup_app.py b/packaging/osx/lbry-osx-app/setup_app.py
new file mode 100644
index 000000000..f6d2a9145
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/setup_app.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+
+import os
+from setuptools import setup
+from lbrynet.conf import PROTOCOL_PREFIX, APP_NAME, ICON_PATH
+import sys
+
+APP = [os.path.join('lbrygui', 'main.py')]
+DATA_FILES = []
+DATA_FILES.append('app.icns')
+
+OPTIONS = {
+    # 'argv_emulation': True,
+    'iconfile': ICON_PATH,
+    'plist': {
+        'CFBundleIdentifier': 'io.lbry.LBRY',
+        'LSUIElement': True,
+    },
+    'packages': ['lbrynet', 'lbryum', 'requests', 'unqlite', 'certifi',
+                 'pkg_resources', 'json', 'jsonrpc', 'seccure',],
+}
+
+
+setup(
+    name=APP_NAME,
+    app=APP,
+    options={'py2app': OPTIONS},
+    data_files=DATA_FILES,
+)
\ No newline at end of file
diff --git a/packaging/osx/lbry-osx-app/setup_app.sh b/packaging/osx/lbry-osx-app/setup_app.sh
new file mode 100755
index 000000000..d4141326a
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/setup_app.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+set -o errexit
+set -o xtrace
+
+dest=`pwd`
+tmp="${dest}/build"
+
+rm -rf build dist LBRY.app
+
+mkdir -p $tmp
+cd $tmp
+
+echo "Updating lbrynet"
+if [ -z ${TRAVIS_BUILD_DIR+x} ]; then
+    # building locally
+    git clone --depth 1 http://github.com/lbryio/lbry.git
+    cd lbry
+    LBRY="${tmp}/lbry"
+else
+    # building on travis
+    cd ${TRAVIS_BUILD_DIR}
+    LBRY=${TRAVIS_BUILD_DIR}
+fi
+python setup.py install
+echo "Building URI Handler"
+rm -rf build dist
+python setup_uri_handler.py py2app
+
+echo "Signing URI Handler"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${LBRY}/dist/LBRYURIHandler.app/Contents/Frameworks/Python.framework/Versions/2.7"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${LBRY}/dist/LBRYURIHandler.app/Contents/MacOS/python"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${LBRY}/dist/LBRYURIHandler.app/Contents/MacOS/LBRYURIHandler"
+codesign -vvvv "${LBRY}/dist/LBRYURIHandler.app"
+
+cd $dest
+python setup.py py2app &>/dev/null
+
+echo "Moving in correct libgmp"
+rm "${dest}/dist/LBRY.app/Contents/Frameworks/libgmp.10.dylib"
+cp "${dest}/libgmp.10.dylib" "${dest}/dist/LBRY.app/Contents/Frameworks"
+
+echo "Removing i386 libraries"
+
+remove_arch () {
+    lipo -output build/lipo.tmp -remove "$1" "$2" && mv build/lipo.tmp "$2"
+}
+for i in dist/LBRY.app/Contents/Resources/lib/python2.7/lib-dynload/* ; do
+    remove_arch i386 ${i}
+done
+
+echo "Moving LBRYURIHandler.app into LBRY.app"
+mv "${LBRY}/dist/LBRYURIHandler.app" "${dest}/dist/LBRY.app/Contents/Resources"
+
+echo "Signing LBRY.app"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${dest}/dist/LBRY.app/Contents/Frameworks/Python.framework/Versions/2.7"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${dest}/dist/LBRY.app/Contents/Frameworks/libgmp.10.dylib"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${dest}/dist/LBRY.app/Contents/MacOS/python"
+codesign -s "${LBRY_DEVELOPER_ID}" -f "${dest}/dist/LBRY.app/Contents/MacOS/LBRY"
+codesign -vvvv "${dest}/dist/LBRY.app"
+
+rm -rf $tmp
+mv dist/LBRY.app LBRY.app
+rm -rf dist
diff --git a/packaging/osx/lbry-osx-app/setup_uri_handler.py b/packaging/osx/lbry-osx-app/setup_uri_handler.py
new file mode 100644
index 000000000..26097d8a4
--- /dev/null
+++ b/packaging/osx/lbry-osx-app/setup_uri_handler.py
@@ -0,0 +1,25 @@
+from setuptools import setup
+import os
+
+APP = [os.path.join('lbry_uri_handler', 'LBRYURIHandler.py')]
+DATA_FILES = []
+OPTIONS = {'argv_emulation': True,
+           'packages': ['jsonrpc'],
+           'plist': {
+               'LSUIElement': True,
+               'CFBundleIdentifier': 'io.lbry.LBRYURIHandler',
+               'CFBundleURLTypes': [
+                    {
+                    'CFBundleURLTypes': 'LBRYURIHandler',
+                    'CFBundleURLSchemes': ['lbry']
+                    }
+               ]
+           }
+        }
+
+setup(
+    app=APP,
+    data_files=DATA_FILES,
+    options={'py2app': OPTIONS},
+    setup_requires=['py2app'],
+)
\ No newline at end of file