diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d8c6bfeb3207a422759ac9724420cd9db3da3e98..f78d12dbf4ba744e3885a6dcfe3599063ed05c15 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -239,6 +239,13 @@
       android:parentActivityName=".ui.setup.accounts.selection.AccountSelectionActivity"
       android:windowSoftInputMode="adjustResize" />
 
+    <!-- Core Setup Flow -->
+    <activity
+      android:name=".ui.setup.core.CoreSetupActivity"
+      android:exported="false"
+      android:label="@string/setup_core_title"
+      android:parentActivityName=".ui.chat.ChatActivity"
+      android:windowSoftInputMode="adjustResize" />
     <!-- Core User Setup Flow -->
     <activity
       android:name=".ui.setup.user.UserSetupActivity"
@@ -246,6 +253,7 @@
       android:label="@string/setup_user_title"
       android:parentActivityName=".ui.chat.ChatActivity"
       android:windowSoftInputMode="adjustResize" />
+    <!-- Network Setup Flow -->
     <activity
       android:name=".ui.setup.network.NetworkSetupActivity"
       android:exported="false"
diff --git a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
index 21b8e918abbfcb298af01854ad9b61c619a75fb1..0d955e3b22988bf883b2f02d0bb8435f45f97194 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/dagger/ActivityModule.kt
@@ -80,6 +80,8 @@ import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActiv
 import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionFragmentProvider
 import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupActivity
 import de.kuschku.quasseldroid.ui.setup.accounts.setup.AccountSetupFragmentProvider
+import de.kuschku.quasseldroid.ui.setup.core.CoreSetupActivity
+import de.kuschku.quasseldroid.ui.setup.core.CoreSetupFragmentProvider
 import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupActivity
 import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupFragmentProvider
 import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
