diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..eb43d04608a6587011083327ca41a9e0b8c715ef
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientProxyMessageHandler.kt
@@ -0,0 +1,55 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.session.ProxyMessageHandler
+import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
+import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
+import de.justjanne.libquassel.protocol.syncables.invoker.Invokers
+
+class ClientProxyMessageHandler(
+  val heartBeatHandler: HeartBeatHandler,
+  val objectRepository: ObjectRepository,
+  val rpcHandler: RpcHandler
+) : ProxyMessageHandler {
+  override fun emit(message: SignalProxyMessage) {
+    TODO("Not Implemented")
+  }
+
+  override fun dispatch(message: SignalProxyMessage) {
+    when (message) {
+      is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp))
+      is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true)
+      is SignalProxyMessage.InitData -> objectRepository.init(
+        objectRepository.find(message.className, message.objectName) ?: return,
+        message.initData
+      )
+      is SignalProxyMessage.InitRequest -> {
+        // Ignore incoming requests, we’re a client, we shouldn’t ever receive these
+      }
+      is SignalProxyMessage.Rpc -> {
+        val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler")
+          ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler")
+        invoker.invoke(rpcHandler, message.slotName, message.params)
+      }
+      is SignalProxyMessage.Sync -> {
+        val invoker = Invokers.get(ProtocolSide.CLIENT, message.className)
+          ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className)
+        val syncable = objectRepository.find(message.className, message.objectName)
+          ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName)
+        invoker.invoke(syncable, message.slotName, message.params)
+      }
+    }
+  }
+}
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1a16c05721c3d257d064416dcd5b7cdd4186cf79
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientRpcHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client
+
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.StatusMessage
+import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.Session
+import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import java.nio.ByteBuffer
+
+class ClientRpcHandler(session: Session) : RpcHandler(session) {
+  override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
+    val objectRepository = session?.objectRepository ?: return
+    val className = StringSerializerUtf8.deserializeRaw(classname)
+    val syncable = objectRepository.find(className, oldName ?: return)
+      ?: return
+    objectRepository.rename(syncable, newName ?: return)
+  }
+
+  override fun displayMsg(message: Message) {
+    messages.tryEmit(message)
+  }
+
+  override fun displayStatusMsg(net: String?, msg: String?) {
+    statusMessage.tryEmit(StatusMessage(net, msg ?: return))
+  }
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun messages(): Flow<Message> = messages
+
+  @PublishedApi
+  internal val messages = MutableSharedFlow<Message>()
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
+
+  @PublishedApi
+  internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
+}
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
index d194481a26865333203fda8109aa17db08db7f18..07ee170da88b6077b86849eda3d85041adc914d3 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/ClientSession.kt
@@ -9,17 +9,14 @@
 package de.justjanne.libquassel.client
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.exceptions.RpcInvocationFailedException
-import de.justjanne.libquassel.protocol.models.Message
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
-import de.justjanne.libquassel.protocol.models.StatusMessage
+import de.justjanne.libquassel.client.io.CoroutineChannel
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.CommonSyncProxy
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.HeartBeatHandler
 import de.justjanne.libquassel.protocol.syncables.ObjectRepository
-import de.justjanne.libquassel.protocol.syncables.Session
-import de.justjanne.libquassel.protocol.syncables.SyncableStub
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -32,27 +29,35 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
-import de.justjanne.libquassel.protocol.syncables.invoker.Invokers
 import de.justjanne.libquassel.protocol.syncables.state.NetworkState
 import de.justjanne.libquassel.protocol.util.update
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import java.nio.ByteBuffer
 
