diff --git a/src/briar/api/api.py b/src/briar/api/api.py
index 97409faf7f31526f578cd4942f08db8bdcdafbff..5afcacf44d9929299c3f6a581198a0b68c4d4501 100644
--- a/src/briar/api/api.py
+++ b/src/briar/api/api.py
@@ -2,13 +2,17 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 # License-Filename: LICENSE.md
 
-from subprocess import Popen, PIPE
+from subprocess import Popen, PIPE, STDOUT
+from threading import Thread
 
 
 class Api:
 
+    _process = None
+
     def __init__(self, headless_jar, debug=False):
-        self.headless_jar = headless_jar
+        self._command = ['java', '-jar', headless_jar, '-v']
+        self._debug = debug
 
     def has_account(self):
         from pathlib import Path
@@ -16,18 +20,55 @@ class Api:
         from os.path import isdir, join
         return isdir(join(home, ".briar", "db"))
 
+    def is_running(self):
+        return (self._process is not None) and (self._process.poll() is None)
+
     def login(self, password, callback):
-        p = Popen(['java', '-jar', self.headless_jar, '-v'],
-                  stdin=PIPE, universal_newlines=True)
-        p.communicate(password + '\n')
-        callback()
+        self._start_and_watch(callback)
+        startup_thread = Thread(target=self._login, args=(password,),
+                                daemon=True)
+        startup_thread.start()
 
     def register(self, credentials, callback):
-        p = Popen(['java', '-jar', self.headless_jar, '-v'],
-                  stdin=PIPE, universal_newlines=True)
-        p.communicate(credentials[0] + '\n' + credentials[1] +
-                      '\n' + credentials[1] + '\n')
-        callback()
+        if len(credentials) != 2:
+            raise Exception("Can't process credentials")
+        self._start_and_watch(callback)
+        startup_thread = Thread(target=self._register, args=(credentials,),
+                                daemon=True)
+        startup_thread.start()
 
     def stop(self):
-        pass
+        if not self.is_running():
+            raise Exception("Nothing to stop")
+        self._process.terminate()
+
+    def _start_and_watch(self, callback):
+        if self.is_running():
+            raise Exception("API already running")
+        self._process = Popen(self._command, stdin=PIPE,
+                              stdout=PIPE, stderr=STDOUT)
+        watch_thread = Thread(target=self._watch_thread, args=(callback,),
+                              daemon=True)
+        watch_thread.start()
+
+    def _watch_thread(self, callback):
+        for line in self._process.stdout:
+            if self._debug:
+                print(line.decode("utf-8"), end='', flush=True)
+            # TODO: Sometimes we miss this line (or Briar doesn't send it?)
+            if "Listening on http://localhost:7000/" in line.decode("utf-8"):
+                callback(True)
+                return
+        callback(False)
+
+    def _login(self, password):
+        if not self.is_running():
+            raise Exception("Can't login; API not running")
+        self._process.communicate((password + '\n').encode("utf-8"))
+
+    def _register(self, credentials):
+        if not self.is_running():
+            raise Exception("Can't register; API not running")
+        self._process.communicate((credentials[0] + '\n' +
+                                   credentials[1] + '\n' +
+                                   credentials[1] + '\n').encode("utf-8"))
diff --git a/src/briar/gtk/application.py b/src/briar/gtk/application.py
index 5ab8c1d60b9b4df151cf01a30fb67043fdd0252f..ba120a5598c62c2070cd06abeaa00654f96b9883 100644
--- a/src/briar/gtk/application.py
+++ b/src/briar/gtk/application.py
@@ -36,7 +36,7 @@ class Application(Gtk.Application):
                                              Gtk.STYLE_PROVIDER_PRIORITY_USER)
 
         self.debug = True  # TODO: Change this in production
-        self.__api = Api('/app/briar/briar-headless.jar', self.debug)
+        self.api = Api('/app/briar/briar-headless.jar', self.debug)
 
     def do_activate(self):
         if self.window is None:
@@ -45,10 +45,6 @@ class Application(Gtk.Application):
         self.window.present()
 
     def quit(self):
-        self.__api.stop()
+        self.api.stop()
         self.window.hide()
         Gio.Application.quit(self)
-
-    @property
-    def api(self):
-        return self.__api
diff --git a/src/briar/gtk/containers/startup.py b/src/briar/gtk/containers/startup.py
index 2667a6e6329e75124ed7c1453bdbe27d9ce2a620..251af6da828a5213337d0841e7ff2c0b5dd9a9a5 100644
--- a/src/briar/gtk/containers/startup.py
+++ b/src/briar/gtk/containers/startup.py
@@ -14,8 +14,9 @@ class StartupContainer(Container):
 
     def __init__(self):
         super().__init__()
-        self.__setup_view()
-        self.__register_signals()
+        self._setup_view()
+        self._register_signals()
+        self._api = App().api
 
     def on_username_button_clicked(self, button):
         self.builder.get_object("username_grid").set_visible(False)
@@ -24,14 +25,13 @@ class StartupContainer(Container):
 
     def on_password_button_clicked(self, button):
         password = self.builder.get_object("password_entry").get_text()
-        App().api.register((self.username, password),
-                           self.__startup_completed)
+        self._api.register((self.username, password), self._startup_finished)
 
     def on_login_pressed(self, button):
         password = self.builder.get_object("password_entry").get_text()
-        App().api.login(password, self.__startup_completed)
+        self._api.login(password, self._startup_finished)
 
-    def __setup_view(self):
+    def _setup_view(self):
         self.set_hexpand(True)
         self.set_vexpand(True)
         if not App().api.has_account():
@@ -42,10 +42,14 @@ class StartupContainer(Container):
             self.add(self.builder.get_object("login"))
         self.builder.connect_signals(self)
 
-    def __register_signals(self):
+    def _register_signals(self):
         GObject.signal_new("briar_startup_completed", Gtk.Overlay,
                            GObject.SignalFlags.RUN_LAST, GObject.TYPE_BOOLEAN,
                            (GObject.TYPE_STRING,))
 
-    def __startup_completed(self, succeded=True):
-        self.emit("briar_startup_completed", ("succeded",))
+    def _startup_finished(self, succeeded):
+        if succeeded:
+            print("Startup succeeded")
+            self.emit("briar_startup_completed", (succeeded,))
+            return
+        print("Startup failed")
diff --git a/src/briar/gtk/window.py b/src/briar/gtk/window.py
index f44cc8b1525aac15d0a8699a55ef8e3f2bd819b8..78362d66819a81e05cb451070d5466c8cb86d118 100644
--- a/src/briar/gtk/window.py
+++ b/src/briar/gtk/window.py
@@ -23,7 +23,7 @@ class Window(Gtk.ApplicationWindow):
         return self.__container
 
     def __setup_content(self):
-        self.__setup_size((800, 600))  # TODO: do properly (constants, save)
+        self.__setup_size((600, 400))  # TODO: do properly (constants, save)
         self.__setup_grid()
         self.__setup_startup_container()
 
@@ -47,9 +47,9 @@ class Window(Gtk.ApplicationWindow):
                                  self.__on_startup_completed)
         self.__grid.add(self.__container)
 
-    def __on_startup_completed(self):
-        self.__grid.remove_all()
-        self.__setup_main_container(self)
+    def __on_startup_completed(self, inst, obj):
+        self.__grid = Gtk.Grid()
+        self.__setup_main_container()
 
     def __setup_main_container(self):
         self.__container = MainContainer()