@@ -207,6 +209,10 @@ abstract class ActivityModule {
   @ContributesAndroidInjector(modules = [NetworkSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
   abstract fun bindNetworkSetupActivity(): NetworkSetupActivity
 
+  @ActivityScope
+  @ContributesAndroidInjector(modules = [CoreSetupFragmentProvider::class, SettingsModule::class, DatabaseModule::class, ActivityBaseModule::class])
+  abstract fun bindCoreSetupActivity(): CoreSetupActivity
+
   @ActivityScope
   @ContributesAndroidInjector(modules = [QuasselServiceModule::class, SettingsModule::class, DatabaseModule::class])
   abstract fun bindQuasselService(): QuasselService
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
index dad3a2e534a4e8e7873ad5a89b00137c6b110658..dbde648ea40f9ef7f94d014466480df812df7cf7 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/ChatActivity.kt
@@ -60,6 +60,7 @@ import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.Message
 import de.kuschku.libquassel.protocol.Message_Type
 import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
 import de.kuschku.libquassel.protocol.message.HandshakeMessage
 import de.kuschku.libquassel.session.Error
 import de.kuschku.libquassel.session.ISession
@@ -84,6 +85,7 @@ import de.kuschku.quasseldroid.ui.chat.input.ChatlineFragment
 import de.kuschku.quasseldroid.ui.clientsettings.client.ClientSettingsActivity
 import de.kuschku.quasseldroid.ui.coresettings.CoreSettingsActivity
 import de.kuschku.quasseldroid.ui.setup.accounts.selection.AccountSelectionActivity
+import de.kuschku.quasseldroid.ui.setup.core.CoreSetupActivity
 import de.kuschku.quasseldroid.ui.setup.network.LinkNetwork
 import de.kuschku.quasseldroid.ui.setup.network.NetworkSetupActivity
 import de.kuschku.quasseldroid.ui.setup.user.UserSetupActivity
@@ -337,11 +339,21 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
         when (it) {
           is Error.HandshakeError -> it.message.let {
             when (it) {
+              is HandshakeMessage.ClientInitAck     ->
+                if (it.coreConfigured == false)
+                  CoreSetupActivity.launch(
+                    this,
+                    accountDatabase.accounts().findById(accountId),
+                    CoreSetupData.of(it)
+                  )
               is HandshakeMessage.ClientInitReject  ->
                 MaterialDialog.Builder(this)
                   .title(R.string.label_error_init)
                   .content(Html.fromHtml(it.errorString))
-                  .neutralText(R.string.label_close)
+                  .negativeText(R.string.label_disconnect)
+                  .onNegative { _, _ ->
+                    disconnect()
+                  }
                   .titleColorAttr(R.attr.colorTextPrimary)
                   .backgroundColorAttr(R.attr.colorBackgroundCard)
                   .contentColorAttr(R.attr.colorTextPrimary)
@@ -351,7 +363,10 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
                 MaterialDialog.Builder(this)
                   .title(R.string.label_error_setup)
                   .content(Html.fromHtml(it.errorString))
-                  .neutralText(R.string.label_close)
+                  .negativeText(R.string.label_disconnect)
+                  .onNegative { _, _ ->
+                    disconnect()
+                  }
                   .titleColorAttr(R.attr.colorTextPrimary)
                   .backgroundColorAttr(R.attr.colorBackgroundCard)
                   .contentColorAttr(R.attr.colorTextPrimary)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
index b0c89b6cfc2c1039a744d8645c84a3009fe1705c..5966688b4eddacbd0231ac802774cb78c8001b21 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
@@ -22,16 +22,13 @@ package de.kuschku.quasseldroid.ui.setup
 import android.content.Context
 import android.content.SharedPreferences
 import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
 import android.os.Build
 import android.os.Bundle
-import android.os.Parcelable
-import android.util.SparseArray
-import android.view.ViewGroup
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import androidx.appcompat.widget.ActionMenuView
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentStatePagerAdapter
+import androidx.core.graphics.drawable.DrawableCompat
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.viewpager.widget.ViewPager
@@ -123,13 +120,20 @@ abstract class ServiceBoundSetupActivity :
   private lateinit var pageChangeListener: SetupActivityViewPagerPageChangeListener
 
   private fun pageChanged() {
-    currentPage.value = adapter.getItem(viewPager.currentItem)
-    val drawable = if (viewPager.currentItem == adapter.totalCount - 1)
-      R.drawable.ic_check
-    else
-      R.drawable.ic_arrow_right
-    button.setImageResource(drawable)
-    currentPage.value?.requestFocus()
+    val page = adapter.getItem(viewPager.currentItem)
+    currentPage.value = page
+
+    val finish = viewPager.currentItem == adapter.totalCount - 1
+    button.contentDescription =
+      if (finish) descriptionFinish
+      else descriptionNext
+    button.setTooltip()
+    button.setImageDrawable(
+      if (finish) drawableFinish
+      else drawableNext
+    )
+
+    page.requestFocus()
   }
 
   fun updateRecentsHeader() {
@@ -143,6 +147,12 @@ abstract class ServiceBoundSetupActivity :
     updateRecentsHeader()
   }
 
+  private var drawableFinish: Drawable? = null
+  private var drawableNext: Drawable? = null
+
+  private var descriptionFinish: String? = null
+  private var descriptionNext: String? = null
+
   override fun onCreate(savedInstanceState: Bundle?) {
     setTheme(R.style.Theme_SetupTheme)
     super.onCreate(savedInstanceState)
@@ -152,6 +162,16 @@ abstract class ServiceBoundSetupActivity :
     setContentView(R.layout.activity_setup)
     ButterKnife.bind(this)
 
+    drawableFinish = getVectorDrawableCompat(R.drawable.ic_check)?.mutate()?.also {
+      DrawableCompat.setTint(it, -1)
+    }
+    drawableNext = getVectorDrawableCompat(R.drawable.ic_arrow_right)?.mutate()?.also {
+      DrawableCompat.setTint(it, -1)
+    }
+
+    descriptionFinish = getString(R.string.label_finish)
+    descriptionNext = getString(R.string.label_next)
+
     menuView.popupTheme = R.style.Widget_PopupOverlay_Light
     menuInflater.inflate(R.menu.activity_setup, menuView.menu)
     menuView.setOnMenuItemClickListener {
@@ -239,70 +259,6 @@ abstract class ServiceBoundSetupActivity :
     pageChanged()
   }
 
-  private class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
-    FragmentStatePagerAdapter(fragmentManager) {
-    private val retainedFragments = SparseArray<SlideFragment>()
-
-    val result = Bundle()
-      get() {
-        (0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
-          it.getData(field)
-        }
-        return field
-      }
-
-    var lastValidItem = -1
-      set(value) {
-        field = value
-        notifyDataSetChanged()
-      }
-    private val list = mutableListOf<SlideFragment>()
-
-    override fun getItem(position: Int): SlideFragment {
-      return retainedFragments.get(position) ?: list[position]
-    }
-
-    override fun getCount() = Math.min(list.size, lastValidItem + 2)
-    val totalCount get() = list.size
-    fun addFragment(fragment: SlideFragment) {
-      list.add(fragment)
-    }
-
-    override fun instantiateItem(container: ViewGroup, position: Int): Any {
-      val fragment = super.instantiateItem(container, position)
-      storeNewFragment(position, fragment as SlideFragment)
-      return fragment
-    }
-
-    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
-      retainedFragments.get(position)?.getData(result)
-      retainedFragments.remove(position)
-      super.destroyItem(container, position, `object`)
-    }
-
-    override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
-      super.restoreState(state, loader)
-      if (state != null) {
-        val bundle = state as Bundle
-        val keys = bundle.keySet()
-        for (key in keys) {
-          if (key.startsWith("f")) {
-            val index = Integer.parseInt(key.substring(1))
-            val f = fragmentManager.getFragment(bundle, key)
-            if (f != null && f is SlideFragment) {
-              storeNewFragment(index, f)
-            }
-          }
-        }
-      }
-    }
-
-    private fun storeNewFragment(index: Int, fragment: SlideFragment) {
-      fragment.initData = result
-      retainedFragments.put(index, fragment)
-    }
-  }
-
   override fun onStart() {
     if (Settings.appearance(this) != appearanceSettings) {
       recreate()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
index 9b1df118651b2ebe9a7f2ef36787c9fadbc34bc9..fcc7790be186b44bf5485c7436b52f408d21d41d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
@@ -24,15 +24,10 @@ import android.content.pm.PackageManager
 import android.graphics.drawable.Drawable
 import android.os.Build
 import android.os.Bundle
-import android.os.Parcelable
-import android.util.SparseArray
-import android.view.ViewGroup
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
 import androidx.appcompat.widget.ActionMenuView
 import androidx.core.graphics.drawable.DrawableCompat
-import androidx.fragment.app.FragmentManager
-import androidx.fragment.app.FragmentStatePagerAdapter
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import androidx.viewpager.widget.ViewPager
@@ -225,70 +220,6 @@ abstract class SetupActivity : DaggerAppCompatActivity() {
     pageChanged()
   }
 
-  private class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
-    FragmentStatePagerAdapter(fragmentManager) {
-    private val retainedFragments = SparseArray<SlideFragment>()
-
-    val result = Bundle()
-      get() {
-        (0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
-          it.getData(field)
-        }
-        return field
-      }
-
-    var lastValidItem = -1
-      set(value) {
-        field = value
-        notifyDataSetChanged()
-      }
-    private val list = mutableListOf<SlideFragment>()
-
-    override fun getItem(position: Int): SlideFragment {
-      return retainedFragments.get(position) ?: list[position]
-    }
-
-    override fun getCount() = Math.min(list.size, lastValidItem + 2)
-    val totalCount get() = list.size
-    fun addFragment(fragment: SlideFragment) {
-      list.add(fragment)
-    }
-
-    override fun instantiateItem(container: ViewGroup, position: Int): Any {
-      val fragment = super.instantiateItem(container, position)
-      storeNewFragment(position, fragment as SlideFragment)
-      return fragment
-    }
-
-    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
-      retainedFragments.get(position)?.getData(result)
-      retainedFragments.remove(position)
-      super.destroyItem(container, position, `object`)
-    }
-
-    override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
-      super.restoreState(state, loader)
-      if (state != null) {
-        val bundle = state as Bundle
-        val keys = bundle.keySet()
-        for (key in keys) {
-          if (key.startsWith("f")) {
-            val index = Integer.parseInt(key.substring(1))
-            val f = fragmentManager.getFragment(bundle, key)
-            if (f != null && f is SlideFragment) {
-              storeNewFragment(index, f)
-            }
-          }
-        }
-      }
-    }
-
-    private fun storeNewFragment(index: Int, fragment: SlideFragment) {
-      fragment.initData = result
-      retainedFragments.put(index, fragment)
-    }
-  }
-
   override fun onBackPressed() {
     if (viewPager.currentItem == 0)
       super.onBackPressed()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlideFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlideFragment.kt
index 7e95b20ed6b07b45d89e176482d4961565a820b0..96d5c5ef00a648116d1d1ad6fe314b27be530cff 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlideFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlideFragment.kt
@@ -92,6 +92,15 @@ abstract class SlideFragment : DaggerFragment() {
     this.view?.requestFocus()
   }
 
+  private var hasChangedListener: ((SlideFragment) -> Unit)? = null
+  fun setHasChangedListener(listener: ((SlideFragment) -> Unit)?) {
+    hasChangedListener = listener
+  }
+
+  protected fun hasChanged() {
+    hasChangedListener?.invoke(this)
+  }
+
   protected abstract fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
                                          savedInstanceState: Bundle?): View
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlidePagerAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlidePagerAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6db79e0793ea60a95b91290e2673b0fbceb923da
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SlidePagerAdapter.kt
@@ -0,0 +1,99 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup
+
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.SparseArray
+import android.view.ViewGroup
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentStatePagerAdapter
+
+class SlidePagerAdapter(private val fragmentManager: FragmentManager) :
+  FragmentStatePagerAdapter(fragmentManager) {
+  private val retainedFragments = SparseArray<SlideFragment>()
+
+  val result = Bundle()
+    get() {
+      (0 until retainedFragments.size()).map(retainedFragments::valueAt).forEach {
+        it.getData(field)
+      }
+      return field
+    }
+
+  var lastValidItem = -1
+    set(value) {
+      field = value
+      notifyDataSetChanged()
+    }
+  private val list = mutableListOf<SlideFragment>()
+
+  override fun getItem(position: Int): SlideFragment {
+    return retainedFragments.get(position) ?: list[position]
+  }
+
+  override fun getCount() = Math.min(list.size, lastValidItem + 2)
+  val totalCount get() = list.size
+  fun addFragment(fragment: SlideFragment) {
+    list.add(fragment)
+  }
+
+  override fun instantiateItem(container: ViewGroup, position: Int): Any {
+    val fragment = super.instantiateItem(container, position)
+    storeNewFragment(position, fragment as SlideFragment)
+    return fragment
+  }
+
+  override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
+    retainedFragments.get(position)?.getData(result)
+    retainedFragments.remove(position)
+    super.destroyItem(container, position, `object`)
+  }
+
+  override fun restoreState(state: Parcelable?, loader: ClassLoader?) {
+    super.restoreState(state, loader)
+    if (state != null) {
+      val bundle = state as Bundle
+      val keys = bundle.keySet()
+      for (key in keys) {
+        if (key.startsWith("f")) {
+          val index = Integer.parseInt(key.substring(1))
+          val f = fragmentManager.getFragment(bundle, key)
+          if (f != null && f is SlideFragment) {
+            storeNewFragment(index, f)
+          }
+        }
+      }
+    }
+  }
+
+  private fun storeNewFragment(index: Int, fragment: SlideFragment) {
+    fragment.initData = result
+    fragment.setHasChangedListener { hasChanged(index, fragment) }
+    retainedFragments.put(index, fragment)
+  }
+
+  fun hasChanged(index: Int, fragment: SlideFragment) {
+    fragment.getData(result)
+    if (index > -1 && index < totalCount) {
+      getItem(index + 1).setData(result)
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendChooseSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendChooseSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..25749886198b259d51e2ce40bfe1fe2062090531
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendChooseSlide.kt
@@ -0,0 +1,31 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
+import de.kuschku.quasseldroid.R
+
+class CoreAuthenticatorBackendChooseSlide : CoreBackendChooseSlide() {
+  override val title = R.string.slide_core_authenticator_select_title
+  override val description = R.string.slide_core_authenticator_select_description
+
+  override val inputKey = CoreSetupData::authenticatorInfo
+  override val outputKey = "authenticator"
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendSetupSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendSetupSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9e98906377b70179127df24cd08c51b60574c20e
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreAuthenticatorBackendSetupSlide.kt
@@ -0,0 +1,30 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import de.kuschku.quasseldroid.R
+
+class CoreAuthenticatorBackendSetupSlide : CoreBackendSetupSlide() {
+  override val title = R.string.slide_core_backend_setup_title
+  override val description = R.string.slide_core_backend_setup_description
+
+  override val inputKey = "authenticator"
+  override val outputKey = "authenticatorSetup"
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendAdapter.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6ac4024e6cbd3ab98f65f2fd93de87f311c826bc
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendAdapter.kt
@@ -0,0 +1,150 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.appcompat.widget.AppCompatRadioButton
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
+import de.kuschku.quasseldroid.R
+
+class CoreBackendAdapter : RecyclerView.Adapter<CoreBackendAdapter.BackendViewHolder>() {
+  private val selectionListeners = mutableSetOf<(CoreSetupBackend) -> Unit>()
+  private var selectedItem: Pair<CoreSetupBackend?, CoreSetupBackend?> = Pair(null, null)
+
+  fun selection() = selectedItem.second
+
+  private val clickListener = { item: CoreSetupBackend ->
+    selectionListener.invoke(item)
+  }
+
+  fun updateSelection(item: CoreSetupBackend) {
+    selectedItem = Pair(selectedItem.second, item)
+    submitList()
+  }
+
+  private val selectionListener = { item: CoreSetupBackend ->
+    updateSelection(item)
+    for (selectionListener in selectionListeners) {
+      selectionListener.invoke(item)
+    }
+  }
+
+  private var list: List<Pair<Boolean, CoreSetupBackend>> = emptyList()
+
+  fun submitList(
+    list: List<CoreSetupBackend> = this.list.map(Pair<Boolean, CoreSetupBackend>::second)) {
+    val oldList = this.list
+
+    val oldSelected = selectedItem.first
+    val newSelected = selectedItem.second
+
+    val newList = list.map {
+      Pair(it == newSelected, it)
+    }
+    this.list = newList
+
+    DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+      override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+        val oldItem = oldList[oldItemPosition].second
+        val newItem = newList[newItemPosition].second
+
+        return oldItem == newItem
+      }
+
+      override fun getOldListSize(): Int {
+        return oldList.size
+      }
+
+      override fun getNewListSize(): Int {
+        return newList.size
+      }
+
+      override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+        val oldItem = oldList[oldItemPosition].second
+        val newItem = newList[newItemPosition].second
+
+        return oldItem == newItem &&
+               oldItem != newSelected &&
+               newItem != newSelected &&
+               oldItem != oldSelected &&
+               newItem != oldSelected
+      }
+    }).dispatchUpdatesTo(this)
+  }
+
+  fun addSelectionListener(f: (CoreSetupBackend) -> Unit) {
+    selectionListeners.add(f)
+  }
+
+  override fun onBindViewHolder(holder: BackendViewHolder, position: Int) {
+    val item = list[position]
+    holder.bind(item.second, item.first)
+  }
+
+  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackendViewHolder {
+    val inflater = LayoutInflater.from(parent.context)
+    val view = inflater.inflate(R.layout.widget_core_backend, parent, false)
+    return BackendViewHolder(view, clickListener)
+  }
+
+  override fun getItemCount() = list.size
+
+  class BackendViewHolder(itemView: View, clickListener: (CoreSetupBackend) -> Unit) :
+    RecyclerView.ViewHolder(itemView) {
+    @BindView(R.id.backend_name)
+    lateinit var backendName: TextView
+
+    @BindView(R.id.backend_description)
+    lateinit var backendDescription: TextView
+
+    @BindView(R.id.backend_select)
+    lateinit var backendSelect: AppCompatRadioButton
+
+    private var item: CoreSetupBackend? = null
+
+    init {
+      ButterKnife.bind(this, itemView)
+      itemView.setOnClickListener {
+        item?.let(clickListener)
+      }
+    }
+
+    fun bind(backend: CoreSetupBackend, selected: Boolean) {
+      item = backend
+      backendName.text = backend.displayName
+      backendDescription.text = backend.description
+      backendSelect.isChecked = selected
+    }
+
+    fun clear() {
+      item = null
+      backendName.text = ""
+      backendDescription.text = ""
+      backendSelect.isChecked = false
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendChooseSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendChooseSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..98613658bf49b5c289a5f7681de5f482e479c356
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendChooseSlide.kt
@@ -0,0 +1,75 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DefaultItemAnimator
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.ui.setup.SlideFragment
+
+abstract class CoreBackendChooseSlide : SlideFragment() {
+  @BindView(R.id.account_list)
+  lateinit var backendList: RecyclerView
+
+  override fun isValid() = adapter.selection() != null
+
+  protected val adapter = CoreBackendAdapter()
+
+  abstract val outputKey: String
+  abstract val inputKey: ((CoreSetupData) -> List<CoreSetupBackend>)
+
+  override fun getData(data: Bundle) {
+    data.putSerializable(
+      outputKey,
+      adapter.selection()
+    )
+  }
+
+  override fun setData(data: Bundle) {
+    val message = data.getSerializable("data") as? CoreSetupData
+    adapter.submitList(message?.let(inputKey).orEmpty())
+    (data.getSerializable(outputKey) as? CoreSetupBackend)?.let(adapter::updateSelection)
+  }
+
+  override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
+                               savedInstanceState: Bundle?): View {
+    val view = inflater.inflate(R.layout.setup_select_account, container, false)
+    ButterKnife.bind(this, view)
+
+    backendList.layoutManager = LinearLayoutManager(context)
+    backendList.itemAnimator = DefaultItemAnimator()
+    backendList.adapter = adapter
+    adapter.addSelectionListener {
+      updateValidity()
+      hasChanged()
+    }
+
+    return view
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendSetupSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendSetupSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7f18d97a70137b07fc0cdc70132902c66b498e75
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreBackendSetupSlide.kt
@@ -0,0 +1,80 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
+import butterknife.BindView
+import butterknife.ButterKnife
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
+import de.kuschku.quasseldroid.R
+import de.kuschku.quasseldroid.ui.setup.SlideFragment
+
+abstract class CoreBackendSetupSlide : SlideFragment() {
+  @BindView(R.id.frame)
+  lateinit var frame: LinearLayout
+
+  @BindView(R.id.no_options_info)
+  lateinit var noOptionsInfo: View
+
+  override fun isValid() = true
+
+  abstract val inputKey: String
+  abstract val outputKey: String
+
+  private var widgets = mutableListOf<QuasselSetupEntry>()
+
+  override fun setData(data: Bundle) {
+    widgets.clear()
+    frame.removeAllViews()
+    noOptionsInfo.visibility = View.VISIBLE
+
+    val backend = data.getSerializable(inputKey) as? CoreSetupBackend
+    for (configEntry in backend?.setupData.orEmpty()) {
+      val entry = QuasselSetupEntry(frame.context,
+                                    null,
+                                    0,
+                                    configEntry)
+      widgets.add(entry)
+      frame.addView(entry)
+      noOptionsInfo.visibility = View.GONE
+    }
+  }
+
+  override fun getData(data: Bundle) {
+    val setupData = HashMap<String, QVariant_>()
+    for (configEntry in widgets) {
+      setupData[configEntry.key()] = configEntry.value()
+    }
+    data.putSerializable(outputKey, setupData)
+  }
+
+  override fun onCreateContent(inflater: LayoutInflater, container: ViewGroup?,
+                               savedInstanceState: Bundle?): View {
+    val view = inflater.inflate(R.layout.setup_core_backend_configure, container, false)
+    ButterKnife.bind(this, view)
+
+    return view
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupActivity.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d19699b254f7936a74a34573c3b7aaa47bf450a9
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupActivity.kt
@@ -0,0 +1,108 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackend
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.quassel.ExtendedFeature
+import de.kuschku.libquassel.util.helpers.value
+import de.kuschku.quasseldroid.persistence.AccountDatabase
+import de.kuschku.quasseldroid.ui.setup.ServiceBoundSetupActivity
+
+class CoreSetupActivity : ServiceBoundSetupActivity() {
+  override val initData = Bundle()
+
+  override fun onCreate(savedInstanceState: Bundle?) {
+    initData.clear()
+    initData.putAll(intent.extras)
+    super.onCreate(savedInstanceState)
+  }
+
+  override fun onDone(data: Bundle) {
+    val user = initData.getString("user")
+    val pass = initData.getString("pass")
+
+    val storageBackend = data.getSerializable("storage") as? CoreSetupBackend
+    val storageBackendSetup = data.getSerializable("storageSetup") as? HashMap<String, QVariant_>
+
+    val authenticatorBackend = data.getSerializable("authenticator") as? CoreSetupBackend
+    val authenticatorBackendSetup = data.getSerializable("authenticatorSetup") as? HashMap<String, QVariant_>
+
+    val setupData = HandshakeMessage.CoreSetupData(
+      adminUser = user,
+      adminPassword = pass,
+      backend = storageBackend?.backendId,
+      setupData = storageBackendSetup.orEmpty(),
+      authenticator = authenticatorBackend?.backendId,
+      authSetupData = authenticatorBackendSetup.orEmpty()
+    )
+
+    viewModel.sessionManager.value?.orNull()?.setupCore(setupData)
+    println(setupData)
+
+    setResult(Activity.RESULT_OK)
+    finish()
+  }
+
+  override val fragments
+    get() = if ((initData.getSerializable("data") as? CoreSetupData)
+        ?.features
+        ?.hasFeature(ExtendedFeature.Authenticators) == true) {
+      listOf(
+        CoreStorageBackendChooseSlide(),
+        CoreStorageBackendSetupSlide(),
+        CoreAuthenticatorBackendChooseSlide(),
+        CoreAuthenticatorBackendSetupSlide()
+      )
+    } else {
+      listOf(
+        CoreStorageBackendChooseSlide(),
+        CoreStorageBackendSetupSlide()
+      )
+    }
+
+  companion object {
+    fun launch(
+      context: Context,
+      account: AccountDatabase.Account? = null,
+      data: CoreSetupData? = null
+    ) = context.startActivity(intent(context, account, data))
+
+    fun intent(
+      context: Context,
+      account: AccountDatabase.Account? = null,
+      data: CoreSetupData? = null
+    ) = Intent(context, CoreSetupActivity::class.java).apply {
+      if (account != null) {
+        putExtra("user", account.user)
+        putExtra("pass", account.pass)
+      }
+      if (data != null) {
+        putExtra("data", data)
+      }
+    }
+  }
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e5f524b9510684207bd410cb51f3903ac1df35ed
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreSetupFragmentProvider.kt
@@ -0,0 +1,43 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import androidx.fragment.app.FragmentActivity
+import dagger.Binds
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+
+@Module
+abstract class CoreSetupFragmentProvider {
+  @Binds
+  abstract fun bindFragmentActivity(activity: CoreSetupActivity): FragmentActivity
+
+  @ContributesAndroidInjector
+  abstract fun bindCoreStorageBackendChooseSlide(): CoreStorageBackendChooseSlide
+
+  @ContributesAndroidInjector
+  abstract fun bindCoreStorageBackendSetupSlide(): CoreStorageBackendSetupSlide
+
+  @ContributesAndroidInjector
+  abstract fun bindCoreAuthenticatorBackendChooseSlide(): CoreAuthenticatorBackendChooseSlide
+
+  @ContributesAndroidInjector
+  abstract fun bindCoreAuthenticatorBackendSetupSlide(): CoreAuthenticatorBackendSetupSlide
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendChooseSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendChooseSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..74ba5f5801c519964940676ca631948378509b51
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendChooseSlide.kt
@@ -0,0 +1,31 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupData
+import de.kuschku.quasseldroid.R
+
+class CoreStorageBackendChooseSlide : CoreBackendChooseSlide() {
+  override val title = R.string.slide_core_backend_select_title
+  override val description = R.string.slide_core_backend_select_description
+
+  override val inputKey = CoreSetupData::backendInfo
+  override val outputKey = "storage"
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendSetupSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendSetupSlide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b9b3c0539b0377cb96603a3ee677288d7574e5a7
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/CoreStorageBackendSetupSlide.kt
@@ -0,0 +1,30 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import de.kuschku.quasseldroid.R
+
+class CoreStorageBackendSetupSlide : CoreBackendSetupSlide() {
+  override val title = R.string.slide_core_backend_setup_title
+  override val description = R.string.slide_core_backend_setup_description
+
+  override val inputKey = "storage"
+  override val outputKey = "storageSetup"
+}
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8c7f376ea087588fb0405a3e0d500479e01e88c7
--- /dev/null
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/core/QuasselSetupEntry.kt
@@ -0,0 +1,116 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.quasseldroid.ui.setup.core
+
+import android.content.Context
+import android.text.InputType
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.widget.FrameLayout
+import butterknife.BindView
+import butterknife.ButterKnife
+import com.google.android.material.textfield.TextInputEditText
+import com.google.android.material.textfield.TextInputLayout
+import de.kuschku.libquassel.protocol.QVariant_
+import de.kuschku.libquassel.protocol.Type
+import de.kuschku.libquassel.protocol.coresetup.CoreSetupBackendConfigElement
+import de.kuschku.libquassel.protocol.value
+import de.kuschku.quasseldroid.R
+
+class QuasselSetupEntry : FrameLayout {
+  @BindView(R.id.wrapper)
+  lateinit var wrapper: TextInputLayout
+
+  @BindView(R.id.field)
+  lateinit var field: TextInputEditText
+
+  private var data: CoreSetupBackendConfigElement? = null
+
+  constructor(context: Context) :
+    this(context, null)
+
+  constructor(context: Context, attrs: AttributeSet?) :
+    this(context, attrs, 0)
+
+  constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
+    this(context, attrs, defStyleAttr, null)
+
+  constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0,
+              data: CoreSetupBackendConfigElement? = null
+  ) : super(context, attrs, defStyleAttr) {
+
+    LayoutInflater.from(context).inflate(R.layout.widget_quassel_setup_entry, this, true)
+    ButterKnife.bind(this)
+
+    if (data != null) {
+      this.data = data
+
+      wrapper.hint = data.displayName
+      when {
+        data.defaultValue.type == Type.QString &&
+        data.key.contains("password", ignoreCase = true) -> {
+          wrapper.isPasswordVisibilityToggleEnabled = true
+          field.inputType =
+            InputType.TYPE_CLASS_TEXT or
+            InputType.TYPE_TEXT_VARIATION_PASSWORD
+          field.setText(data.defaultValue.value(""))
+        }
+        data.defaultValue.type == Type.QString &&
+        data.key.contains("hostname", ignoreCase = true) -> {
+          field.inputType =
+            InputType.TYPE_CLASS_TEXT or
+            InputType.TYPE_TEXT_VARIATION_URI
+          field.setText(data.defaultValue.value(""))
+        }
+        data.defaultValue.type == Type.QString           -> {
+          field.inputType =
+            InputType.TYPE_CLASS_TEXT or
+            InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
+            InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
+          field.setText(data.defaultValue.value(""))
+        }
+        data.defaultValue.type == Type.Int               -> {
+          field.inputType =
+            InputType.TYPE_CLASS_NUMBER
+          field.setText(data.defaultValue.value<Int>()?.toString())
+        }
+      }
+    }
+  }
+
+  fun key() = data?.key ?: ""
+
+  fun value(): QVariant_ {
+    val rawValue = field.text.toString()
+    val data = this.data
+
+    return when (data?.defaultValue?.type) {
+      Type.QString -> {
+        QVariant_.of(rawValue, data.defaultValue.type)
+      }
+      Type.Int     -> {
+        QVariant_.of(rawValue.toInt(), data.defaultValue.type)
+      }
+      else         -> {
+        QVariant_.of("", Type.QString)
+      }
+    }
+  }
+}
diff --git a/app/src/main/res/layout/setup_core_backend_configure.xml b/app/src/main/res/layout/setup_core_backend_configure.xml
new file mode 100644
index 0000000000000000000000000000000000000000..00676f878007d892effdb778b9a69b9ec63514a0
--- /dev/null
+++ b/app/src/main/res/layout/setup_core_backend_configure.xml
@@ -0,0 +1,37 @@
+<!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2019 Janne Koschinski
+  Copyright (c) 2019 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program. If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical"
+  android:padding="32dp">
+
+  <TextView
+    android:id="@+id/no_options_info"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:text="@string/setup_core_backend_no_options" />
+
+  <LinearLayout
+    android:id="@+id/frame"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" />
+</LinearLayout>
diff --git a/app/src/main/res/layout/widget_core_backend.xml b/app/src/main/res/layout/widget_core_backend.xml
new file mode 100644
index 0000000000000000000000000000000000000000..85324d285564a52917284a0e02f67afaed78fb80
--- /dev/null
+++ b/app/src/main/res/layout/widget_core_backend.xml
@@ -0,0 +1,79 @@
+<!--
+  Quasseldroid - Quassel client for Android
+  
+  Copyright (c) 2019 Janne Koschinski
+  Copyright (c) 2019 The Quassel Project
+  
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+  
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+  
+  You should have received a copy of the GNU General Public License along
+  with this program. If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:tools="http://schemas.android.com/tools"
+  android:layout_width="match_parent"
+  android:layout_height="wrap_content"
+  android:background="?attr/selectableItemBackground"
+  android:baselineAligned="false"
+  android:clickable="true"
+  android:focusable="true"
+  android:focusableInTouchMode="false"
+  android:minHeight="72dp"
+  android:orientation="horizontal"
+  android:paddingLeft="16dp"
+  android:paddingRight="16dp">
+
+  <LinearLayout
+    android:layout_width="48dp"
+    android:layout_height="72dp">
+
+    <androidx.appcompat.widget.AppCompatRadioButton
+      android:id="@+id/backend_select"
+      android:layout_width="48dp"
+      android:layout_height="48dp"
+      android:layout_gravity="center_vertical"
+      android:background="@null"
+      android:clickable="false" />
+
+  </LinearLayout>
+
+  <LinearLayout
+    android:layout_width="0dip"
+    android:layout_height="wrap_content"
+    android:layout_margin="8dp"
+    android:layout_weight="1"
+    android:gravity="center_vertical|start"
+    android:minHeight="72dp"
+    android:orientation="vertical">
+
+    <TextView
+      android:id="@+id/backend_name"
+      style="@style/Widget.RtlConformTextView"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:fontFamily="sans-serif-medium"
+      android:gravity="center_vertical|start"
+      android:lines="1"
+      android:singleLine="true"
+      android:textSize="14sp"
+      tools:text="Remote" />
+
+    <TextView
+      android:id="@+id/backend_description"
+      style="@style/Widget.RtlConformTextView"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:fontFamily="sans-serif"
+      android:gravity="center_vertical|start"
+      android:textSize="14sp"
+      tools:text="testUser on localhost" />
+  </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/layout/widget_quassel_setup_entry.xml b/app/src/main/res/layout/widget_quassel_setup_entry.xml
new file mode 100644
index 0000000000000000000000000000000000000000..315e876877359e485172a93cfee4b8f5418d9582
--- /dev/null
+++ b/app/src/main/res/layout/widget_quassel_setup_entry.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2019 Janne Koschinski
+  Copyright (c) 2019 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program. If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<com.google.android.material.textfield.TextInputLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:app="http://schemas.android.com/apk/res-auto"
+  xmlns:tools="http://schemas.android.com/tools"
+  android:id="@+id/wrapper"
+  android:layout_width="match_parent"
+  android:layout_height="wrap_content"
+  tools:ignore="LabelFor">
+
+  <com.google.android.material.textfield.TextInputEditText
+    android:id="@+id/field"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    app:errorEnabled="true" />
+</com.google.android.material.textfield.TextInputLayout>
diff --git a/app/src/main/res/values/strings_setup.xml b/app/src/main/res/values/strings_setup.xml
index e7f1f0dbed85d6dc885cd37462f2563ff7c42900..728420f946301f80c1ed1389c4b324bedce979bd 100644
--- a/app/src/main/res/values/strings_setup.xml
+++ b/app/src/main/res/values/strings_setup.xml
@@ -54,6 +54,8 @@
 
   <!-- Core Setup -->
 
+  <string name="setup_core_title">Setup Core</string>
+
   <!-- Core Authenticator Select -->
   <string name="slide_core_authenticator_select_title">Select Authentication Backend</string>
   <string name="slide_core_authenticator_select_description">Please select an authentication backend for the Quassel Core to use for authenticating users.</string>
@@ -62,10 +64,14 @@
   <string name="slide_core_backend_select_title">Select Storage Backend</string>
   <string name="slide_core_backend_select_description">Please select a database backend for the Quassel Core storage to store the backlog and other data in.</string>
 
+  <string name="label_backend">Backend</string>
+
   <!-- Core Backend Config -->
   <string name="slide_core_backend_setup_title">Configure Storage Backend</string>
   <string name="slide_core_backend_setup_description">Please configure the selected database backend.</string>
 
+  <string name="setup_core_backend_no_options">This backend has no configuration options, you’re done here!</string>
+
   <!-- User Setup -->
 
   <string name="setup_user_title">Setup User</string>
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
index 68ca2d858dc2cad8fbbd6e9f425bda6308cdba6f..471ee1211485677188ce2ecd03aad90984dec66a 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/QVariant.kt
@@ -24,7 +24,7 @@ import de.kuschku.libquassel.protocol.primitive.serializer.Serializer
 sealed class QVariant<T> constructor(val data: T?, val type: Type, val serializer: Serializer<T>) {
   class Typed<T> internal constructor(data: T?, type: Type, serializer: Serializer<T>) :
     QVariant<T>(data, type, serializer) {
-    override fun toString() = "QVariant.Typed(${type.serializableName}, $data})"
+    override fun toString() = "QVariant.Typed(${type.serializableName}, $data)"
     override fun equals(other: Any?): Boolean {
       if (this === other) return true
       if (other !is Typed<*>) return false
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bd0d861b9ac37cb40e5ac57847eb26f6d5bbab4b
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackend.kt
@@ -0,0 +1,58 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.coresetup
+
+import de.kuschku.libquassel.protocol.*
+import de.kuschku.libquassel.util.helpers.getOr
+import java.io.Serializable
+
+data class CoreSetupBackend(
+  val backendId: String,
+  val displayName: String,
+  val description: String,
+  val setupData: List<CoreSetupBackendConfigElement>
+) : Serializable {
+  companion object {
+    fun of(props: QVariantMap): CoreSetupBackend {
+      val entries = if (!props.containsKey("SetupData")) {
+        val result = mutableListOf<CoreSetupBackendConfigElement>()
+        val setupDefaults = props["SetupDefaults"]?.value<QVariantMap>(emptyMap()).orEmpty()
+        for (key in props["SetupKeys"]?.value<QStringList>(emptyList()).orEmpty()) {
+          val default = setupDefaults.getOr(key ?: "", QVariant_.of("", Type.QString))
+          result.add(CoreSetupBackendConfigElement(key ?: "", key ?: "", default))
+        }
+        result
+      } else {
+        props["SetupData"]?.value<QVariantList>(emptyList()).orEmpty().chunked(3) { (key, displayName, defaultValue) ->
+          CoreSetupBackendConfigElement(key.value(""), displayName.value(""), defaultValue)
+        }
+      }
+
+      val fallback = QVariant_.of("", Type.QString)
+
+      return CoreSetupBackend(
+        displayName = props.getOr("DisplayName", fallback).value(""),
+        backendId = props.getOr("BackendId", props.getOr("DisplayName", fallback)).value(""),
+        description = props.getOr("Description", fallback).value(""),
+        setupData = entries
+      )
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a56f1053f1256638dfb9e9cf6e96e3496e8ecc04
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupBackendConfigElement.kt
@@ -0,0 +1,79 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.coresetup
+
+import de.kuschku.libquassel.protocol.*
+import java.io.Serializable
+
+class CoreSetupBackendConfigElement : Serializable {
+  val key: String
+  val displayName: String
+  private val typeId: Int
+  private val customType: String
+  private val rawDefaultValue: Any?
+  val defaultValue: QVariant_
+    get() {
+      val type = Type.of(typeId)
+      return if (type == Type.UserType) {
+        val name = customType
+        val qType = QType.of(name) ?: throw IllegalArgumentException("No such type: $name")
+        QVariant.of<All_>(rawDefaultValue, qType)
+      } else {
+        QVariant.of<All_>(rawDefaultValue,
+                          type ?: throw IllegalArgumentException("No such type: $type"))
+      }
+    }
+
+  constructor(key: String, displayName: String, defaultValue: QVariant_) {
+    this.key = key
+    this.displayName = displayName
+    this.typeId = defaultValue.type.id
+    this.customType = defaultValue.type.serializableName
+    this.rawDefaultValue = defaultValue.data
+  }
+
+  override fun equals(other: Any?): Boolean {
+    if (this === other) return true
+    if (javaClass != other?.javaClass) return false
+
+    other as CoreSetupBackendConfigElement
+
+    if (key != other.key) return false
+    if (displayName != other.displayName) return false
+    if (typeId != other.typeId) return false
+    if (customType != other.customType) return false
+    if (rawDefaultValue != other.rawDefaultValue) return false
+
+    return true
+  }
+
+  override fun hashCode(): Int {
+    var result = key.hashCode()
+    result = 31 * result + displayName.hashCode()
+    result = 31 * result + typeId
+    result = 31 * result + customType.hashCode()
+    result = 31 * result + (rawDefaultValue?.hashCode() ?: 0)
+    return result
+  }
+
+  override fun toString(): String {
+    return "CoreBackendSetupDataEntry(key='$key', displayName='$displayName', defaultValue='$defaultValue')"
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupData.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7bdd78a10a5ca879982c236bd4feaf8671a11f54
--- /dev/null
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/coresetup/CoreSetupData.kt
@@ -0,0 +1,46 @@
+/*
+ * Quasseldroid - Quassel client for Android
+ *
+ * Copyright (c) 2019 Janne Koschinski
+ * Copyright (c) 2019 The Quassel Project
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 3 as published
+ * by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.kuschku.libquassel.protocol.coresetup
+
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
+import de.kuschku.libquassel.protocol.value
+import de.kuschku.libquassel.quassel.QuasselFeatures
+import java.io.Serializable
+
+data class CoreSetupData(
+  val backendInfo: List<CoreSetupBackend>,
+  val authenticatorInfo: List<CoreSetupBackend>,
+  val features: QuasselFeatures
+) : Serializable {
+  companion object {
+    fun of(data: HandshakeMessage.ClientInitAck): CoreSetupData {
+
+      return CoreSetupData(
+        backendInfo = data.backendInfo.orEmpty().map {
+          CoreSetupBackend.of(it.value(emptyMap()))
+        },
+        authenticatorInfo = data.authenticatorInfo.orEmpty().map {
+          CoreSetupBackend.of(it.value(emptyMap()))
+        },
+        features = QuasselFeatures(data.coreFeatures, data.featureList)
+      )
+    }
+  }
+}
diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt
index 45c8a17d5e65c9b1294a19178409ca894a39c880..f504c0aed950ac5f62b390ecacf37197ca6f8237 100644
--- a/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/protocol/message/HandshakeMessage.kt
@@ -23,8 +23,9 @@ import de.kuschku.libquassel.protocol.Legacy_Features
 import de.kuschku.libquassel.protocol.QVariantList
 import de.kuschku.libquassel.protocol.QVariantMap
 import de.kuschku.libquassel.protocol.value
+import java.io.Serializable
 
-sealed class HandshakeMessage {
+sealed class HandshakeMessage : Serializable {
   class ClientInit(
     val clientVersion: String?, val buildDate: String?,
     val clientFeatures: Legacy_Features?, val featureList: List<String>
@@ -55,7 +56,7 @@ sealed class HandshakeMessage {
                       val authSetupData: QVariantMap?) :
     HandshakeMessage() {
     override fun toString(): String {
-      return "CoreSetupData"
+      return "CoreSetupData(adminUser=$adminUser, adminPassword=$adminPassword, backend=$backend, setupData=$setupData, authenticator=$authenticator, authSetupData=$authSetupData)"
     }
   }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
index 6cb5745716bfb8e003120403b42d1cef4de66516..89efe81ceb0171fea10b369a75bab3e4545332a3 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/BufferInfo.kt
@@ -19,6 +19,7 @@
 
 package de.kuschku.libquassel.quassel
 
+import de.kuschku.libquassel.protocol.Buffer_Type
 import de.kuschku.libquassel.protocol.Buffer_Types
 import de.kuschku.libquassel.util.flag.Flag
 import de.kuschku.libquassel.util.flag.Flags
@@ -26,11 +27,11 @@ import de.kuschku.libquassel.util.flag.ShortFlag
 import de.kuschku.libquassel.util.flag.ShortFlags
 
 data class BufferInfo(
-  var bufferId: Int,
-  var networkId: Int,
-  var type: Buffer_Types,
-  var groupId: Int,
-  var bufferName: String?
+  var bufferId: Int = -1,
+  var networkId: Int = -1,
+  var type: Buffer_Types = Buffer_Type.of(),
+  var groupId: Int = -1,
+  var bufferName: String? = null
 ) {
   enum class Type(override val bit: Short) : ShortFlag<Type> {
     InvalidBuffer(0x00),
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/QuasselFeatures.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/QuasselFeatures.kt
index 808585a4fad1e29a2316ac91d34252f53c0d7593..dd56be021ee17602cd63e2c8ff3ca1f3f544cf3d 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/QuasselFeatures.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/QuasselFeatures.kt
@@ -21,11 +21,12 @@ package de.kuschku.libquassel.quassel
 
 import de.kuschku.libquassel.protocol.Legacy_Feature
 import de.kuschku.libquassel.protocol.Legacy_Features
+import java.io.Serializable
 
 class QuasselFeatures(
   val enabledFeatures: Set<ExtendedFeature>,
   val unknownFeatures: Set<String> = emptySet()
-) {
+) : Serializable {
   constructor(legacyFeatures: Legacy_Features?, extendedFeatures: Collection<String>) : this(
     legacyFeatures?.enabledValues()?.map(Legacy_Feature::toExtended).orEmpty() union
       extendedFeatures.mapNotNull(ExtendedFeature.Companion::of),
@@ -38,6 +39,11 @@ class QuasselFeatures(
 
   infix fun hasFeature(feature: ExtendedFeature) = enabledFeatures.contains(feature)
 
+  override fun toString(): String {
+    return "QuasselFeatures(enabledFeatures=$enabledFeatures, unknownFeatures=$unknownFeatures)"
+  }
+
+
   companion object {
     fun empty() = QuasselFeatures(emptySet(), emptySet())
     fun all() = QuasselFeatures(
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
index f122fe96f9fd4bfc048f7c210d35f7d30f7572fb..8e0e595e74e50b8d56d89420cd80a713f72acbbc 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/ISession.kt
@@ -23,6 +23,7 @@ import de.kuschku.libquassel.connection.ConnectionState
 import de.kuschku.libquassel.connection.Features
 import de.kuschku.libquassel.protocol.IdentityId
 import de.kuschku.libquassel.protocol.NetworkId
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
 import de.kuschku.libquassel.quassel.QuasselFeatures
 import de.kuschku.libquassel.quassel.syncables.*
 import de.kuschku.libquassel.util.Optional
@@ -65,6 +66,7 @@ interface ISession : Closeable {
   val lag: Observable<Long>
 
   fun login(user: String, pass: String)
+  fun setupCore(setupData: HandshakeMessage.CoreSetupData)
 
   companion object {
     val NULL = object : ISession {
@@ -101,6 +103,7 @@ interface ISession : Closeable {
       override fun identity(id: IdentityId): Identity? = null
 
       override fun login(user: String, pass: String) = Unit
+      override fun setupCore(setupData: HandshakeMessage.CoreSetupData) = Unit
       override fun close() = Unit
     }
   }
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
index fbafa47d4f1880091c6589a1d192d006c6e566f5..48a65e5d2851a678df6657be210a7b0ff5461e87 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt
@@ -144,6 +144,10 @@ class Session(
     login()
   }
 
+  override fun setupCore(setupData: HandshakeMessage.CoreSetupData) {
+    dispatch(setupData)
+  }
+
   override fun handle(f: HandshakeMessage.CoreSetupAck): Boolean {
     login()
     return true
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
index b240aa1580cdb2237705bbcd9cf3ffd97da97227..d5f18920ccf38d88d17e0d61e8a1bae3dbe3fce3 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
@@ -23,6 +23,7 @@ import de.kuschku.libquassel.connection.ConnectionState
 import de.kuschku.libquassel.connection.HostnameVerifier
 import de.kuschku.libquassel.connection.SocketAddress
 import de.kuschku.libquassel.protocol.ClientData
+import de.kuschku.libquassel.protocol.message.HandshakeMessage
 import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
 import de.kuschku.libquassel.util.compatibility.HandlerService
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
@@ -105,6 +106,10 @@ class SessionManager(
     inProgressSession.value.login(user, pass)
   }
 
+  fun setupCore(setupData: HandshakeMessage.CoreSetupData) {
+    inProgressSession.value.setupCore(setupData)
+  }
+
   fun connect(
     clientData: ClientData,
     trustManager: X509TrustManager,