diff --git a/launchers/macosx/AppDelegate.h b/launchers/macosx/AppDelegate.h
index c1ea98d460e3d1c310252727fc60450b752e60f5..171c6b9d31f82c246aa12b09711bfa7f6b6cc628 100644
--- a/launchers/macosx/AppDelegate.h
+++ b/launchers/macosx/AppDelegate.h
@@ -21,6 +21,7 @@
 #include "version.h"
 
 @class SwiftMainDelegate;
+@class I2PDeployer;
 
 @protocol SwiftMainDelegateProto
 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification;
@@ -92,6 +93,7 @@ inline void sendUserNotification(NSString* title, NSString* informativeText, boo
 @property (assign) SwiftMainDelegate *swiftRuntime;
 @property (assign) NSUserDefaults *userPreferences;
 @property (assign) ExtractMetaInfo *metaInfo;
+@property (assign) I2PDeployer *deployer;
 @property (copy) NSImage *contentImage NS_AVAILABLE(10_9, NA);
 
 - (void) extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion;
diff --git a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib
index bc21f8b438470a00e994826f7bded656bfc630c6..22333743948e4ab59a92db1dda9ac771da6703dd 100644
--- a/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib
+++ b/launchers/macosx/I2PLauncher/Base.lproj/UserInterfaces.xib
@@ -37,6 +37,5 @@
             </items>
             <point key="canvasLocation" x="17" y="167"/>
         </menu>
-        <customObject id="xTS-ll-kWI" customClass="SUUpdater"/>
     </objects>
 </document>
diff --git a/launchers/macosx/I2PLauncher/Storyboard.storyboard b/launchers/macosx/I2PLauncher/Storyboard.storyboard
index 3363ad9d2983e6314b14a032d82b52bc31124c9f..cd53b65b4ac506b618c7ed5a7546945f2d14fd2a 100644
--- a/launchers/macosx/I2PLauncher/Storyboard.storyboard
+++ b/launchers/macosx/I2PLauncher/Storyboard.storyboard
@@ -90,16 +90,8 @@
                                                             <font key="font" metaFont="system"/>
                                                         </buttonCell>
                                                     </button>
-                                                    <button identifier="restartRouterButton" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="C1m-1t-Xiq" userLabel="restart-button">
-                                                        <rect key="frame" x="10" y="150" width="128" height="32"/>
-                                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
-                                                        <buttonCell key="cell" type="push" title="Restart Router" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="sih-uF-V06">
-                                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
-                                                            <font key="font" metaFont="system"/>
-                                                        </buttonCell>
-                                                    </button>
                                                     <button identifier="openConsoleButton" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XZi-oz-5gy" userLabel="open-console-button">
-                                                        <rect key="frame" x="11" y="123" width="126" height="32"/>
+                                                        <rect key="frame" x="10" y="150" width="126" height="32"/>
                                                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                                                         <buttonCell key="cell" type="push" title="Open Console" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Yh8-lj-JFi">
                                                             <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -133,7 +125,7 @@
                                             <autoresizingMask key="autoresizingMask"/>
                                             <clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="E5l-WA-qOn">
                                                 <rect key="frame" x="1" y="1" width="407" height="226"/>
-                                                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                                                <autoresizingMask key="autoresizingMask"/>
                                                 <subviews>
                                                     <textView identifier="LoggerTextView" ambiguous="YES" importsGraphics="NO" verticallyResizable="YES" usesFontPanel="YES" findStyle="panel" continuousSpellChecking="YES" allowsUndo="YES" usesRuler="YES" allowsNonContiguousLayout="YES" quoteSubstitution="YES" dashSubstitution="YES" spellingCorrection="YES" smartInsertDelete="YES" id="bgQ-8i-Xgb" userLabel="LoggerTextView">
                                                         <rect key="frame" x="0.0" y="0.0" width="407" height="226"/>
diff --git a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift
index ed5ef7857c36c09d45dec6c33fd2333275ce28f2..719e0ed0120a70ddb71ab118e49a2eedd002fae5 100644
--- a/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift
+++ b/launchers/macosx/I2PLauncher/SwiftMainDelegate.swift
@@ -93,7 +93,8 @@ class Logger {
   }
   
   @objc static func openLink(url: String) {
-    SBridge.sharedInstance().openUrl(url)
+    NSLog("Trying to open \(url)")
+    NSWorkspace.shared().open(NSURL(string: url)! as URL)
   }
   
   @objc func applicationWillTerminate() {
diff --git a/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift b/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift
new file mode 100644
index 0000000000000000000000000000000000000000..168989344a900e2635b76e780c537c3afaea5533
--- /dev/null
+++ b/launchers/macosx/I2PLauncher/Utils/FolderContentMonitor.swift
@@ -0,0 +1,111 @@
+//
+//  FolderContentMonitor.swift
+//  I2PLauncher
+//
+//  Created by Mikal Villa on 28/09/2018.
+//  Copyright © 2018 The I2P Project. All rights reserved.
+//
+
+import Foundation
+
+/*
+ infix operator ~>
+ 
+ private let queue = DispatchQueue(label: "background")
+ 
+ func ~> <R> (
+ backgroundClosure:   @escaping () -> R,
+ mainClosure:         @escaping (_ result: R) -> ())
+ {
+ queue.async {
+ let result = backgroundClosure()
+ DispatchQueue.main.async(execute: {
+ mainClosure(result)
+ })
+ }
+ }
+ */
+
+public struct Event: CustomStringConvertible {
+  
+  public let eventId: FSEventStreamEventId
+  public let eventPath: String
+  public let eventFlags: FSEventStreamEventFlags
+  
+  public var description: String {
+    return "\(eventId) - \(eventFlags) - \(eventPath)"
+  }
+}
+
+public class FolderContentMonitor {
+  
+  let callback: (Event) -> Void
+  
+  public init(pathsToWatch: [String], sinceWhen: FSEventStreamEventId = FSEventStreamEventId(kFSEventStreamEventIdSinceNow), callback: @escaping (Event) -> Void) {
+    
+    self.lastEventId = sinceWhen
+    self.pathsToWatch = pathsToWatch
+    self.callback = callback
+  }
+  
+  deinit {
+    stop()
+  }
+  
+  // MARK: - Private Properties
+  private let eventCallback: FSEventStreamCallback = {
+    (stream: ConstFSEventStreamRef,
+    contextInfo: UnsafeMutableRawPointer?,
+    numEvents: Int,
+    eventPaths: UnsafeMutableRawPointer,
+    eventFlags: UnsafePointer<FSEventStreamEventFlags>,
+    eventIds: UnsafePointer<FSEventStreamEventId>) in
+    
+    let fileSystemWatcher: FolderContentMonitor = unsafeBitCast(contextInfo, to: FolderContentMonitor.self)
+    
+    guard let paths = unsafeBitCast(eventPaths, to: NSArray.self) as? [String] else { return }
+    
+    for index in 0..<numEvents {
+      fileSystemWatcher.processEvent(eventId: eventIds[index], eventPath: paths[index], eventFlags: eventFlags[index])
+    }
+    
+    fileSystemWatcher.lastEventId = eventIds[numEvents - 1]
+  }
+  
+  private let pathsToWatch: [String]
+  private var started = false
+  private var streamRef: FSEventStreamRef!
+  
+  private func processEvent(eventId: FSEventStreamEventId, eventPath: String, eventFlags: FSEventStreamEventFlags) {
+    
+    let event = Event(eventId: eventId, eventPath: eventPath, eventFlags: eventFlags)
+    callback(event)
+  }
+  
+  public private(set) var lastEventId: FSEventStreamEventId
+  
+  public func start() {
+    guard started == false else { return }
+    
+    var context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
+    context.info = Unmanaged.passUnretained(self).toOpaque()
+    let flags = UInt32(kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents)
+    streamRef = FSEventStreamCreate(kCFAllocatorDefault, eventCallback, &context, pathsToWatch as CFArray, lastEventId, 0, flags)
+    
+    FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetMain(), CFRunLoopMode.defaultMode.rawValue)
+    FSEventStreamStart(streamRef)
+    
+    started = true
+  }
+  
+  public func stop() {
+    guard started == true else { return }
+    
+    FSEventStreamStop(streamRef)
+    FSEventStreamInvalidate(streamRef)
+    FSEventStreamRelease(streamRef)
+    streamRef = nil
+    
+    started = false
+  }
+}
diff --git a/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift b/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift
index fa7286d68a5e78c1a20ed50b3d4a372d90a4772d..8e1a19245d2bc4c16a6439e0270bec8733a88798 100644
--- a/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift
+++ b/launchers/macosx/I2PLauncher/Utils/LaunchAgent+Status.swift
@@ -34,15 +34,15 @@ extension LaunchAgent {
   /// Run `launchctl start` on the agent
   ///
   /// Check the status of the job with `.status()`
-  public func start() {
-    LaunchAgentManager.shared.start(self)
+  public func start(_ callback: ((Process) -> Void)? = nil ) {
+    LaunchAgentManager.shared.start(self, callback)
   }
   
   /// Run `launchctl stop` on the agent
   ///
   /// Check the status of the job with `.status()`
-  public func stop() {
-    LaunchAgentManager.shared.stop(self)
+  public func stop(_ callback: ((Process) -> Void)? = nil ) {
+    LaunchAgentManager.shared.stop(self, callback)
   }
   
   /// Run `launchctl load` on the agent
diff --git a/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift b/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift
index 2eb50fe3691444c6330e0ff1b0f9d2a24f081ed4..b9c26c78f891f99d580527cfdd491a372d353baa 100644
--- a/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift
+++ b/launchers/macosx/I2PLauncher/Utils/LaunchAgentManager.swift
@@ -88,41 +88,53 @@ extension LaunchAgentManager {
   /// Run `launchctl start` on the agent
   ///
   /// Check the status of the job with `.status(_: LaunchAgent)`
-  public func start(_ agent: LaunchAgent) {
+  public func start(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) {
     let arguments = ["start", agent.label]
-    Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    if ((termHandler) != nil) {
+      proc.terminationHandler = termHandler
+    }
   }
   
   /// Run `launchctl stop` on the agent
   ///
   /// Check the status of the job with `.status(_: LaunchAgent)`
-  public func stop(_ agent: LaunchAgent) {
+  public func stop(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) {
     let arguments = ["stop", agent.label]
-    Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    if ((termHandler) != nil) {
+      proc.terminationHandler = termHandler
+    }
   }
   
   /// Run `launchctl load` on the agent
   ///
   /// Check the status of the job with `.status(_: LaunchAgent)`
-  public func load(_ agent: LaunchAgent) throws {
+  public func load(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) throws {
     guard let agentURL = agent.url else {
       throw LaunchAgentManagerError.urlNotSet(label: agent.label)
     }
     
     let arguments = ["load", agentURL.path]
-    Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    if ((termHandler) != nil) {
+      proc.terminationHandler = termHandler
+    }
   }
   
   /// Run `launchctl unload` on the agent
   ///
   /// Check the status of the job with `.status(_: LaunchAgent)`
-  public func unload(_ agent: LaunchAgent) throws {
+  public func unload(_ agent: LaunchAgent, _ termHandler: ((Process) -> Void)? = nil ) throws {
     guard let agentURL = agent.url else {
       throw LaunchAgentManagerError.urlNotSet(label: agent.label)
     }
     
     let arguments = ["unload", agentURL.path]
-    Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    let proc = Process.launchedProcess(launchPath: LaunchAgentManager.launchctl, arguments: arguments)
+    if ((termHandler) != nil) {
+      proc.terminationHandler = termHandler
+    }
   }
   
   /// Retreives the status of the LaunchAgent from `launchctl`
diff --git a/launchers/macosx/I2PLauncher/logger_c.h b/launchers/macosx/I2PLauncher/logger_c.h
index 95b11c82363013138623792c33a7695c571e3193..102204b38f18bde1584459a1ecf24724739d9a32 100644
--- a/launchers/macosx/I2PLauncher/logger_c.h
+++ b/launchers/macosx/I2PLauncher/logger_c.h
@@ -82,4 +82,7 @@ inline void MLog(int loglevel, NSString* format, ...)
 }
 
 
+
+#define MMLog(format_string,...) ((MLog(1, [NSString stringWithFormat:format_string,##__VA_ARGS__])))
+
 #endif /* logger_c_h */
diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift
index e274d2017e2d57447d699094b3813cf3688b7a92..4776b84a377cd64c707a9a48f747ab67d28a14ec 100644
--- a/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift
+++ b/launchers/macosx/I2PLauncher/routermgmt/RouterManager.swift
@@ -25,6 +25,7 @@ class RouterManager : NSObject {
   let routerRunner = RouterRunner()
   
   var logViewStorage: NSTextStorage?
+  var lastRouterPid : String? = nil
   
   private static func handleRouterException(information:Any?) {
     Logger.MLog(level:1,"event! - handle router exception")
@@ -94,6 +95,27 @@ class RouterManager : NSObject {
     routerManager.eventManager.listenTo(eventName: "router_already_running", action: handleRouterAlreadyStarted)
     routerManager.eventManager.listenTo(eventName: "router_can_start", action: routerManager.routerRunner.StartAgent)
     routerManager.eventManager.listenTo(eventName: "router_can_setup", action: routerManager.routerRunner.SetupAgent)
+    
+    //routerManager.eventManager.listenTo(eventName: "launch_agent_running", action: routerManager.routerRunner.onLoadedAgent)
+    
+    routerManager.eventManager.listenTo(eventName: "launch_agent_running", action: {
+      let agent = RouterRunner.launchAgent!
+      let agentStatus = agent.status()
+      switch agentStatus {
+      case .running(let pid):
+        DispatchQueue.main.async {
+          routerManager.eventManager.trigger(eventName: "router_start", information: String(pid))
+          routerManager.routerRunner.routerStatus.setRouterStatus(true)
+          routerManager.routerRunner.routerStatus.setRouterRanByUs(true)
+          routerManager.eventManager.trigger(eventName: "router_pid", information: String(pid))
+        }
+        break
+        
+      default:
+        NSLog("wtf, agent status = \(agentStatus)")
+        break
+      }
+    })
     return routerManager
   }()
   
diff --git a/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift b/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift
index aa416f7709d8a9576a8e41c4051f9c2215277414..3f11a0c9a9b4f364380e78b3fad2fd492ab75f63 100644
--- a/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift
+++ b/launchers/macosx/I2PLauncher/routermgmt/RouterRunner.swift
@@ -22,7 +22,7 @@ class RouterRunner: NSObject {
   
   let domainLabel = "net.i2p.macosx.I2PRouter"
   
-  let plistName = "net.i2p.macosx.I2PRouterAgent.plist"
+  let plistName = "net.i2p.macosx.I2PRouter.plist"
   
   let defaultStartupCommand:String = "/usr/libexec/java_home"
   
@@ -35,6 +35,10 @@ class RouterRunner: NSObject {
   
   let appSupportPath = FileManager.default.urls(for: FileManager.SearchPathDirectory.applicationSupportDirectory, in: FileManager.SearchPathDomainMask.userDomainMask)
   
+  override init() {
+    super.init()
+  }
+  
   func SetupAgent() {
     let agent = SetupAndReturnAgent()
     RouterRunner.launchAgent = agent
@@ -106,78 +110,70 @@ class RouterRunner: NSObject {
     let shouldStartupAtLogin = userPreferences.bool(forKey: "startRouterAtLogin")
     agent.runAtLoad = shouldStartupAtLogin
     agent.keepAlive = true
-    
-    do {
-      
-      try LaunchAgentManager.shared.write(agent, called: self.plistName)
-      sleep(1)
-      try LaunchAgentManager.shared.load(agent)
-      sleep(1)
-      
-      let agentStatus = LaunchAgentManager.shared.status(agent)
-      switch agentStatus {
-      case .running:
-        break
-      case .loaded:
-        break
-      case .unloaded:
-        sleep(2)
-        break
+    DispatchQueue(label: "background_starter").async {
+      do {
+        try LaunchAgentManager.shared.write(agent, called: self.plistName)
+        sleep(1)
+        try LaunchAgentManager.shared.load(agent)
+        sleep(1)
+        
+        let agentStatus = LaunchAgentManager.shared.status(agent)
+        switch agentStatus {
+        case .running:
+          break
+        case .loaded:
+          DispatchQueue.main.async {
+            RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: agent)
+          }
+          break
+        case .unloaded:
+          break
+        }
+      } catch {
+        DispatchQueue.main.async {
+          RouterManager.shared().eventManager.trigger(eventName: "router_setup_error", information: "\(error)")
+        }
       }
-      
-      
-      RouterManager.shared().eventManager.trigger(eventName: "router_can_start", information: agent)
-    } catch {
-      RouterManager.shared().eventManager.trigger(eventName: "router_setup_error", information: "\(error)")
     }
     return agent
   }
   
-  func StartAgent(information:Any?) {
-    let agent = RouterRunner.launchAgent!
-    LaunchAgentManager.shared.start(agent)
-    sleep(1)
-    let agentStatus = agent.status()
-    switch agentStatus {
-    case .running(let pid):
-      RouterManager.shared().eventManager.trigger(eventName: "router_start", information: String(pid))
-      routerStatus.setRouterStatus(true)
-      routerStatus.setRouterRanByUs(true)
-      DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
-        // Delayed message to ensure UI has been initialized.
-        RouterManager.shared().eventManager.trigger(eventName: "router_pid", information: String(pid))
-      }
-      break
-      
-    default: break
+  
+  func StartAgent(_ information:Any? = nil) {
+    let agent = RouterRunner.launchAgent ?? information as! LaunchAgent
+    DispatchQueue(label: "background_block").async {
+      LaunchAgentManager.shared.start(agent, { (proc) in
+        NSLog("Will call onLaunchdStarted")
+      })
     }
   }
   
-  func StopAgent() {
-    var agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!)
-    switch agentStatus {
-    case .running:
-      LaunchAgentManager.shared.stop(RouterRunner.launchAgent!)
-      break
-    case .loaded, .unloaded:
-      try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
-      routerStatus.setRouterStatus(false)
-      routerStatus.setRouterRanByUs(false)
-      RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok")
-      return;
-      break
-    default: break
-    }
-    sleep(1)
-    agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!)
-    switch agentStatus {
-    case .loaded, .unloaded:
-      try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
-      routerStatus.setRouterStatus(false)
-      routerStatus.setRouterRanByUs(false)
-      RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok")
-      break
-    default: break
+  func StopAgent(_ callback: @escaping () -> () = {}) {
+    let agentStatus = LaunchAgentManager.shared.status(RouterRunner.launchAgent!)
+    DispatchQueue(label: "background_block").async {
+      do {
+        switch agentStatus {
+        case .running:
+          // For now we need to use unload to stop it.
+          try LaunchAgentManager.shared.unload(RouterRunner.launchAgent!, { (proc) in
+            // Called when stop is actually executed
+            proc.waitUntilExit()
+            DispatchQueue.main.async {
+              RouterManager.shared().eventManager.trigger(eventName: "router_stop", information: "ok")
+              callback()
+            }
+          })
+          try LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
+          break
+        case .unloaded:
+          // Seems it sometimes get unloaded on stop, we load it again.
+          try! LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
+          return
+        default: break
+        }
+      } catch {
+        NSLog("Error \(error)")
+      }
     }
   }
   
diff --git a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift
index 25885881955248a65f95813e9bec9f8729e1ece5..9f4199c31ff3411d0707c3d4393f98301fc5c645 100644
--- a/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift
+++ b/launchers/macosx/I2PLauncher/userinterface/RouterStatusView.swift
@@ -24,63 +24,73 @@ import Cocoa
   @IBOutlet var routerUptimeLabel: NSTextField?
   @IBOutlet var routerPIDLabel: NSTextField?
   
+  
   @IBOutlet var quickControlView: NSView?
   @IBOutlet var routerStartStopButton: NSButton?
   @IBOutlet var openConsoleButton: NSButton?
   
+  
+  @objc func actionBtnOpenConsole(_ sender: Any?) {
+    SwiftMainDelegate.openLink(url: "http://localhost:7657")
+  }
+  
   @objc func actionBtnStartRouter(_ sender: Any?) {
-    NSLog("START ROUTER")
+    NSLog("Router start clicked")
     /*if (RouterManager.shared().getRouterTask() == nil) {
       SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath)
     }*/
     (sender as! NSButton).isTransparent = true
     let routerStatus = RouterRunner.launchAgent?.status()
-    switch routerStatus {
-    case .loaded?:
-      RouterManager.shared().routerRunner.StartAgent(information: RouterRunner.launchAgent)
-    case .unloaded?:
-      do {
-        try LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
-        RouterManager.shared().routerRunner.StartAgent(information: RouterRunner.launchAgent)
-      } catch {
-        RouterManager.shared().eventManager.trigger(eventName: "router_exception", information: error)
+    DispatchQueue(label: "background_start").async {
+      switch routerStatus {
+      case .loaded?:
+        RouterManager.shared().routerRunner.StartAgent(RouterRunner.launchAgent)
+      case .unloaded?:
+        do {
+          try LaunchAgentManager.shared.load(RouterRunner.launchAgent!)
+          RouterManager.shared().routerRunner.StartAgent(RouterRunner.launchAgent)
+        } catch {
+          RouterManager.shared().eventManager.trigger(eventName: "router_exception", information: error)
+        }
+        break
+      default:
+        break
+      }
+      DispatchQueue.main.async {
+        self.reEnableButton()
       }
-      break
-    default:
-      break
     }
-    self.reEnableButton()
   }
   
   @objc func actionBtnStopRouter(_ sender: Any?) {
-    NSLog("STOP ROUTER")
-    let routerStatus = RouterRunner.launchAgent?.status()
-    switch routerStatus {
-    case .running?:
-      NSLog("Found running router")
-      RouterManager.shared().routerRunner.StopAgent()
-      break
-    default:
-      break
+    NSLog("Router stop clicked")
+    DispatchQueue(label: "background_shutdown").async {
+      RouterManager.shared().routerRunner.StopAgent({
+        RouterProcessStatus.isRouterRunning = false
+        RouterProcessStatus.isRouterChildProcess = false
+        NSLog("Router should be stopped by now.")
+      })
+      // Wait for it to die.
+      
     }
+    RouterManager.shared().eventManager.trigger(eventName: "toggle_popover")
     self.reEnableButton()
   }
   
-  @objc func actionBtnRestartRouter(sender: Any?) {
-    if (RouterManager.shared().getRouterTask() != nil) {
-      //RouterManager.shared().getRouterTask()?.requestRestart()
-    } else {
-      NSLog("Can't restart a non running router, start it however...")
-      //SBridge.sharedInstance().startupI2PRouter(RouterProcessStatus.i2pDirectoryPath)
-    }
+  func restartFn() {
+    RouterManager.shared().routerRunner.StopAgent({
+      sleep(30)
+      RouterManager.shared().routerRunner.StartAgent()
+    })
   }
   
   func handlerRouterStart(information:Any?) {
-    print("Triggered handlerRouterStart")
+    NSLog("Triggered handlerRouterStart")
     NSLog("PID2! %@", information as! String)
     routerPIDLabel?.cell?.stringValue = "Router PID: "+(information as! String)
     routerPIDLabel?.needsDisplay = true
     routerStatusLabel?.cell?.stringValue = "Router status: Running"
+    RouterManager.shared().lastRouterPid = (information as? String)
     self.toggleSetButtonStop()
     self.reEnableButton()
   }
@@ -112,10 +122,14 @@ import Cocoa
       RouterStatusView.instance = self
     }
     self.reEnableButton()
+    openConsoleButton!.cell!.action = #selector(self.actionBtnOpenConsole(_:))
+    openConsoleButton!.cell!.target = self
+    
   }
   
   func handleRouterStop() {
     routerPIDLabel?.cell?.stringValue = "Router PID: Not running"
+    RouterManager.shared().lastRouterPid = nil
     self.toggleSetButtonStart()
     reEnableButton()
   }
@@ -133,41 +147,49 @@ import Cocoa
   }
   
   func setRouterStatusLabelText() {
-    routerStartStopButton?.needsDisplay = true
     routerStartStopButton?.target = self
-    quickControlView?.needsDisplay = true
+    let staticStartedByLabelText = "Router started by launcher? "
+    let staticIsRunningLabelText = "Router status: "
+    let staticRouterVersionLabelText = "Router version: "
+    let staticRouterPidLabelText = "Router PID: "
     
-    do {
-      let currentStatus : AgentStatus = RouterRunner.launchAgent!.status()
-      if currentStatus == AgentStatus.loaded || currentStatus == AgentStatus.unloaded  {
-        routerStatusLabel?.cell?.stringValue = "Router status: Not running"
-      } else {
-        routerStatusLabel?.cell?.stringValue = "Router status: Running"
-      }
-    } catch {
-      // Ensure it's set even AgentStatus is nil (uninitialized yet..)
-      routerStatusLabel?.cell?.stringValue = "Router status: Not running"
+    // Use default here to avoid any potential crashes with force unwrapping
+    let currentStatus : AgentStatus = RouterRunner.launchAgent?.status() ?? AgentStatus.unloaded
+    if currentStatus == AgentStatus.loaded || currentStatus == AgentStatus.unloaded  {
+      routerStatusLabel?.cell?.stringValue = staticIsRunningLabelText+"Not running"
+    } else {
+      routerStatusLabel?.cell?.stringValue = staticIsRunningLabelText+"Running"
     }
     
-    let staticStartedByLabelText = "Router started by launcher?"
     if RouterProcessStatus.isRouterChildProcess {
-      routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" Yes"
+      routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+"Yes"
     } else {
-      routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+" No"
+      routerStartedByLabel?.cell?.stringValue = staticStartedByLabelText+"No"
     }
-    routerStartedByLabel?.needsDisplay = true
+    
+    // Try to display PID - if not, the string behind ?? is used as "default"
+    let tmpPidText = RouterManager.shared().lastRouterPid ?? "Not running"
+    routerPIDLabel?.cell?.stringValue = staticRouterPidLabelText+tmpPidText
     
     if let version = RouterProcessStatus.routerVersion {
-      routerVersionLabel?.cell?.stringValue = "Router version: " + version
+      routerVersionLabel?.cell?.stringValue = staticRouterVersionLabelText + version
     } else {
-      routerVersionLabel?.cell?.stringValue = "Router version: Still unknown"
+      routerVersionLabel?.cell?.stringValue = staticRouterVersionLabelText + "Still unknown"
     }
+    
     if let routerStartTime = RouterProcessStatus.routerStartedAt {
       routerUptimeLabel?.cell?.stringValue = "Uptime: Router started " + DateTimeUtils.timeAgoSinceDate(date: NSDate(date: routerStartTime), numericDates: false)
     } else {
       routerUptimeLabel?.cell?.stringValue = "Uptime: Router isn't running"
     }
+    
+    // Needs display function alerts the rendrerer that the UI parts need to be re-drawed.
+    routerStartStopButton?.needsDisplay = true
+    quickControlView?.needsDisplay = true
     routerUptimeLabel?.needsDisplay = true
+    routerVersionLabel?.needsDisplay = true
+    routerStartedByLabel?.needsDisplay = true
+    routerPIDLabel?.needsDisplay = true
   }
   
   
diff --git a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift
index ad70d9edb68e6c23b83c91fa85cd1d320c0a511f..07dc27f38bf3a61db38c7ca2758e701ace22df5d 100644
--- a/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift
+++ b/launchers/macosx/I2PLauncher/userinterface/StatusBarController.swift
@@ -44,19 +44,22 @@ import Cocoa
     let pidStr = information as! String
     NSLog("PID! %@", pidStr)
     showPopover(sender: nil)
-    //self.ctrl?.getRouterStatusView()?.handlerRouterStart(information: pidStr)
+    RouterManager.shared().lastRouterPid = pidStr
     self.ctrl?.getRouterStatusView()?.needsDisplay = true
   }
+  
+  func event_toggle(information:Any?) {
+    self.togglePopover(sender: self)
+  }
 
   
   override init() {
     super.init()
     self.ctrl = PopoverViewController.freshController()
     popover.contentViewController = self.ctrl
-    //updateObjectRef = SUUpdater.shared()
-    //updateObjectRef?.checkForUpdatesInBackground()
-   RouterManager.shared().eventManager.listenTo(eventName: "router_pid", action: pidReaction)
+    RouterManager.shared().eventManager.listenTo(eventName: "router_pid", action: pidReaction)
     
+    RouterManager.shared().eventManager.listenTo(eventName: "toggle_popover", action: event_toggle)
     
     if let button = statusItem.button {
       button.image = NSImage(named:"StatusBarButtonImage")
diff --git a/launchers/macosx/include/strutil.hpp b/launchers/macosx/include/strutil.hpp
index 085464fad48c759c32369257a4573a30cce98672..ca52442a2f2a2c1bd990e7d1ac38e4f1deb8fe48 100644
--- a/launchers/macosx/include/strutil.hpp
+++ b/launchers/macosx/include/strutil.hpp
@@ -100,11 +100,11 @@ static inline std::string trim_copy(std::string s) {
     return s;
 }
 
-NSString* stdStringToNSString(std::string &stdstr) {
+inline NSString* stdStringToNSString(std::string &stdstr) {
   return [NSString stringWithUTF8String:stdstr.c_str()];
 }
 
-std::string nsStringToStd(NSString* nsStr) {
+inline std::string nsStringToStd(NSString* nsStr) {
   const char *charlist = [nsStr UTF8String];
   return std::string(charlist);
 }
diff --git a/launchers/macosx/main.mm b/launchers/macosx/main.mm
index 06aa715c3ffc083c3beaa697bd2299898abaf9e7..60cbc5ebc86615197b11e40700384d3d7bc37b84 100644
--- a/launchers/macosx/main.mm
+++ b/launchers/macosx/main.mm
@@ -65,8 +65,8 @@ using namespace subprocess;
 
 - (void)extractI2PBaseDir:(void(^)(BOOL success, NSError *error))completion
 {
-  auto deployer = [[I2PDeployer alloc] initWithMetaInfo:self.metaInfo];
-  [deployer extractI2PBaseDir:completion];
+  self.deployer = [[I2PDeployer alloc] initWithMetaInfo:self.metaInfo];
+  [self.deployer extractI2PBaseDir:completion];
 }
 
 - (void)setApplicationDefaultPreferences {
@@ -77,6 +77,7 @@ using namespace subprocess;
     @"startRouterAtLogin": @NO,
     @"startRouterAtStartup": @NO,
     @"letRouterLiveEvenLauncherDied": @NO,
+    @"consolePortCheckNum": @7657,
     @"i2pBaseDirectory": (NSString *)CFStringCreateWithCString(NULL, const_cast<const char *>(getDefaultBaseDir().c_str()), kCFStringEncodingUTF8)
   }];
 
@@ -119,10 +120,10 @@ using namespace subprocess;
   // Initialize the Swift environment (the UI components)
   [self.swiftRuntime applicationDidFinishLaunching];
   
-  // TODO: Make the port a setting which defaults to 7657
-  if (port_check(7657) != 0)
+  NSInteger portNum = [self.userPreferences integerForKey:@"consolePortCheckNum"];
+  if (port_check((int)portNum) != 0)
   {
-    NSLog(@"Seems i2p is already running - I will not start the router (port 7657 is in use..)");
+    NSLog(@"Seems i2p is already running - I will not start the router (port %d is in use..)", (int)portNum);
     sendUserNotification(@"Found already running router", @"TCP port 7657 seem to be used by another i2p instance.");
     
     [routerStatus setRouterStatus: true];
@@ -131,6 +132,13 @@ using namespace subprocess;
   } else {
     shouldAutoStartRouter = true;
   }
+  if (![self.userPreferences boolForKey:@"startRouterAtLogin"] && ![self.userPreferences boolForKey:@"startRouterAtStartup"])
+  {
+    // In this case we don't want to find a running service
+    std::string launchdFile(RealHomeDirectory());
+    launchdFile += "/Library/LaunchAgents/net.i2p.macosx.I2PRouter.plist";
+    
+  }
 
   NSBundle *launcherBundle = [NSBundle mainBundle];
   
@@ -148,13 +156,6 @@ using namespace subprocess;
   std::string jarfile("-cp ");
   jarfile += [self.metaInfo.zipFile UTF8String];
   
-  // Might be hard to read if you're not used to Objective-C
-  // But this is a "function call" that contains a "callback function"
-  /*[routerStatus listenForEventWithEventName:@"router_can_start" callbackActionFn:^(NSString* information) {
-    NSLog(@"Got signal, router can be started");
-    [[SBridge sharedInstance] startupI2PRouter:self.metaInfo.i2pBase];
-  }];*/
-  
   // This will trigger the router start after an upgrade.
   [routerStatus listenForEventWithEventName:@"router_must_upgrade" callbackActionFn:^(NSString* information) {
     NSLog(@"Got signal, router must be deployed from base.zip");
diff --git a/launchers/macosx/osx_create_dmg.sh b/launchers/macosx/osx_create_dmg.sh
index d2ebf4b96d07036bf2c5aacbb7a477460df20b76..a44946c7815e850bf355f0addb2b1c46e68efa2f 100755
--- a/launchers/macosx/osx_create_dmg.sh
+++ b/launchers/macosx/osx_create_dmg.sh
@@ -1,14 +1,14 @@
 #!/usr/bin/env bash
 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 
-. .sign-secrets
+source $DIR/.sign-secrets
 
 APP_NAME="I2PLauncher"
-VERSION="0.9.36"
-DMG_BACKGROUND_IMG="${DIR}/Background.png"
+VERSION="0.9.37"
+DMG_BACKGROUND_IMG=${BACKGROUND_IMG:-"Background.png"}
 
 APP_EXE="${APP_NAME}.app/Contents/MacOS/${APP_NAME}"
-VOL_NAME="${APP_NAME} ${VERSION}"
+VOL_NAME="${APP_NAME}-${VERSION}"
 DMG_TMP="${VOL_NAME}-temp.dmg"
 DMG_FINAL="${VOL_NAME}.dmg"
 STAGING_DIR="/tmp/mkdmg$$"
@@ -23,13 +23,11 @@ if [ $(echo " $_BACKGROUND_IMAGE_DPI_H != 72.0 " | bc) -eq 1 -o $(echo " $_BACKG
 
    _DMG_BACKGROUND_TMP="${DMG_BACKGROUND_IMG%.*}"_dpifix."${DMG_BACKGROUND_IMG##*.}"
 
-   sips -s dpiWidth 72 -s dpiHeight 72 ${DMG_BACKGROUND_IMG} --out ${_DMG_BACKGROUND_TMP}
+   sips -s dpiWidth 72 -s dpiHeight 72 $DIR/${DMG_BACKGROUND_IMG} --out $DIR/${_DMG_BACKGROUND_TMP}
 
-   DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}"
-fi
 
-# clear out any old data
-rm -rf "${STAGING_DIR}" "${DMG_TMP}" "${DMG_FINAL}"
+  DMG_BACKGROUND_IMG="${_DMG_BACKGROUND_TMP}"
+fi
 
 # copy over the stuff we want in the final disk image to our staging dir
 mkdir -p "${STAGING_DIR}"
@@ -39,7 +37,7 @@ cp -rpf "${APP_NAME}.app" "${STAGING_DIR}"
 # figure out how big our DMG needs to be
 #  assumes our contents are at least 1M!
 SIZE=`du -sh "${STAGING_DIR}" | sed 's/\([0-9\.]*\)M\(.*\)/\1/'`
-SIZE=`echo "${SIZE} + 1.0" | bc | awk '{print int($1+0.5)}'`
+SIZE=`echo "${SIZE} + 23.0" | bc | awk '{print int($1+0.5)}'`
 
 if [ $? -ne 0 ]; then
    echo "Error: Cannot compute size of staging dir"
@@ -48,24 +46,28 @@ fi
 
 # create the temp DMG file
 hdiutil create -srcfolder "${STAGING_DIR}" -volname "${VOL_NAME}" -fs HFS+ \
-      -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "${DMG_TMP}"
+      -fsargs "-c c=64,a=16,e=16" -format UDRW -size ${SIZE}M "$RELEASE_DIR/${DMG_TMP}"
 
 echo "Created DMG: ${DMG_TMP}"
 
 # mount it and save the device
-DEVICE=$(hdiutil attach -readwrite -noverify "${DMG_TMP}" | \
+DEVICE=$(hdiutil attach -readwrite -noverify "$RELEASE_DIR/${DMG_TMP}" | \
          egrep '^/dev/' | sed 1q | awk '{print $1}')
 
 sleep 2
 
+
+
 # add a link to the Applications dir
 echo "Add link to /Applications"
-pushd /Volumes/"${VOL_NAME}"
-ln -s /Applications
+cd /Volumes/"${VOL_NAME}"
+ln -sf /Applications Applications
 
 # add a background image
-mkdir /Volumes/"${VOL_NAME}"/.background
-cp "${DMG_BACKGROUND_IMG}" /Volumes/"${VOL_NAME}"/.background/
+mkdir -p /Volumes/"${VOL_NAME}"/.background
+cp "$DIR/`basename ${DMG_BACKGROUND_IMG}`" /Volumes/"${VOL_NAME}"/.background/`basename ${DMG_BACKGROUND_IMG}`
+
+cd $RELEASE_DIR
 
 # tell the Finder to resize the window, set the background,
 #  change the icon size, place the icons in the right position, etc.
@@ -98,14 +100,15 @@ hdiutil detach "${DEVICE}"
 
 # now make the final image a compressed disk image
 echo "Creating compressed image"
-hdiutil convert "${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "${DMG_FINAL}"
+hdiutil convert "$RELEASE_DIR/${DMG_TMP}" -format UDZO -imagekey zlib-level=9 -o "$RELEASE_DIR/${DMG_FINAL}"
 
-codesign --force --sign "${APPLE_CODE_SIGNER_ID}" "${DMG_FINAL}"
+codesign --force --deep --sign "${APPLE_CODE_SIGNER_ID}" "$RELEASE_DIR/${DMG_FINAL}"
 
 # clean up
-rm -rf "${DMG_TMP}"
+rm -rf "$RELEASE_DIR/${DMG_TMP}"
 rm -rf "${STAGING_DIR}"
 
+
 echo 'Done.'
 
 exit