-class ClientSession : Session {
-  override val protocolSide = ProtocolSide.CLIENT
+class ClientSession(
+  private val connection: CoroutineChannel
+) : Session {
+  override val side = ProtocolSide.CLIENT
+
+  private val rpcHandler = ClientRpcHandler(this)
+  private val heartBeatHandler = HeartBeatHandler()
   override val objectRepository = ObjectRepository()
-  override val rpcHandler = ClientRpcHandler(this)
-  val heartBeatHandler = HeartBeatHandler()
+  private val proxyMessageHandler = ClientProxyMessageHandler(
+    heartBeatHandler,
+    objectRepository,
+    rpcHandler
+  )
+  override val proxy = CommonSyncProxy(
+    ProtocolSide.CLIENT,
+    objectRepository,
+    proxyMessageHandler
+  )
 
   override fun network(id: NetworkId) = state().networks[id]
   override fun addNetwork(id: NetworkId) {
     val network = Network(this, state = NetworkState(id))
-    synchronize(network)
+    proxy.synchronize(network)
     state.update {
       copy(networks = networks + Pair(id, network))
     }
@@ -60,7 +65,7 @@ class ClientSession : Session {
 
   override fun removeNetwork(id: NetworkId) {
     val network = network(id) ?: return
-    stopSynchronize(network)
+    proxy.stopSynchronize(network)
     state.update {
       copy(networks = networks - id)
     }
@@ -71,7 +76,7 @@ class ClientSession : Session {
   override fun addIdentity(properties: QVariantMap) {
     val identity = Identity(this)
     identity.fromVariantMap(properties)
-    synchronize(identity)
+    proxy.synchronize(identity)
     state.update {
       copy(identities = identities + Pair(identity.id(), identity))
     }
@@ -79,12 +84,20 @@ class ClientSession : Session {
 
   override fun removeIdentity(id: IdentityId) {
     val identity = identity(id) ?: return
-    stopSynchronize(identity)
+    proxy.stopSynchronize(identity)
     state.update {
       copy(identities = identities - id)
     }
   }
 
+  override fun rename(className: String, oldName: String, newName: String) {
+    rpcHandler.objectRenamed(
+      StringSerializerUtf8.serializeRaw(className),
+      oldName,
+      newName
+    )
+  }
+
   override val aliasManager get() = state().aliasManager
 
   override val backlogManager get() = state().backlogManager
@@ -105,46 +118,6 @@ class ClientSession : Session {
 
   override val networkConfig get() = state().networkConfig
 
-  override fun synchronize(syncable: SyncableStub) {
-    if (objectRepository.add(syncable)) {
-      dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName))
-    }
-  }
-
-  override fun stopSynchronize(syncable: SyncableStub) {
-    objectRepository.remove(syncable)
-  }
-
-  override fun emit(message: SignalProxyMessage) {
-    TODO("Not yet implemented")
-  }
-
-  override fun dispatch(message: SignalProxyMessage) {
-    when (message) {
-      is SignalProxyMessage.HeartBeat -> emit(SignalProxyMessage.HeartBeatReply(message.timestamp))
-      is SignalProxyMessage.HeartBeatReply -> heartBeatHandler.recomputeLatency(message.timestamp, force = true)
-      is SignalProxyMessage.InitData -> objectRepository.init(
-        objectRepository.find(message.className, message.objectName) ?: return,
-        message.initData
-      )
-      is SignalProxyMessage.InitRequest -> {
-        // Ignore incoming requests, we’re a client, we shouldn’t ever receive these
-      }
-      is SignalProxyMessage.Rpc -> {
-        val invoker = Invokers.get(ProtocolSide.CLIENT, "RpcHandler")
-          ?: throw RpcInvocationFailedException.InvokerNotFoundException("RpcHandler")
-        invoker.invoke(rpcHandler, message.slotName, message.params)
-      }
-      is SignalProxyMessage.Sync -> {
-        val invoker = Invokers.get(ProtocolSide.CLIENT, message.className)
-          ?: throw RpcInvocationFailedException.InvokerNotFoundException(message.className)
-        val syncable = objectRepository.find(message.className, message.objectName)
-          ?: throw RpcInvocationFailedException.SyncableNotFoundException(message.className, message.objectName)
-        invoker.invoke(syncable, message.slotName, message.params)
-      }
-    }
-  }
-
   @Suppress("NOTHING_TO_INLINE")
   inline fun state(): ClientSessionState = state.value
 
@@ -168,34 +141,4 @@ class ClientSession : Session {
       networkConfig = NetworkConfig(this)
     )
   )
-
-  class ClientRpcHandler(session: Session) : RpcHandler(session) {
-    override fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
-      val objectRepository = session?.objectRepository ?: return
-      val className = StringSerializerUtf8.deserializeRaw(classname)
-      val syncable = objectRepository.find(className, oldName ?: return)
-        ?: return
-      objectRepository.rename(syncable, newName ?: return)
-    }
-
-    override fun displayMsg(message: Message) {
-      messages.tryEmit(message)
-    }
-
-    override fun displayStatusMsg(net: String?, msg: String?) {
-      statusMessage.tryEmit(StatusMessage(net, msg ?: return))
-    }
-
-    @Suppress("NOTHING_TO_INLINE")
-    inline fun messages(): Flow<Message> = messages
-
-    @PublishedApi
-    internal val messages = MutableSharedFlow<Message>()
-
-    @Suppress("NOTHING_TO_INLINE")
-    inline fun statusMessage(): StateFlow<StatusMessage?> = statusMessage
-
-    @PublishedApi
-    internal val statusMessage = MutableStateFlow<StatusMessage?>(null)
-  }
 }
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
index f9ec357342ae4fa9e211528a06724b415b430343..62ca790d076a380fcd860ff9afa18f05c1efd2c2 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannel.kt
@@ -9,12 +9,12 @@
 
 package de.justjanne.libquassel.client.io
 
-import de.justjanne.libquassel.client.util.TlsInfo
 import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.util.update
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.runInterruptible
 import java.net.InetSocketAddress
 import java.net.Socket
@@ -26,8 +26,6 @@ class CoroutineChannel {
   private lateinit var channel: StreamChannel
   private val writeContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
   private val readContext = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
-  private val _tlsInfo = MutableStateFlow<TlsInfo?>(null)
-  val tlsInfo: StateFlow<TlsInfo?> get() = _tlsInfo
 
   suspend fun connect(
     address: InetSocketAddress,
@@ -38,25 +36,33 @@ class CoroutineChannel {
     socket.keepAlive = keepAlive
     socket.connect(address, timeout)
     this.channel = StreamChannel(socket)
+    state.update {
+      copy(connected = true)
+    }
   }
 
   fun enableCompression() {
     channel = channel.withCompression()
+    state.update {
+      copy(compression = true)
+    }
   }
 
   suspend fun enableTLS(context: SSLContext) {
     channel = runInterruptible(writeContext) {
       channel.withTLS(context)
     }
-    _tlsInfo.emit(channel.tlsInfo())
+    state.update {
+      copy(tlsInfo = channel.tlsInfo())
+    }
   }
 
   suspend fun read(buffer: ByteBuffer): Int = runInterruptible(readContext) {
-    this.channel.read(buffer)
+    channel.read(buffer)
   }
 
   suspend fun write(buffer: ByteBuffer): Int = runInterruptible(writeContext) {
-    this.channel.write(buffer)
+    channel.write(buffer)
   }
 
   suspend fun write(chainedBuffer: ChainedByteBuffer) {
@@ -66,6 +72,22 @@ class CoroutineChannel {
   }
 
   suspend fun flush() = runInterruptible(writeContext) {
-    this.channel.flush()
+    channel.flush()
   }
+
+  fun close() {
+    channel.close()
+    state.update {
+      copy(connected = false)
+    }
+  }
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun state(): CoroutineChannelState = state.value
+
+  @Suppress("NOTHING_TO_INLINE")
+  inline fun flow(): Flow<CoroutineChannelState> = state
+
+  @PublishedApi
+  internal val state = MutableStateFlow(CoroutineChannelState())
 }
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..6721f76a1a5714dc1c0ccf57b21ecc63004192c7
--- /dev/null
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/io/CoroutineChannelState.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.client.io
+
+import de.justjanne.libquassel.client.util.TlsInfo
+
+data class CoroutineChannelState(
+  val tlsInfo: TlsInfo? = null,
+  val compression: Boolean = false,
+  val connected: Boolean = false,
+)
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
index d66caabf9f2fc58bd5cdd6a96407163a967b9a9c..b989199e16092003d78ea98e266d1f7cd681aa4f 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientBacklogManager.kt
@@ -16,7 +16,7 @@ import de.justjanne.libquassel.protocol.models.flags.MessageType
 import de.justjanne.libquassel.protocol.models.flags.MessageTypes
 import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.MsgId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import kotlin.coroutines.Continuation
diff --git a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
index a46a04bebef2ac8ec8217b002cfb5cd00788e0dd..35c4b0da79dde50b9872502c615e26b1d874175d 100644
--- a/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
+++ b/libquassel-client/src/main/kotlin/de/justjanne/libquassel/client/syncables/ClientIrcListHelper.kt
@@ -12,7 +12,7 @@ package de.justjanne.libquassel.client.syncables
 import de.justjanne.libquassel.client.exceptions.IrcListException
 import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.into
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7a3d8041120f4e5bfb778cd382bfbeaf7d12ea68
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/CommonSyncProxy.kt
@@ -0,0 +1,66 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+class CommonSyncProxy(
+  private val protocolSide: ProtocolSide,
+  private val objectRepository: ObjectRepository,
+  private val proxyMessageHandler: ProxyMessageHandler
+) : SyncProxy {
+  override fun synchronize(syncable: SyncableStub) {
+    if (objectRepository.add(syncable)) {
+      proxyMessageHandler.dispatch(SignalProxyMessage.InitRequest(syncable.className, syncable.objectName))
+    }
+  }
+
+  override fun stopSynchronize(syncable: SyncableStub) {
+    objectRepository.remove(syncable)
+  }
+
+  override fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  ) {
+    if (target != protocolSide) {
+      proxyMessageHandler.emit(
+        SignalProxyMessage.Sync(
+          className,
+          objectName,
+          function,
+          arguments
+        )
+      )
+    }
+  }
+
+  override fun rpc(
+    target: ProtocolSide,
+    function: String,
+    arguments: QVariantList
+  ) {
+    if (target != protocolSide) {
+      proxyMessageHandler.emit(
+        SignalProxyMessage.Rpc(
+          function,
+          arguments
+        )
+      )
+    }
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..331f13868a8f6e6979dcdf72012c38836535649e
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/HandshakeMessageHandler.kt
@@ -0,0 +1,77 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+
+interface HandshakeMessageHandler {
+  /**
+   * Register client and start connection
+   */
+  suspend fun init(
+    /**
+     * Human readable (HTML formatted) version of the client
+     */
+    clientVersion: String,
+    /**
+     * Build timestamp of the client
+     */
+    buildDate: String,
+    /**
+     * Enabled client features for this connection
+     */
+    featureSet: FeatureSet
+  )
+
+  /**
+   * Login to core with authentication data
+   */
+  suspend fun login(
+    /**
+     * Username of the core account
+     */
+    username: String,
+    /**
+     * Password of the core account
+     */
+    password: String
+  )
+
+  /**
+   * Configure core for the first time
+   */
+  suspend fun configureCore(
+    /**
+     * Username of a new core account to be created
+     */
+    adminUsername: String,
+    /**
+     * Password of a new core account to be created
+     */
+    adminPassword: String,
+    /**
+     * Chosen storage backend id
+     */
+    backend: String,
+    /**
+     * Storage backend configuration data
+     */
+    backendConfiguration: QVariantMap,
+    /**
+     * Chosen authenticator backend id
+     */
+    authenticator: String,
+    /**
+     * Authenticator backend configuration data
+     */
+    authenticatorConfiguration: QVariantMap
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1264ec408b9afa7a2cc53ef9dae920db98f88971
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/ProxyMessageHandler.kt
@@ -0,0 +1,17 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+
+interface ProxyMessageHandler {
+  fun emit(message: SignalProxyMessage)
+  fun dispatch(message: SignalProxyMessage)
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
similarity index 65%
rename from libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt
rename to libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
index 6a13f0d25d8026efd410afacd2bb8e7b1dd98e53..d04f5de184d72830b29f4515f307a72fe27d3c65 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/Session.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/Session.kt
@@ -7,12 +7,12 @@
  * obtain one at https://mozilla.org/MPL/2.0/.
  */
 
-package de.justjanne.libquassel.protocol.syncables
+package de.justjanne.libquassel.protocol.session
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.syncables.ObjectRepository
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -25,14 +25,12 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
-import de.justjanne.libquassel.protocol.variant.QVariantList
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 
 interface Session {
-  val protocolSide: ProtocolSide
+  val side: ProtocolSide
+  val proxy: SyncProxy
   val objectRepository: ObjectRepository
-  val rpcHandler: RpcHandler
 
   fun network(id: NetworkId): Network?
   fun addNetwork(id: NetworkId)
@@ -42,6 +40,8 @@ interface Session {
   fun addIdentity(properties: QVariantMap)
   fun removeIdentity(id: IdentityId)
 
+  fun rename(className: String, oldName: String, newName: String)
+
   val aliasManager: AliasManager
   val backlogManager: BacklogManager
   val bufferSyncer: BufferSyncer
@@ -53,44 +53,4 @@ interface Session {
   val coreInfo: CoreInfo
   val dccConfig: DccConfig
   val networkConfig: NetworkConfig
-
-  fun synchronize(syncable: SyncableStub)
-  fun stopSynchronize(syncable: SyncableStub)
-
-  fun sync(
-    target: ProtocolSide,
-    className: String,
-    objectName: String,
-    function: String,
-    arguments: QVariantList
-  ) {
-    if (target != protocolSide) {
-      emit(
-        SignalProxyMessage.Sync(
-          className,
-          objectName,
-          function,
-          arguments
-        )
-      )
-    }
-  }
-
-  fun rpc(
-    target: ProtocolSide,
-    function: String,
-    arguments: QVariantList
-  ) {
-    if (target != protocolSide) {
-      emit(
-        SignalProxyMessage.Rpc(
-          function,
-          arguments
-        )
-      )
-    }
-  }
-
-  fun emit(message: SignalProxyMessage)
-  fun dispatch(message: SignalProxyMessage)
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..7d8db67aa753802a2e152ea066885b593b053d91
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/session/SyncProxy.kt
@@ -0,0 +1,34 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.session
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+interface SyncProxy {
+  fun synchronize(syncable: SyncableStub)
+
+  fun stopSynchronize(syncable: SyncableStub)
+
+  fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  )
+
+  fun rpc(
+    target: ProtocolSide,
+    function: String,
+    arguments: QVariantList
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
index f30a16390f52e39921d36d5317b0397ddf26f1d3..0b1067121421a8956a50bca35578e0ebf30ecaee 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/StatefulSyncableObject.kt
@@ -9,6 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables
 
+import de.justjanne.libquassel.protocol.session.Session
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
index 12549e5146cc845933c01f88c34545038902b409..404e6beb4adffa66e59b7f94f4310912c6f25805 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableObject.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables
 
-import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.session.Session
 
 abstract class SyncableObject(
   override var session: Session?,
@@ -28,11 +28,7 @@ abstract class SyncableObject(
     } else if (oldName != newName) {
       objectName = newName
       session?.objectRepository?.rename(this, newName)
-      session?.rpcHandler?.objectRenamed(
-        StringSerializerUtf8.serializeRaw(className),
-        oldName,
-        newName
-      )
+      session?.rename(className, oldName, newName)
     }
   }
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
index 20b6b59aacb9d5dfaf8f887ca38be4880b18928c..0398f2b041a41447a36935696ac3f8ad8ecb18ef 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/SyncableStub.kt
@@ -10,6 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables
 
 import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.variant.QVariant_
 
 interface SyncableStub {
@@ -20,13 +21,13 @@ interface SyncableStub {
 
   fun sync(target: ProtocolSide, function: String, vararg arg: QVariant_) {
     if (initialized) {
-      session?.sync(target, className, objectName, function, arg.toList())
+      session?.proxy?.sync(target, className, objectName, function, arg.toList())
     }
   }
 
   fun rpc(target: ProtocolSide, function: String, vararg arg: QVariant_) {
     if (initialized) {
-      session?.rpc(target, function, arg.toList())
+      session?.proxy?.rpc(target, function, arg.toList())
     }
   }
 }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
index 2a91ad354275e950e7503decb4bb7490fea23291..e09328ebc6c789d9f22b555efa3a05021de93ed0 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/AliasManager.kt
@@ -14,7 +14,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.alias.Alias
 import de.justjanne.libquassel.protocol.models.alias.Command
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.AliasManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.AliasManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
index 96bca3d14806fa79df363e0a9c1e6be5df5c5a17..706d6bfb54a6d34416ea0f599f08e31c6482d16b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BacklogManager.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables.common
 
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.BacklogManagerStub
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
index 03d1c17a396a92c298a070619ed6ae0402b39d37..52b62f4d8d3d9266ce11a72ed9f4bfbdce3b2483 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferSyncer.kt
@@ -20,7 +20,7 @@ import de.justjanne.libquassel.protocol.models.ids.MsgId
 import de.justjanne.libquassel.protocol.models.ids.isValid
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferSyncerState
 import de.justjanne.libquassel.protocol.syncables.stubs.BufferSyncerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
index bc35bae2c2845dd4baedf241171e462a1a1e7724..a0498d99adf45825073eef8d2afbcf265e7accf4 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewConfig.kt
@@ -18,7 +18,7 @@ import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.BufferViewConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
index 4529cd9cec0e3f5530c7eec779a73bc3c27ad0d2..ce5afde76cd9fccb4aa2e5a1db23416197cd7ee3 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/BufferViewManager.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.BufferInfo
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewConfigState
 import de.justjanne.libquassel.protocol.syncables.state.BufferViewManagerState
@@ -59,7 +59,7 @@ open class BufferViewManager(
         bufferViewId = bufferViewConfigId
       )
     )
-    session?.synchronize(config)
+    session?.proxy?.synchronize(config)
     state.update {
       copy(bufferViewConfigs = bufferViewConfigs + Pair(bufferViewConfigId, config))
     }
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
index df15b194374f9b097042a9b95ed85712e3c11099..c52bdc5616aeb919b2727879a152099a082c2279 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CertManager.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.CertManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.CertManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
index 69da65c4f8245238a10ab36279abbce293ade426..68226dec04bfcb33a0a0fee6165a8c296f4deb98 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/CoreInfo.kt
@@ -11,7 +11,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.ConnectedClient
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.CoreInfoState
 import de.justjanne.libquassel.protocol.syncables.stubs.CoreInfoStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
index 49c553f1b4c82253b7ae31f2df562103e39ad363..0a9d9565e368f72657deedea7b4aaf3002c181fd 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/DccConfig.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.dcc.DccIpDetectionMode
 import de.justjanne.libquassel.protocol.models.dcc.DccPortSelectionMode
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.DccConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.DccConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
index 35cecdd867657bf12a212dd99fd5ea5a5d83c429..6e3c1cbfe846505583ee14a35cef383efa045b0b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/HighlightRuleManager.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.rules.HighlightNickType
 import de.justjanne.libquassel.protocol.models.rules.HighlightRule
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.HighlightRuleManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.HighlightRuleManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
index 922929205dc4146cfc461c7eb33a604dcd253cf4..abc31353bd2315e9c33a6a49096ec77720356e6b 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Identity.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IdentityState
 import de.justjanne.libquassel.protocol.syncables.stubs.IdentityStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
index c4708181e55533c3d8609436e9b56cc69691dd54..3be891d45195d4d0a5e34cbf0dc6b4a798b825e7 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IgnoreListManager.kt
@@ -16,7 +16,7 @@ import de.justjanne.libquassel.protocol.models.rules.IgnoreType
 import de.justjanne.libquassel.protocol.models.rules.ScopeType
 import de.justjanne.libquassel.protocol.models.rules.StrictnessType
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IgnoreListManagerState
 import de.justjanne.libquassel.protocol.syncables.stubs.IgnoreListManagerStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
index e27c6dac33daf3c795664c3adf43c28dc24b8a06..6fc9c0edf2896b16fdf56ff71ee1ec620a8b5dc3 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcChannel.kt
@@ -13,7 +13,7 @@ import de.justjanne.libquassel.protocol.models.QStringList
 import de.justjanne.libquassel.protocol.models.network.ChannelModeType
 import de.justjanne.libquassel.protocol.models.network.ChannelModes
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcChannelState
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcChannelStub
@@ -157,7 +157,7 @@ open class IrcChannel(
           copy(channelModes = ChannelModes())
         }
         network.removeIrcChannel(this)
-        session?.stopSynchronize(this)
+        session?.proxy?.stopSynchronize(this)
       }
     }
     super.part(nick)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
index 768f620e5ac523a1de923a893605f9496b94531f..72fc2638bafbee7b1c13668a95ccd62f4ea16449 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcListHelper.kt
@@ -9,7 +9,7 @@
 
 package de.justjanne.libquassel.protocol.syncables.common
 
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcListHelperStub
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
index ea4abb820083808fe955f455625b417fa4559096..7cf374b58b5ac5eb3caab0df70e67fa0d434c595 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/IrcUser.kt
@@ -10,7 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcUserState
 import de.justjanne.libquassel.protocol.syncables.stubs.IrcUserStub
@@ -273,7 +273,7 @@ open class IrcUser(
       copy(channels = emptySet())
     }
     network?.removeIrcUser(this)
-    session?.stopSynchronize(this)
+    session?.proxy?.stopSynchronize(this)
     super.quit()
   }
 
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
index 84fdab8428d73fc6b98dc5aea9902a539ae636ed..703337e510f1c332a357ca6fa484f257e16eb77d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/Network.kt
@@ -17,7 +17,7 @@ import de.justjanne.libquassel.protocol.models.network.NetworkServer
 import de.justjanne.libquassel.protocol.models.types.QtType
 import de.justjanne.libquassel.protocol.models.types.QuasselType
 import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.IrcChannelState
 import de.justjanne.libquassel.protocol.syncables.state.IrcUserState
@@ -332,7 +332,7 @@ open class Network(
       user.fromVariantMap(properties, index)
       user.initialized = true
     }
-    session?.synchronize(user)
+    session?.proxy?.synchronize(user)
     state.update {
       copy(ircUsers = ircUsers + Pair(caseMapper().toLowerCase(nick), user))
     }
@@ -360,7 +360,7 @@ open class Network(
       channel.fromVariantMap(properties, index)
       channel.initialized = true
     }
-    session?.synchronize(channel)
+    session?.proxy?.synchronize(channel)
     state.update {
       copy(ircChannels = ircChannels + Pair(caseMapper().toLowerCase(name), channel))
     }
@@ -498,8 +498,8 @@ open class Network(
         copy(connected = true)
       } else {
         session?.let {
-          ircChannels.values.forEach(it::stopSynchronize)
-          ircUsers.values.forEach(it::stopSynchronize)
+          ircChannels.values.forEach(it.proxy::stopSynchronize)
+          ircUsers.values.forEach(it.proxy::stopSynchronize)
         }
         copy(
           connected = isConnected,
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
index 9c7eab876f354e4d53ce4c0601436cc16e5f1862..e6542b5e8719d4f3873241b1149cae79de002858 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/NetworkConfig.kt
@@ -10,7 +10,7 @@
 package de.justjanne.libquassel.protocol.syncables.common
 
 import de.justjanne.libquassel.protocol.models.types.QtType
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.StatefulSyncableObject
 import de.justjanne.libquassel.protocol.syncables.state.NetworkConfigState
 import de.justjanne.libquassel.protocol.syncables.stubs.NetworkConfigStub
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
index b3220279d0464909c1971d59ca72df40d561d68c..1de31f4d815ee8a19c008a59edb717c8dd53b61d 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/common/RpcHandler.kt
@@ -12,7 +12,7 @@ package de.justjanne.libquassel.protocol.syncables.common
 import de.justjanne.libquassel.protocol.models.BufferInfo
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
-import de.justjanne.libquassel.protocol.syncables.Session
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.SyncableObject
 import de.justjanne.libquassel.protocol.syncables.stubs.RpcHandlerStub
 import de.justjanne.libquassel.protocol.variant.QVariantMap
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
index 376bb0a7d68604a39f35e6207f04e685d101cec0..518df1c12328342ffef73d01d4b67acd7f2b77c1 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/AliasManagerTest.kt
@@ -18,6 +18,7 @@ import de.justjanne.libquassel.protocol.models.flags.BufferType
 import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
 import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.state.AliasManagerState
 import de.justjanne.libquassel.protocol.testutil.mocks.EmptySession
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
index 97079bed5dc5330eb3e07ec59abab133a4c8842d..5a6790313dfc6390ea3cfbfd4102fa0ce4040ac0 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/syncables/NetworkTest.kt
@@ -20,6 +20,7 @@ import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.state.NetworkState
 import de.justjanne.libquassel.protocol.testutil.mocks.EmptySession
+import de.justjanne.libquassel.protocol.testutil.mocks.EmptySyncProxy
 import de.justjanne.libquassel.protocol.testutil.nextNetwork
 import de.justjanne.libquassel.protocol.testutil.nextString
 import de.justjanne.libquassel.protocol.variant.qVariant
@@ -918,8 +919,10 @@ class NetworkTest {
   class NetworkMockSession : EmptySession() {
     val synchronizeCalls = mutableListOf<SyncableStub>()
 
-    override fun synchronize(syncable: SyncableStub) {
-      synchronizeCalls.add(syncable)
+    override val proxy = object : EmptySyncProxy() {
+      override fun synchronize(syncable: SyncableStub) {
+        synchronizeCalls.add(syncable)
+      }
     }
   }
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9df669994c098708b244581a4c0164e119157777
--- /dev/null
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptyProxyMessageHandler.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.testutil.mocks
+
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.session.ProxyMessageHandler
+
+open class EmptyProxyMessageHandler : ProxyMessageHandler {
+  override fun emit(message: SignalProxyMessage) = Unit
+  override fun dispatch(message: SignalProxyMessage) = Unit
+}
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
index 50ff2ca631519dc468cc162d649eb5793f26ef05..13a7edf8ebe23889922310d62926c3f587ec56e7 100644
--- a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySession.kt
@@ -10,12 +10,10 @@
 package de.justjanne.libquassel.protocol.testutil.mocks
 
 import de.justjanne.libquassel.annotations.ProtocolSide
-import de.justjanne.libquassel.protocol.models.SignalProxyMessage
 import de.justjanne.libquassel.protocol.models.ids.IdentityId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
+import de.justjanne.libquassel.protocol.session.Session
 import de.justjanne.libquassel.protocol.syncables.ObjectRepository
-import de.justjanne.libquassel.protocol.syncables.Session
-import de.justjanne.libquassel.protocol.syncables.SyncableStub
 import de.justjanne.libquassel.protocol.syncables.common.AliasManager
 import de.justjanne.libquassel.protocol.syncables.common.BacklogManager
 import de.justjanne.libquassel.protocol.syncables.common.BufferSyncer
@@ -28,13 +26,12 @@ import de.justjanne.libquassel.protocol.syncables.common.IgnoreListManager
 import de.justjanne.libquassel.protocol.syncables.common.IrcListHelper
 import de.justjanne.libquassel.protocol.syncables.common.Network
 import de.justjanne.libquassel.protocol.syncables.common.NetworkConfig
-import de.justjanne.libquassel.protocol.syncables.common.RpcHandler
 import de.justjanne.libquassel.protocol.variant.QVariantMap
 
 open class EmptySession : Session {
-  override val protocolSide = ProtocolSide.CLIENT
-  override val rpcHandler = RpcHandler(this)
-  override val objectRepository = ObjectRepository()
+  final override val side = ProtocolSide.CLIENT
+  final override val objectRepository = ObjectRepository()
+  override val proxy = EmptySyncProxy()
 
   override fun network(id: NetworkId): Network? = null
   override fun addNetwork(id: NetworkId) = Unit
@@ -42,6 +39,7 @@ open class EmptySession : Session {
   override fun identity(id: IdentityId): Identity? = null
   override fun addIdentity(properties: QVariantMap) = Unit
   override fun removeIdentity(id: IdentityId) = Unit
+  override fun rename(className: String, oldName: String, newName: String) = Unit
 
   override val aliasManager = AliasManager(this)
   override val bufferSyncer = BufferSyncer(this)
@@ -53,9 +51,4 @@ open class EmptySession : Session {
   override val coreInfo = CoreInfo(this)
   override val dccConfig = DccConfig(this)
   override val networkConfig = NetworkConfig(this)
-
-  override fun synchronize(syncable: SyncableStub) = Unit
-  override fun stopSynchronize(syncable: SyncableStub) = Unit
-  override fun emit(message: SignalProxyMessage) = Unit
-  override fun dispatch(message: SignalProxyMessage) = Unit
 }
diff --git a/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..06d8ffd4813aa3a03347e4392c7fd10e4eaeff46
--- /dev/null
+++ b/libquassel-protocol/src/test/kotlin/de/justjanne/libquassel/protocol/testutil/mocks/EmptySyncProxy.kt
@@ -0,0 +1,30 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public License,
+ * v. 2.0. If a copy of the MPL was not distributed with this file, You can
+ * obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package de.justjanne.libquassel.protocol.testutil.mocks
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.session.SyncProxy
+import de.justjanne.libquassel.protocol.syncables.SyncableStub
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+open class EmptySyncProxy : SyncProxy {
+  override fun synchronize(syncable: SyncableStub) = Unit
+  override fun stopSynchronize(syncable: SyncableStub) = Unit
+
+  override fun sync(
+    target: ProtocolSide,
+    className: String,
+    objectName: String,
+    function: String,
+    arguments: QVariantList
+  ) = Unit
+
+  override fun rpc(target: ProtocolSide, function: String, arguments: QVariantList) = Unit
+}