diff --git a/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e1c5c032a68a3325f18334b09e3fbf2d29db8f29
--- /dev/null
+++ b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/ProtocolSide.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.annotations
+
+enum class ProtocolSide {
+  CLIENT,
+  CORE
+}
diff --git a/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt
new file mode 100644
index 0000000000000000000000000000000000000000..28ae775b258fb167a7ade3b1f5abd195c2bf6047
--- /dev/null
+++ b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedCall.kt
@@ -0,0 +1,17 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.annotations
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class SyncedCall(
+  val name: String = "",
+  val target: ProtocolSide
+)
diff --git a/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedData.kt b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5547f9894a4476fff10b9a1d9050f49d8134a33a
--- /dev/null
+++ b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedData.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.annotations
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class SyncedData(
+  val name: String
+)
diff --git a/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a01b13a896feebedfc867f589f08a9a634c6b223
--- /dev/null
+++ b/libquassel-annotations/src/main/kotlin/de/justjanne/libquassel/annotations/SyncedObject.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.annotations
+
+@Retention(AnnotationRetention.SOURCE)
+annotation class SyncedObject(
+  val name: String
+)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Alias.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Alias.kt
new file mode 100644
index 0000000000000000000000000000000000000000..12b18d8ea2826e5450c5084c919eb49083481d77
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Alias.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+data class Alias(
+  val name: String?,
+  val expansion: String?
+)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Command.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Command.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a33196e567088ce075db756bd938f153849642ef
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/Command.kt
@@ -0,0 +1,16 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+data class Command(
+  val buffer: BufferInfo,
+  val message: String
+)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HighlightNickType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HighlightNickType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4d6d996c1f21e7c40687f02902f1135bb0657186
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/HighlightNickType.kt
@@ -0,0 +1,29 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+enum class HighlightNickType(
+  val value: UByte,
+) {
+  NoNick(0x00u),
+  CurrentNick(0x01u),
+  AllNicks(0x02u);
+
+  companion object {
+    private val values = enumValues<HighlightNickType>()
+      .associateBy(HighlightNickType::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: UByte): HighlightNickType? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkInfo.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkInfo.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ce580bb522fe3a9a42c67b401ed877533002dc41
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkInfo.kt
@@ -0,0 +1,42 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+import de.justjanne.libquassel.protocol.models.ids.NetworkId
+
+data class NetworkInfo(
+  var networkId: NetworkId = NetworkId(-1),
+  var networkName: String = "",
+  var identity: IdentityId = IdentityId(-1),
+  var useCustomEncodings: Boolean = false,
+  var codecForServer: String = "UTF_8",
+  var codecForEncoding: String = "UTF_8",
+  var codecForDecoding: String = "UTF_8",
+  var serverList: List<NetworkServer> = emptyList(),
+  var useRandomServer: Boolean = false,
+  var perform: List<String> = emptyList(),
+  var useAutoIdentify: Boolean = false,
+  var autoIdentifyService: String = "",
+  var autoIdentifyPassword: String = "",
+  var useSasl: Boolean = false,
+  var saslAccount: String = "",
+  var saslPassword: String = "",
+  var useAutoReconnect: Boolean = true,
+  var autoReconnectInterval: UInt = 0u,
+  var autoReconnectRetries: UShort = 0u,
+  var unlimitedReconnectRetries: Boolean = true,
+  var rejoinChannels: Boolean = true,
+  var useCustomMessageRate: Boolean = false,
+  var messageRateBurstSize: UInt = 0u,
+  var messageRateDelay: UInt = 0u,
+  var unlimitedMessageRate: Boolean = false
+)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkProxy.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkProxy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bafa63fa4d2928d56cdf0345c02a83197b2d273e
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkProxy.kt
@@ -0,0 +1,32 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+enum class NetworkProxy(
+  val value: Int,
+) {
+  DefaultProxy(0),
+  Socks5Proxy(1),
+  NoProxy(2),
+  HttpProxy(3),
+  HttpCachingProxy(4),
+  FtpCachingProxy(5);
+
+  companion object {
+    private val values = enumValues<NetworkProxy>()
+      .associateBy(NetworkProxy::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): NetworkProxy? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkServer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkServer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..953190b41613cc949427a8c6acf7aa510cf22cae
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/NetworkServer.kt
@@ -0,0 +1,26 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+data class NetworkServer(
+  val host: String,
+  val port: UInt = 6667u,
+  val password: String = "",
+  val useSsl: Boolean = false,
+  val sslVerify: Boolean = true,
+  val sslVersion: Int = 0,
+  val useProxy: Boolean = false,
+  val proxyType: NetworkProxy = NetworkProxy.Socks5Proxy,
+  val proxyHost: String = "localhost",
+  val proxyPort: UInt = 8080u,
+  val proxyUser: String = "",
+  val proxyPass: String = "",
+)
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/PortDefaults.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/PortDefaults.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c7279a2523e8ca900206495a4e4f339287bcea0c
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/PortDefaults.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+enum class PortDefaults(
+  val port: UInt
+) {
+  PORT_PLAINTEXT(6667u),
+  PORT_SSL(6697u)
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferDirection.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferDirection.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8f28f6e1c6fe9a2704116d7d865a28b62aedccb4
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferDirection.kt
@@ -0,0 +1,28 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+enum class TransferDirection(
+  val value: Int,
+) {
+  Send(0),
+  Receive(1);
+
+  companion object {
+    private val values = enumValues<TransferDirection>()
+      .associateBy(TransferDirection::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): TransferDirection? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferIdList.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferIdList.kt
new file mode 100644
index 0000000000000000000000000000000000000000..df0d64c6094a2aea3a514b6273bf6b431c5ac294
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferIdList.kt
@@ -0,0 +1,18 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+import java.util.UUID
+
+/**
+ * Simple alias for a generic TransferIdList type
+ */
+typealias TransferIdList = List<UUID>
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferStatus.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferStatus.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5d90f13a345119f839a5471e78ca6af5fcf59f6c
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/TransferStatus.kt
@@ -0,0 +1,34 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.models
+
+enum class TransferStatus(
+  val value: Int,
+) {
+  New(0),
+  Pending(1),
+  Connecting(2),
+  Transferring(3),
+  Paused(4),
+  Completed(5),
+  Failed(6),
+  Rejected(7);
+
+  companion object {
+    private val values = enumValues<TransferStatus>()
+      .associateBy(TransferStatus::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): TransferStatus? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
index 4624c173732244879604b9c42eb2f24781ca3c5f..9e60bad41b4568f4a1492c99d438b3b0f97c5573 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QtType.kt
@@ -33,6 +33,7 @@ import de.justjanne.libquassel.protocol.serializers.qt.UByteSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UIntSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.ULongSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.UShortSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.UuidSerializer
 import de.justjanne.libquassel.protocol.serializers.qt.VoidSerializer
 
 /**
@@ -120,6 +121,12 @@ enum class QtType(
    */
   Double(6, DoubleSerializer),
 
+  /**
+   * 128-bit UUID
+   * See [java.util.UUID]
+   */
+  Uuid(30, UuidSerializer),
+
   /**
    * Date in the gregorian calender as julian date
    * See [org.threeten.bp.LocalDate]
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
index 1664d088309ede3d2b1eef9362b11fddad4b6703..b91ed389bd55afbff94bf08c8e9394a75e19c5ae 100644
--- a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/types/QuasselType.kt
@@ -20,8 +20,13 @@ import de.justjanne.libquassel.protocol.serializers.quassel.IdentityIdSerializer
 import de.justjanne.libquassel.protocol.serializers.quassel.MessageSerializer
 import de.justjanne.libquassel.protocol.serializers.quassel.MsgIdSerializer
 import de.justjanne.libquassel.protocol.serializers.quassel.NetworkIdSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.NetworkInfoSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.NetworkServerSerializer
 import de.justjanne.libquassel.protocol.serializers.quassel.PeerPtrSerializer
 import de.justjanne.libquassel.protocol.serializers.quassel.QHostAddressSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.TransferDirectionSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.TransferIdListSerializer
+import de.justjanne.libquassel.protocol.serializers.quassel.TransferStatusSerializer
 
 /**
  * Supported quassel types for serialization
@@ -42,80 +47,95 @@ enum class QuasselType(
   val qtType: QtType = QtType.UserType
 ) {
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.BufferId]
+   * Type for [de.justjanne.libquassel.protocol.models.BufferId]
    */
   BufferId("BufferId", BufferIdSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.BufferInfo]
+   * Type for [de.justjanne.libquassel.protocol.models.BufferInfo]
    */
   BufferInfo("BufferInfo", BufferInfoSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.DccIpDetectionMode]
+   * Type for [de.justjanne.libquassel.protocol.models.DccIpDetectionMode]
    */
   DccConfigIpDetectionMode("DccConfig::IpDetectionMode", DccIpDetectionModeSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.DccPortSelectionMode]
+   * Type for [de.justjanne.libquassel.protocol.models.DccPortSelectionMode]
    */
   DccConfigPortSelectionMode("DccConfig::PortSelectionMode", DccPortSelectionModeSerializer),
 
   /**
    * Type for IrcUser objects
-   * Serialized as [QVariantMap]
+   * Serialized as [de.justjanne.libquassel.protocol.variant.QVariantMap]
    */
   IrcUser("IrcUser"),
 
   /**
    * Type for IrcChannel objects
-   * Serialized as [QVariantMap]
+   * Serialized as [de.justjanne.libquassel.protocol.variant.QVariantMap]
    */
   IrcChannel("IrcChannel"),
 
   /**
    * Type for Identity objects
-   * Serialized as [QVariantMap]
+   * Serialized as [de.justjanne.libquassel.protocol.variant.QVariantMap]
    */
   Identity("Identity"),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.IdentityId]
+   * Type for [de.justjanne.libquassel.protocol.models.IdentityId]
    */
   IdentityId("IdentityId", IdentityIdSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.Message]
+   * Type for [de.justjanne.libquassel.protocol.models.Message]
    */
   Message("Message", MessageSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.MsgId]
+   * Type for [de.justjanne.libquassel.protocol.models.MsgId]
    */
   MsgId("MsgId", MsgIdSerializer),
 
   /**
-   * Type for [de.justjanne.libquassel.protocol.types.NetworkId]
+   * Type for [de.justjanne.libquassel.protocol.models.NetworkId]
    */
   NetworkId("NetworkId", NetworkIdSerializer),
 
   /**
    * Type for NetworkInfo objects
-   * Serialized as [QVariantMap]
+   * Serialized as [de.justjanne.libquassel.protocol.variant.QVariantMap]
    */
-  NetworkInfo("NetworkInfo"),
+  NetworkInfo("NetworkInfo", NetworkInfoSerializer),
 
   /**
    * Type for NetworkServer objects
-   * Serialized as [QVariantMap]
+   * Serialized as [de.justjanne.libquassel.protocol.variant.QVariantMap]
    */
-  NetworkServer("Network::Server"),
+  NetworkServer("Network::Server", NetworkServerSerializer),
 
   /**
    * Type for [java.net.InetAddress]
    */
   QHostAddress("QHostAddress", QHostAddressSerializer),
 
+  /**
+   * Type for [de.justjanne.libquassel.protocol.models.TransferDirection]
+   */
+  TransferDirection("Transfer::Direction", TransferDirectionSerializer),
+
+  /**
+   * Type for [de.justjanne.libquassel.protocol.models.TransferIdList]
+   */
+  TransferIdList("Transfer::TransferIdList", TransferIdListSerializer),
+
+  /**
+   * Type for [de.justjanne.libquassel.protocol.models.TransferStatus]
+   */
+  TransferStatus("Transfer::Status", TransferStatusSerializer),
+
   /**
    * Serialization type for PeerPtr.
    *
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UuidSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UuidSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d127d69067ea00b42f4bb866a3fd0da8a6f62da4
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/qt/UuidSerializer.kt
@@ -0,0 +1,34 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.qt
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import java.nio.ByteBuffer
+import java.util.UUID
+
+/**
+ * Serializer for [UUID]
+ */
+object UuidSerializer : PrimitiveSerializer<UUID> {
+  override val javaType: Class<UUID> = UUID::class.java
+
+  override fun serialize(buffer: ChainedByteBuffer, data: UUID, featureSet: FeatureSet) {
+    LongSerializer.serialize(buffer, data.mostSignificantBits, featureSet)
+    LongSerializer.serialize(buffer, data.leastSignificantBits, featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = UUID(
+    LongSerializer.deserialize(buffer, featureSet),
+    LongSerializer.deserialize(buffer, featureSet)
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkInfoSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkInfoSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ebcb290811e9ab65e9a2602941e47dda36f0e79c
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkInfoSerializer.kt
@@ -0,0 +1,95 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.quassel
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.NetworkInfo
+import de.justjanne.libquassel.protocol.models.NetworkServer
+import de.justjanne.libquassel.protocol.models.QStringList
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+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.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.StringSerializerUtf8
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.into
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.nio.ByteBuffer
+
+object NetworkInfoSerializer : PrimitiveSerializer<NetworkInfo> {
+  override val javaType: Class<NetworkInfo> = NetworkInfo::class.java
+  override fun serialize(buffer: ChainedByteBuffer, data: NetworkInfo, featureSet: FeatureSet) {
+    QVariantMapSerializer.serialize(buffer, serializeMap(data), featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): NetworkInfo {
+    return deserializeMap(QVariantMapSerializer.deserialize(buffer, featureSet))
+  }
+
+  fun serializeMap(data: NetworkInfo): QVariantMap = mapOf(
+    "NetworkId" to qVariant(data.networkId, QuasselType.NetworkId),
+    "NetworkName" to qVariant(data.networkName, QtType.QString),
+    "Identity" to qVariant(data.identity, QuasselType.IdentityId),
+    "UseCustomEncodings" to qVariant(data.useCustomEncodings, QtType.Bool),
+    "CodecForServer" to qVariant(StringSerializerUtf8.serializeRaw(data.codecForServer), QtType.QByteArray),
+    "CodecForEncoding" to qVariant(StringSerializerUtf8.serializeRaw(data.codecForEncoding), QtType.QByteArray),
+    "CodecForDecoding" to qVariant(StringSerializerUtf8.serializeRaw(data.codecForDecoding), QtType.QByteArray),
+    "ServerList" to qVariant(data.serverList, QtType.QVariantList),
+    "UseRandomServer" to qVariant(data.useRandomServer, QtType.Bool),
+    "Perform" to qVariant(data.perform, QtType.QStringList),
+    "UseAutoIdentify" to qVariant(data.useAutoIdentify, QtType.Bool),
+    "AutoIdentifyService" to qVariant(data.autoIdentifyService, QtType.QString),
+    "AutoIdentifyPassword" to qVariant(data.autoIdentifyPassword, QtType.QString),
+    "UseSasl" to qVariant(data.useSasl, QtType.Bool),
+    "SaslAccount" to qVariant(data.saslAccount, QtType.QString),
+    "SaslPassword" to qVariant(data.saslPassword, QtType.QString),
+    "UseAutoReconnect" to qVariant(data.useAutoReconnect, QtType.Bool),
+    "AutoReconnectInterval" to qVariant(data.autoReconnectInterval, QtType.UInt),
+    "AutoReconnectRetries" to qVariant(data.autoReconnectRetries, QtType.UShort),
+    "UnlimitedReconnectRetries" to qVariant(data.unlimitedReconnectRetries, QtType.Bool),
+    "RejoinChannels" to qVariant(data.rejoinChannels, QtType.Bool),
+    "UseCustomMessageRate" to qVariant(data.useCustomMessageRate, QtType.Bool),
+    "MessageRateBurstSize" to qVariant(data.messageRateBurstSize, QtType.UInt),
+    "MessageRateDelay" to qVariant(data.messageRateDelay, QtType.UInt),
+    "UnlimitedMessageRate" to qVariant(data.unlimitedMessageRate, QtType.Bool)
+  )
+
+  fun deserializeMap(data: QVariantMap) = NetworkInfo(
+    networkId = data["NetworkId"].into(NetworkId(-1)),
+    networkName = data["NetworkName"].into(""),
+    identity = data["Identity"].into(IdentityId(-1)),
+    useCustomEncodings = data["UseCustomEncodings"].into(false),
+    codecForServer = data["CodecForServer"].into("UTF_8"),
+    codecForEncoding = data["CodecForEncoding"].into("UTF_8"),
+    codecForDecoding = data["CodecForDecoding"].into("UTF_8"),
+    serverList = data["ServerList"].into<List<NetworkServer>>().orEmpty(),
+    useRandomServer = data["UseRandomServer"].into(false),
+    perform = data["Perform"].into<QStringList>().orEmpty().filterNotNull(),
+    useAutoIdentify = data["UseAutoIdentify"].into(false),
+    autoIdentifyService = data["AutoIdentifyService"].into(""),
+    autoIdentifyPassword = data["AutoIdentifyPassword"].into(""),
+    useSasl = data["UseSasl"].into(false),
+    saslAccount = data["SaslAccount"].into(""),
+    saslPassword = data["SaslPassword"].into(""),
+    useAutoReconnect = data["UseAutoReconnect"].into(true),
+    autoReconnectInterval = data["AutoReconnectInterval"].into(0u),
+    autoReconnectRetries = data["AutoReconnectRetries"].into(0u),
+    unlimitedReconnectRetries = data["UnlimitedReconnectRetries"].into(true),
+    rejoinChannels = data["RejoinChannels"].into(true),
+    useCustomMessageRate = data["UseCustomMessageRate"].into(false),
+    messageRateBurstSize = data["MessageRateBurstSize"].into(0u),
+    messageRateDelay = data["MessageRateDelay"].into(0u),
+    unlimitedMessageRate = data["UnlimitedMessageRate"].into(false)
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkServerSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkServerSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4b1beac348b50c5fbbf9ac35908e12b7376bbeb4
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/NetworkServerSerializer.kt
@@ -0,0 +1,66 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.quassel
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.NetworkProxy
+import de.justjanne.libquassel.protocol.models.NetworkServer
+import de.justjanne.libquassel.protocol.models.PortDefaults
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.QVariantMapSerializer
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.into
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.nio.ByteBuffer
+
+object NetworkServerSerializer : PrimitiveSerializer<NetworkServer> {
+  override val javaType: Class<NetworkServer> = NetworkServer::class.java
+  override fun serialize(buffer: ChainedByteBuffer, data: NetworkServer, featureSet: FeatureSet) {
+    QVariantMapSerializer.serialize(buffer, serializeMap(data), featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): NetworkServer {
+    return deserializeMap(QVariantMapSerializer.deserialize(buffer, featureSet))
+  }
+
+  fun serializeMap(data: NetworkServer): QVariantMap = mapOf(
+    "Host" to qVariant(data.host, QtType.QString),
+    "Port" to qVariant(data.port, QtType.UInt),
+    "Password" to qVariant(data.password, QtType.QString),
+    "UseSSL" to qVariant(data.useSsl, QtType.Bool),
+    "sslVerify" to qVariant(data.sslVerify, QtType.Bool),
+    "sslVersion" to qVariant(data.sslVersion, QtType.Int),
+    "UseProxy" to qVariant(data.useProxy, QtType.Bool),
+    "ProxyType" to qVariant(data.proxyType, QtType.Int),
+    "ProxyHost" to qVariant(data.proxyHost, QtType.QString),
+    "ProxyPort" to qVariant(data.proxyPort, QtType.UInt),
+    "ProxyUser" to qVariant(data.proxyUser, QtType.QString),
+    "ProxyPass" to qVariant(data.proxyPass, QtType.QString)
+  )
+
+  fun deserializeMap(data: QVariantMap) = NetworkServer(
+    host = data["Host"].into(""),
+    port = data["Port"].into(PortDefaults.PORT_PLAINTEXT.port),
+    password = data["Password"].into(""),
+    useSsl = data["UseSSL"].into(false),
+    sslVerify = data["sslVerify"].into(false),
+    sslVersion = data["sslVersion"].into(0),
+    useProxy = data["UseProxy"].into(false),
+    proxyType = NetworkProxy.of(data["ProxyType"].into(NetworkProxy.Socks5Proxy.value))
+      ?: NetworkProxy.Socks5Proxy,
+    proxyHost = data["ProxyHost"].into("localhost"),
+    proxyPort = data["ProxyPort"].into(8080u),
+    proxyUser = data["ProxyUser"].into(""),
+    proxyPass = data["ProxyPass"].into("")
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferDirectionSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferDirectionSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2cb85989e8e1f30d120c19ec09bf8ef4db93d274
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferDirectionSerializer.kt
@@ -0,0 +1,33 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.quassel
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.TransferDirection
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
+import java.nio.ByteBuffer
+
+/**
+ * Serializer for [TransferDirection]
+ */
+object TransferDirectionSerializer : PrimitiveSerializer<TransferDirection?> {
+  override val javaType: Class<out TransferDirection?> = TransferDirection::class.java
+
+  override fun serialize(buffer: ChainedByteBuffer, data: TransferDirection?, featureSet: FeatureSet) {
+    IntSerializer.serialize(buffer, data?.value ?: 0, featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = TransferDirection.of(
+    IntSerializer.deserialize(buffer, featureSet)
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferIdListSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferIdListSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..396109a801e56b378019b904bb9c5410cc3cc9dd
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferIdListSerializer.kt
@@ -0,0 +1,44 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.quassel
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.TransferIdList
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.UuidSerializer
+import java.nio.ByteBuffer
+import java.util.UUID
+
+/**
+ * Serializer for [TransferIdList]
+ */
+object TransferIdListSerializer : PrimitiveSerializer<TransferIdList> {
+  @Suppress("UNCHECKED_CAST")
+  override val javaType: Class<TransferIdList> = List::class.java as Class<TransferIdList>
+
+  override fun serialize(buffer: ChainedByteBuffer, data: TransferIdList, featureSet: FeatureSet) {
+    IntSerializer.serialize(buffer, data.size, featureSet)
+    data.forEach {
+      UuidSerializer.serialize(buffer, it, featureSet)
+    }
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet): TransferIdList {
+    val result = mutableListOf<UUID>()
+    val length = IntSerializer.deserialize(buffer, featureSet)
+    for (i in 0 until length) {
+      result.add(UuidSerializer.deserialize(buffer, featureSet))
+    }
+    return result
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferStatusSerializer.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferStatusSerializer.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9d93c21b0163882eb70d925f91d703b32ab399ee
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/serializers/quassel/TransferStatusSerializer.kt
@@ -0,0 +1,33 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.serializers.quassel
+
+import de.justjanne.libquassel.protocol.features.FeatureSet
+import de.justjanne.libquassel.protocol.io.ChainedByteBuffer
+import de.justjanne.libquassel.protocol.models.TransferStatus
+import de.justjanne.libquassel.protocol.serializers.PrimitiveSerializer
+import de.justjanne.libquassel.protocol.serializers.qt.IntSerializer
+import java.nio.ByteBuffer
+
+/**
+ * Serializer for [TransferStatus]
+ */
+object TransferStatusSerializer : PrimitiveSerializer<TransferStatus?> {
+  override val javaType: Class<out TransferStatus?> = TransferStatus::class.java
+
+  override fun serialize(buffer: ChainedByteBuffer, data: TransferStatus?, featureSet: FeatureSet) {
+    IntSerializer.serialize(buffer, data?.value ?: 0, featureSet)
+  }
+
+  override fun deserialize(buffer: ByteBuffer, featureSet: FeatureSet) = TransferStatus.of(
+    IntSerializer.deserialize(buffer, featureSet)
+  )
+}
diff --git a/libquassel-state/build.gradle.kts b/libquassel-state/build.gradle.kts
new file mode 100644
index 0000000000000000000000000000000000000000..ac709bd82221efc352e403bd581612ce221cb43b
--- /dev/null
+++ b/libquassel-state/build.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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/.
+ */
+
+plugins {
+  id("com.vanniktech.maven.publish")
+}
+
+dependencies {
+  api(project(":libquassel-protocol"))
+}
diff --git a/libquassel-state/gradle.properties b/libquassel-state/gradle.properties
new file mode 100644
index 0000000000000000000000000000000000000000..646f2829f35f2e6ac48d549cf08ec0c103f1dee0
--- /dev/null
+++ b/libquassel-state/gradle.properties
@@ -0,0 +1,13 @@
+#
+# libquassel
+# Copyright (c) 2021 Janne Mareike Koschinski
+# Copyright (c) 2021 The Quassel Project
+#
+# 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/.
+#
+
+POM_ARTIFACT_ID=libquassel-state
+POM_NAME=libquassel State
+POM_DESCRIPTION=Library implementing Quassel's stateful RPC objects in Kotlin
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/AliasManager.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/AliasManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4dcc277929c97053bf2c1f062025eb5ad62bad5b
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/AliasManager.kt
@@ -0,0 +1,36 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("AliasManager")
+interface AliasManager : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun addAlias(name: String, expansion: String) {
+    sync(
+      target = ProtocolSide.CORE,
+      "addAlias",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(name, QtType.QString),
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(expansion, QtType.QString)
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BacklogManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BacklogManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..03dd67d829f19a6f315a2487c66c7324a3bab52a
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BacklogManagerProtocol.kt
@@ -0,0 +1,315 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("BacklogManager")
+interface BacklogManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklog(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklog",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(bufferId, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogFiltered(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogFiltered",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(bufferId, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(flags, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogAll(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogAll",
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestBacklogAllFiltered(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestBacklogAll",
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(flags, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklog(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklog",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(bufferId, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogFiltered(
+    bufferId: BufferId,
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogFiltered",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(bufferId, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(flags, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogAll(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogAll",
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveBacklogAllFiltered(
+    first: MsgId = MsgId(-1),
+    last: MsgId = MsgId(-1),
+    limit: Int = -1,
+    additional: Int = 0,
+    type: Int = -1,
+    flags: Int = -1
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveBacklogAllFiltered",
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(first, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(last, QuasselType.MsgId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(additional, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(flags, QtType.Int),
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferSyncerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferSyncerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0f1e5dc90b1795ec10c240d3627f034bc343be8a
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferSyncerProtocol.kt
@@ -0,0 +1,239 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.ids.BufferId
+import de.justjanne.libquassel.protocol.models.ids.MsgId
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("BufferSyncer")
+interface BufferSyncerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun markBufferAsRead(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "markBufferAsRead",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestMarkBufferAsRead(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMarkBufferAsRead",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun mergeBuffersPermanently(buffer: BufferId, buffer2: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "mergeBuffersPermanently",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer2, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestMergeBuffersPermanently(buffer: BufferId, buffer2: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMergeBuffersPermanently",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer2, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun renameBuffer(buffer: BufferId, newName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "renameBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(newName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRenameBuffer(buffer: BufferId, newName: String) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRenameBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(newName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMarkerLine(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMarkerLine",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetLastSeenMsg(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetLastSeenMsg",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastSeenMsg(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastSeenMsg",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetMarkerLine(buffer: BufferId, msgId: MsgId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetMarkerLine",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a MsgId
+       */
+      qVariant(msgId, QuasselType.MsgId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setBufferActivity(buffer: BufferId, count: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setBufferActivity",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(count, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHighlightCount(buffer: BufferId, count: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHighlightCount",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(count, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun requestPurgeBufferIds() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestPurgeBufferIds"
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewConfigProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewConfigProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..897e5b789f0d7f420fed9a47133be1d0e295c97b
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewConfigProtocol.kt
@@ -0,0 +1,274 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+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.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("BufferViewConfig")
+interface BufferViewConfigProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestAddBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun moveBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "moveBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestMoveBuffer(buffer: BufferId, pos: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestMoveBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(pos, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveBuffer(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBuffer",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeBufferPermanently(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeBufferPermanently",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveBufferPermanently(buffer: BufferId) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveBufferPermanently",
+      /**
+       * Construct a QVariant from a BufferId
+       */
+      qVariant(buffer, QuasselType.BufferId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setBufferViewName(value: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setBufferViewName",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetBufferViewName(value: String) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetBufferViewName",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAddNewBuffersAutomatically(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAddNewBuffersAutomatically",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAllowedBufferTypes(value: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAllowedBufferTypes",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(value, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDisableDecoration(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDisableDecoration",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHideInactiveBuffers(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHideInactiveBuffers",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHideInactiveNetworks(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHideInactiveNetworks",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMinimumActivity(value: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMinimumActivity",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(value, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNetworkId(value: NetworkId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNetworkId",
+      /**
+       * Construct a QVariant from a Message
+       */
+      qVariant(value, QuasselType.NetworkId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setShowSearch(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setShowSearch",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSortAlphabetically(value: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSortAlphabetically",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(value, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a74319ce6ecda3e5ed038bf92079b21c046c181f
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/BufferViewManagerProtocol.kt
@@ -0,0 +1,87 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantList
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("BufferViewManager")
+interface BufferViewManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addBufferViewConfig(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addBufferViewConfig",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun newBufferViewConfig(bufferViewConfigId: Int) {
+    addBufferViewConfig(bufferViewConfigId)
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestCreateBufferView(properties: QVariantMap) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestCreateBufferView",
+      /**
+       * Construct a QVariant from a QVariantMap
+       */
+      qVariant(properties, QtType.QVariantMap),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestCreateBufferViews(properties: QVariantList) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestCreateBufferViews",
+      /**
+       * Construct a QVariant from a QVariantList
+       */
+      qVariant(properties, QtType.QVariantList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun deleteBufferViewConfig(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "deleteBufferViewConfig",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestDeleteBufferView(bufferViewConfigId: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestDeleteBufferView",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(bufferViewConfigId, QtType.Int),
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CertManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CertManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..78565d732c0684cb0548ed2638dc387ce0741c02
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CertManagerProtocol.kt
@@ -0,0 +1,52 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.nio.ByteBuffer
+
+@SyncedObject("CertManager")
+interface CertManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSslCert(encoded: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSslCert",
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(encoded, QtType.QByteArray),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSslKey(encoded: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSslKey",
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(encoded, QtType.QByteArray),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CoreInfoProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CoreInfoProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..41174326be6d450f98d0c3506b6cf04b8ba558eb
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/CoreInfoProtocol.kt
@@ -0,0 +1,39 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("CoreInfo")
+interface CoreInfoProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCoreData(data: QVariantMap) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCoreData",
+      /**
+       * Construct a QVariant from a QVariantMap
+       */
+      qVariant(data, QtType.QVariantMap),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/DccConfigProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/DccConfigProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..84aa847faa465a5a1695b077afebdfb9b44ce1d1
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/DccConfigProtocol.kt
@@ -0,0 +1,151 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.DccIpDetectionMode
+import de.justjanne.libquassel.protocol.models.DccPortSelectionMode
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.net.InetAddress
+
+@SyncedObject("DccConfig")
+interface DccConfigProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDccEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDccEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setOutgoingIp(outgoingIp: InetAddress) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setOutgoingIp",
+      /**
+       * Construct a QVariant from a InetAddress
+       */
+      qVariant(outgoingIp, QuasselType.QHostAddress),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIpDetectionMode(ipDetectionMode: DccIpDetectionMode) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIpDetectionMode",
+      /**
+       * Construct a QVariant from a DccIpDetectionMode
+       */
+      qVariant(ipDetectionMode, QuasselType.DccConfigIpDetectionMode),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPortSelectionMode(portSelectionMode: DccPortSelectionMode) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPortSelectionMode",
+      /**
+       * Construct a QVariant from a DccPortSelectionMode
+       */
+      qVariant(portSelectionMode, QuasselType.DccConfigPortSelectionMode),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMinPort(port: UShort) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMinPort",
+      /**
+       * Construct a QVariant from a UShort
+       */
+      qVariant(port, QtType.UShort),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMaxPort(port: UShort) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMaxPort",
+      /**
+       * Construct a QVariant from a UShort
+       */
+      qVariant(port, QtType.UShort),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setChunkSize(chunkSize: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setChunkSize",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(chunkSize, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSendTimeout(timeout: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSendTimeout",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(timeout, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUsePassiveDcc(use: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUsePassiveDcc",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(use, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseFastSend(use: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseFastSend",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(use, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/HighlightRuleManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/HighlightRuleManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0bc2d9984f72b789ad19d1c130f5582f2b96270b
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/HighlightRuleManagerProtocol.kt
@@ -0,0 +1,221 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("HighlightRuleManager")
+interface HighlightRuleManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestToggleHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun toggleHighlightRule(highlightRule: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "toggleHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightRule, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddHighlightRule(
+    id: Int,
+    name: String?,
+    isRegEx: Boolean,
+    isCaseSensitive: Boolean,
+    isEnabled: Boolean,
+    isInverse: Boolean,
+    sender: String?,
+    chanName: String?
+  ) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(id, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(name, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isRegEx, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isCaseSensitive, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isEnabled, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isInverse, QtType.Bool),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(sender, QtType.QString),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(chanName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addHighlightRule(
+    id: Int,
+    name: String?,
+    isRegEx: Boolean,
+    isCaseSensitive: Boolean,
+    isEnabled: Boolean,
+    isInverse: Boolean,
+    sender: String?,
+    chanName: String?
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addHighlightRule",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(id, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(name, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isRegEx, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isCaseSensitive, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isEnabled, QtType.Bool),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isInverse, QtType.Bool),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(sender, QtType.QString),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(chanName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetHighlightNick(highlightNick: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetHighlightNick",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightNick, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHighlightNick(highlightNick: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHighlightNick",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(highlightNick, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetNicksCaseSensitive(nicksCaseSensitive: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetNicksCaseSensitive",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(nicksCaseSensitive, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNicksCaseSensitive(nicksCaseSensitive: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNicksCaseSensitive",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(nicksCaseSensitive, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IdentityProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IdentityProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..db92c1c04e7e39bb14db65d88e2b7080a8582786
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IdentityProtocol.kt
@@ -0,0 +1,258 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+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.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("Identity")
+interface IdentityProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoAwayEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoAwayReason(reason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(reason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoAwayReasonEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayReasonEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoAwayTime(time: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoAwayTime",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(time, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayNick(awayNick: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayNick",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(awayNick, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayNickEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayNickEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayReason(awayReason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(awayReason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayReasonEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayReasonEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDetachAwayEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDetachAwayReason(reason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(reason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDetachAwayReasonEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDetachAwayReasonEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setId(id: IdentityId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setId",
+      /**
+       * Construct a QVariant from a IdentityId
+       */
+      qVariant(id, QuasselType.IdentityId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdent(ident: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdent",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ident, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdentityName(name: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdentityName",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(name, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setKickReason(reason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setKickReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(reason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNicks(nicks: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNicks",
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(nicks, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPartReason(reason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPartReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(reason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setQuitReason(reason: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setQuitReason",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(reason, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setRealName(realName: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRealName",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(realName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IgnoreListManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IgnoreListManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0265d16ab89da67430be2aadbf60bc5537b2b574
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IgnoreListManagerProtocol.kt
@@ -0,0 +1,156 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject(name = "IgnoreListManager")
+interface IgnoreListManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIgnoreListItem(
+    type: Int,
+    ignoreRule: String?,
+    isRegEx: Boolean,
+    strictness: Int,
+    scope: Int,
+    scopeRule: String?,
+    isActive: Boolean
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIgnoreListItem",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isRegEx, QtType.Bool),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(strictness, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(scope, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(scopeRule, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isActive, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeIgnoreListItem(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeIgnoreListItem",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAddIgnoreListItem(
+    type: Int,
+    ignoreRule: String?,
+    isRegEx: Boolean,
+    strictness: Int,
+    scope: Int,
+    scopeRule: String?,
+    isActive: Boolean
+  ) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestAddIgnoreListItem",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(type, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isRegEx, QtType.Bool),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(strictness, QtType.Int),
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(scope, QtType.Int),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(scopeRule, QtType.QString),
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isActive, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRemoveIgnoreListItem(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRemoveIgnoreListItem",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestToggleIgnoreRule(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestToggleIgnoreRule",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun toggleIgnoreRule(ignoreRule: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestToggleIgnoreRule",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ignoreRule, QtType.QString),
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcChannelProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcChannelProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1b3d800f22061d5757c7767bc04da5b0112b742c
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcChannelProtocol.kt
@@ -0,0 +1,170 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.QStringList
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+interface IrcChannelProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addChannelMode(mode: Char, value: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addChannelMode",
+      /**
+       * Construct a QVariant from a Char
+       */
+      qVariant(mode, QtType.QChar),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addUserMode(nick: String?, mode: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addUserMode",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(nick, QtType.QString),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(mode, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun joinIrcUsers(nicks: QStringList, modes: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "joinIrcUsers",
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(nicks, QtType.QStringList),
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(modes, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun part(nick: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "part",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(nick, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeChannelMode(mode: Char, value: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeChannelMode",
+      /**
+       * Construct a QVariant from a Char
+       */
+      qVariant(mode, QtType.QChar),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeUserMode(nick: String?, mode: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeUserMode",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(nick, QtType.QString),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(mode, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setEncrypted(encrypted: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setEncrypted",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(encrypted, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPassword(password: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPassword",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(password, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setTopic(topic: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setTopic",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(topic, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUserModes(nick: String?, modes: String? = null) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUserModes",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(nick, QtType.QString),
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcListHelperProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcListHelperProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ccb26f84fbccf256ad28d13d087a657140d663de
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcListHelperProtocol.kt
@@ -0,0 +1,92 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.QStringList
+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.variant.QVariantList
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+@SyncedObject("IrcListHelper")
+interface IrcListHelperProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestChannelList(netId: NetworkId, channelFilters: QStringList): QVariantList {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestChannelList",
+      /**
+       * Construct a QVariant from a Message
+       */
+      qVariant(netId, QuasselType.NetworkId),
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(channelFilters, QtType.QStringList),
+    )
+    return emptyList()
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun receiveChannelList(netId: NetworkId, channelFilters: QStringList, channels: QVariantList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "receiveChannelList",
+      /**
+       * Construct a QVariant from a Message
+       */
+      qVariant(netId, QuasselType.NetworkId),
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(channelFilters, QtType.QStringList),
+      /**
+       * Construct a QVariant from a QVariantList
+       */
+      qVariant(channels, QtType.QVariantList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun reportError(error: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "reportError",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(error, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun reportFinishedList(netId: NetworkId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "reportFinishedList",
+      /**
+       * Construct a QVariant from a Message
+       */
+      qVariant(netId, QuasselType.NetworkId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcUserProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcUserProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ccbb0ddbbd6c6102459a023f95fbd9e87bab2e22
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/IrcUserProtocol.kt
@@ -0,0 +1,299 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import org.threeten.bp.temporal.Temporal
+
+interface IrcUserProtocol : SyncableProtocol {
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addUserModes(modes: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addUserModes",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun joinChannel(channelname: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "joinChannel",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(channelname, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun partChannel(channelname: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "partChannel",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(channelname, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun quit() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "quit",
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeUserModes(modes: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeUserModes",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAccount(account: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAccount",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(account, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAway(away: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAway",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(away, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAwayMessage(awayMessage: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAwayMessage",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(awayMessage, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setEncrypted(encrypted: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setEncrypted",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(encrypted, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setHost(host: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setHost",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(host, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdleTime(idleTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdleTime",
+      /**
+       * Construct a QVariant from a Temporal object
+       */
+      qVariant(idleTime, QtType.QDateTime),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIrcOperator(ircOperator: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIrcOperator",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(ircOperator, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastAwayMessage(lastAwayMessage: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastAwayMessage",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(lastAwayMessage, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLastAwayMessageTime(lastAwayMessageTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLastAwayMessageTIme",
+      /**
+       * Construct a QVariant from a Temporal object
+       */
+      qVariant(lastAwayMessageTime, QtType.QDateTime),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLoginTime(loginTime: Temporal) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLoginTime",
+      /**
+       * Construct a QVariant from a Temporal object
+       */
+      qVariant(loginTime, QtType.QDateTime),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNick(nick: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNick",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(nick, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setRealName(realName: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRealName",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(realName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setServer(server: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setServer",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(server, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSuserHost(suserHost: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSuserHost",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(suserHost, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUser(user: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUser",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(user, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUserModes(modes: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUserModes",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(modes, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setWhoisServiceReply(whoisServiceReply: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setWhoisServiceReply",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(whoisServiceReply, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun updateHostmask(mask: String?) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "updateHostmask",
+      /**
+       * Construct a QVariant from a String?
+       */
+      qVariant(mask, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkConfigProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkConfigProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..05bf4766134d3c25fb3dcb06faf0ad8852ebd62d
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkConfigProtocol.kt
@@ -0,0 +1,217 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+interface NetworkConfigProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoDelay(delay: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoDelay",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(delay, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoDelay(delay: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoDelay",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(delay, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoInterval",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(interval, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoWhoInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoWhoInterval",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(interval, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetAutoWhoNickLimit(limit: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetAutoWhoNickLimit",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setAutoWhoNickLimit(limit: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setAutoWhoNickLimit",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(limit, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetMaxPingCount(count: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetMaxPingCount",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(count, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setMaxPingCount(count: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setMaxPingCount",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(count, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetPingInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetPingInterval",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(interval, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setPingInterval(interval: Int) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setPingInterval",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(interval, QtType.Int)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetPingTimeoutEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetPingTimeoutEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setPingTimeoutEnabled(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setPingTimeoutEnabled",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetStandardCtcp(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestSetStandardCtcp",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun setStandardCtcp(enabled: Boolean) {
+    sync(
+      target = ProtocolSide.CORE,
+      "setStandardCtcp",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(enabled, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5bf3376a4a1ac19a6ae1bc61417e9c3a1457e1b5
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/NetworkProtocol.kt
@@ -0,0 +1,499 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.NetworkInfo
+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.variant.QVariantList
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.nio.ByteBuffer
+
+interface NetworkProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNetworkName(networkName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNetworkName",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(networkName, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCurrentServer(currentServer: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCurrentServer",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(currentServer, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMyNick(myNick: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMyNick",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(myNick, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setLatency(latency: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setLatency",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(latency, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForServer(codecForServer: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForServer",
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(codecForServer, QtType.QByteArray),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForEncoding(codecForEncoding: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForEncoding",
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(codecForEncoding, QtType.QByteArray),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setCodecForDecoding(codecForDecoding: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setCodecForDecoding",
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(codecForDecoding, QtType.QByteArray),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setIdentity(identityId: IdentityId) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setIdentity",
+      /**
+       * Construct a QVariant from a IdentityId
+       */
+      qVariant(identityId, QuasselType.IdentityId),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setConnected(isConnected: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setConnected",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(isConnected, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setConnectionState(connectionState: Int) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setConnectionState",
+      /**
+       * Construct a QVariant from a Int
+       */
+      qVariant(connectionState, QtType.Int),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseRandomServer(useRandomServer: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseRandomServer",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(useRandomServer, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPerform(perform: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPerform",
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(perform, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSkipCaps(skipCaps: QStringList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSkipCaps",
+      /**
+       * Construct a QVariant from a QStringList
+       */
+      qVariant(skipCaps, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseAutoIdentify(useAutoIdentify: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseAutoIdentify",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(useAutoIdentify, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoIdentifyService(autoIdentifyService: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoIdentifyService",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(autoIdentifyService, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoIdentifyPassword(autoIdentifyPassword: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoIdentifyPassword",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(autoIdentifyPassword, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseSasl(useSasl: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseSasl",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(useSasl, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSaslAccount(saslAccount: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSaslAccount",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(saslAccount, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setSaslPassword(saslPassword: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setSaslPassword",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(saslPassword, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseAutoReconnect(useAutoReconnect: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseAutoReconnect",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(useAutoReconnect, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoReconnectInterval(autoReconnectInterval: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoReconnectInterval",
+      /**
+       * Construct a QVariant from a UInt
+       */
+      qVariant(autoReconnectInterval, QtType.UInt),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAutoReconnectRetries(autoReconnectRetries: UShort) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAutoReconnectRetries",
+      /**
+       * Construct a QVariant from a UShort
+       */
+      qVariant(autoReconnectRetries, QtType.UShort),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUnlimitedReconnectRetries(unlimitedReconnectRetries: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUnlimitedReconnectRetries",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(unlimitedReconnectRetries, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setRejoinChannels(rejoinChannels: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setRejoinChannels",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(rejoinChannels, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUseCustomMessageRate(useCustomMessageRate: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUseCustomMessageRate",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(useCustomMessageRate, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMessageRateBurstSize(messageRateBurstSize: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMessageRateBurstSize",
+      /**
+       * Construct a QVariant from a UInt
+       */
+      qVariant(messageRateBurstSize, QtType.UInt),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setMessageRateDelay(messageRateDelay: UInt) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setMessageRateDelay",
+      /**
+       * Construct a QVariant from a UInt
+       */
+      qVariant(messageRateDelay, QtType.UInt),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setUnlimitedMessageRate(unlimitedMessageRate: Boolean) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setUnlimitedMessageRate",
+      /**
+       * Construct a QVariant from a Boolean
+       */
+      qVariant(unlimitedMessageRate, QtType.Bool),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setServerList(serverList: QVariantList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setServerList",
+      /**
+       * Construct a QVariant from a QVariantList
+       */
+      qVariant(serverList, QtType.QVariantList),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addSupport(param: String, value: String = "") {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addSupport",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(param, QtType.QString),
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeSupport(param: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeSupport",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(param, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addCap(capability: String, value: String = "") {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addCap",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(capability, QtType.QString),
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(value, QtType.QString),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun acknowledgeCap(param: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "acknowledgeCap",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(param, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun removeCap(param: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "removeCap",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(param, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun clearCaps() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "clearCaps"
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIrcUser(hostmask: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIrcUser",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(hostmask, QtType.QString),
+    )
+  }
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun addIrcChannel(channel: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "addIrcChannel",
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestConnect() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestConnect",
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestDisconnect() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestDisconnect",
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestSetNetworkInfo(info: NetworkInfo) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "requestSetNetworkInfo",
+      qVariant(info, QuasselType.NetworkInfo),
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/RpcHandlerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/RpcHandlerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f79db17ea55741219a84e1458f2cc7a6a375320f
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/RpcHandlerProtocol.kt
@@ -0,0 +1,190 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.annotations.SyncedObject
+import de.justjanne.libquassel.protocol.models.BufferInfo
+import de.justjanne.libquassel.protocol.models.Message
+import de.justjanne.libquassel.protocol.models.NetworkInfo
+import de.justjanne.libquassel.protocol.models.ids.IdentityId
+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.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.nio.ByteBuffer
+
+@SyncedObject(name = "RpcHandler")
+interface RpcHandlerProtocol : SyncableProtocol {
+  @SyncedCall(name = "__objectRenamed__", target = ProtocolSide.CLIENT)
+  fun objectRenamed(classname: ByteBuffer, newName: String?, oldName: String?) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "__objectRenamed__",
+      qVariant(classname, QtType.QByteArray),
+      qVariant(newName, QtType.QString),
+      qVariant(oldName, QtType.QString)
+    )
+  }
+
+  @SyncedCall(name = "2displayMsg(Message)", target = ProtocolSide.CLIENT)
+  fun displayMsg(message: Message) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2displayMsg(Message)",
+      qVariant(message, QuasselType.Message)
+    )
+  }
+
+  @SyncedCall(name = "2displayStatusMsg(QString,QString)", target = ProtocolSide.CLIENT)
+  fun displayStatusMsg(net: String?, msg: String?) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2displayStatusMsg(QString,QString)",
+      qVariant(net, QtType.QString),
+      qVariant(msg, QtType.QString)
+    )
+  }
+
+  @SyncedCall(name = "2bufferInfoUpdated(BufferInfo)", target = ProtocolSide.CLIENT)
+  fun bufferInfoUpdated(bufferInfo: BufferInfo) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2bufferInfoUpdated(BufferInfo)",
+      qVariant(bufferInfo, QuasselType.BufferInfo)
+    )
+  }
+
+  @SyncedCall(name = "2identityCreated(Identity)", target = ProtocolSide.CLIENT)
+  fun identityCreated(identity: IdentityProtocol) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2identityCreated(Identity)",
+      qVariant(identity, QuasselType.Identity)
+    )
+  }
+
+  @SyncedCall(name = "2identityRemoved(IdentityId)", target = ProtocolSide.CLIENT)
+  fun identityRemoved(identityId: IdentityId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2identityRemoved(IdentityId)",
+      qVariant(identityId, QuasselType.IdentityId)
+    )
+  }
+
+  @SyncedCall(name = "2networkCreated(NetworkId)", target = ProtocolSide.CLIENT)
+  fun networkCreated(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2networkCreated(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId)
+    )
+  }
+
+  @SyncedCall(name = "2networkRemoved(NetworkId)", target = ProtocolSide.CLIENT)
+  fun networkRemoved(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2networkRemoved(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId)
+    )
+  }
+
+  @SyncedCall(name = "2passwordChanged(PeerPtr,bool)", target = ProtocolSide.CLIENT)
+  fun passwordChanged(peer: ULong, success: Boolean) {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2passwordChanged(PeerPtr,bool)",
+      qVariant(peer, QuasselType.PeerPtr),
+      qVariant(success, QtType.Bool)
+    )
+  }
+
+  @SyncedCall(name = "2disconnectFromCore()", target = ProtocolSide.CLIENT)
+  fun disconnectFromCore() {
+    rpc(
+      target = ProtocolSide.CLIENT,
+      "2disconnectFromCore()",
+    )
+  }
+
+  @SyncedCall(name = "2createIdentity(Identity,QVariantMap)", target = ProtocolSide.CORE)
+  fun createIdentity(identity: IdentityProtocol, additional: QVariantMap) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2createIdentity(Identity,QVariantMap)",
+      qVariant(identity, QuasselType.Identity),
+      qVariant(additional, QtType.QVariantMap),
+    )
+  }
+
+  @SyncedCall(name = "2removeIdentity(IdentityId)", target = ProtocolSide.CORE)
+  fun removeIdentity(identityId: IdentityId) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2removeIdentity(IdentityId)",
+      qVariant(identityId, QuasselType.IdentityId),
+    )
+  }
+
+  @SyncedCall(name = "2createNetwork(NetworkInfo,QStringList)", target = ProtocolSide.CORE)
+  fun createNetwork(networkInfo: NetworkInfo, channels: List<String>) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2createNetwork(NetworkInfo,QStringList)",
+      qVariant(networkInfo, QuasselType.NetworkInfo),
+      qVariant(channels, QtType.QStringList),
+    )
+  }
+
+  @SyncedCall(name = "2removeNetwork(NetworkId)", target = ProtocolSide.CORE)
+  fun removeNetwork(networkId: NetworkId) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2removeNetwork(NetworkId)",
+      qVariant(networkId, QuasselType.NetworkId),
+    )
+  }
+
+  @SyncedCall(name = "2changePassword(PeerPtr,QString,QString,QString)", target = ProtocolSide.CORE)
+  fun changePassword(peerPtr: ULong, user: String?, old: String?, new: String?) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2changePassword(PeerPtr,QString,QString,QString)",
+      qVariant(peerPtr, QuasselType.PeerPtr),
+      qVariant(user, QtType.QString),
+      qVariant(old, QtType.QString),
+      qVariant(new, QtType.QString)
+    )
+  }
+
+  @SyncedCall(name = "2kickClient(int)", target = ProtocolSide.CORE)
+  fun requestKickClient(id: Int) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2kickClient(int)",
+      qVariant(id, QtType.Int)
+    )
+  }
+
+  @SyncedCall(name = "2sendInput(BufferInfo,QString)", target = ProtocolSide.CORE)
+  fun sendInput(bufferInfo: BufferInfo, message: String?) {
+    rpc(
+      target = ProtocolSide.CORE,
+      "2sendInput(BufferInfo,QString)",
+      qVariant(bufferInfo, QuasselType.BufferInfo),
+      qVariant(message, QtType.QString)
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SignalProxy.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SignalProxy.kt
new file mode 100644
index 0000000000000000000000000000000000000000..d4544d920f017ac0fb063753e0bec9656e7bbc6b
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SignalProxy.kt
@@ -0,0 +1,55 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.models.SignalProxyMessage
+import de.justjanne.libquassel.protocol.variant.QVariantList
+
+interface SignalProxy {
+  val protocolSide: ProtocolSide
+
+  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)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SyncableProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SyncableProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..53704f57d09d087d387763dc4ef0ef11b376d3a6
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/SyncableProtocol.kt
@@ -0,0 +1,62 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.QVariant_
+import de.justjanne.libquassel.protocol.variant.qVariant
+
+interface SyncableProtocol {
+  val className: String
+  val objectName: String
+  val initialized: Boolean
+  val proxy: SignalProxy
+
+  fun fromVariantMap(properties: QVariantMap)
+  fun toVariantMap(): QVariantMap
+
+  fun sync(target: ProtocolSide, function: String, vararg arg: QVariant_) {
+    if (initialized) {
+      proxy.sync(target, className, objectName, function, arg.toList())
+    }
+  }
+
+  fun rpc(target: ProtocolSide, function: String, vararg arg: QVariant_) {
+    if (initialized) {
+      proxy.rpc(target, function, arg.toList())
+    }
+  }
+
+  fun update(properties: QVariantMap) {
+    fromVariantMap(properties)
+    sync(
+      target = ProtocolSide.CLIENT,
+      "update",
+      /**
+       * Construct a QVariant from a QVariantMap
+       */
+      qVariant(properties, QtType.QVariantMap)
+    )
+  }
+
+  fun requestUpdate(properties: QVariantMap = toVariantMap()) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestUpdate",
+      /**
+       * Construct a QVariant from a QVariantMap
+       */
+      qVariant(properties, QtType.QVariantMap)
+    )
+  }
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferManagerProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferManagerProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..5db8f52a8c33ed12265b790687b6e82bc411e392
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferManagerProtocol.kt
@@ -0,0 +1,46 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.TransferIdList
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.util.UUID
+
+interface TransferManagerProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setTransferIds(transferIds: TransferIdList) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setTransferIds",
+      qVariant(transferIds, QuasselType.TransferIdList)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun onCoreTransferAdded(transferId: UUID) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "onCoreTransferAdded",
+      qVariant(transferId, QtType.Uuid)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferProtocol.kt b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferProtocol.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b58fb52c12d7207ce7e37908df375f1e389c328c
--- /dev/null
+++ b/libquassel-state/src/main/kotlin/de/justjanne/libquassel/state/protocol/TransferProtocol.kt
@@ -0,0 +1,185 @@
+/*
+ * libquassel
+ * Copyright (c) 2021 Janne Mareike Koschinski
+ * Copyright (c) 2021 The Quassel Project
+ *
+ * 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.state.protocol
+
+import de.justjanne.libquassel.annotations.ProtocolSide
+import de.justjanne.libquassel.annotations.SyncedCall
+import de.justjanne.libquassel.protocol.models.TransferDirection
+import de.justjanne.libquassel.protocol.models.TransferStatus
+import de.justjanne.libquassel.protocol.models.types.QtType
+import de.justjanne.libquassel.protocol.models.types.QuasselType
+import de.justjanne.libquassel.protocol.variant.QVariantMap
+import de.justjanne.libquassel.protocol.variant.qVariant
+import java.net.InetAddress
+import java.nio.ByteBuffer
+
+interface TransferProtocol : SyncableProtocol {
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun accept(savePath: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "accept",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(savePath, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestAccepted(peer: ULong = 0uL) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestAccepted",
+      /**
+       * Construct a QVariant from a ULong
+       */
+      qVariant(peer, QtType.ULong)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun reject() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "reject"
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  fun requestRejected(peer: ULong = 0uL) {
+    sync(
+      target = ProtocolSide.CORE,
+      "requestRejected",
+      /**
+       * Construct a QVariant from a ULong
+       */
+      qVariant(peer, QtType.ULong)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setStatus(status: TransferStatus) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setStatus",
+      qVariant(status, QuasselType.TransferStatus)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setDirection(direction: TransferDirection) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setDirection",
+      qVariant(direction, QuasselType.TransferDirection)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setAddress(address: InetAddress) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setAddress",
+      /**
+       * Construct a QVariant from a InetAddress
+       */
+      qVariant(address, QuasselType.QHostAddress)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setPort(port: UShort) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setPort",
+      /**
+       * Construct a QVariant from a UShort
+       */
+      qVariant(port, QtType.UShort)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setFileName(fileName: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setFileName",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(fileName, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setFileSize(fileSize: ULong) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setFileSize",
+      /**
+       * Construct a QVariant from a ULong
+       */
+      qVariant(fileSize, QtType.ULong)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setNick(nick: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setNick",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(nick, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun setError(errorString: String) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "setError",
+      /**
+       * Construct a QVariant from a String
+       */
+      qVariant(errorString, QtType.QString)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun dataReceived(peer: ULong, data: ByteBuffer) {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "dataReceived",
+      qVariant(peer, QuasselType.PeerPtr),
+      /**
+       * Construct a QVariant from a ByteBuffer
+       */
+      qVariant(data, QtType.QByteArray)
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  fun cleanUp() {
+    sync(
+      target = ProtocolSide.CLIENT,
+      "cleanUp",
+    )
+  }
+
+  @SyncedCall(target = ProtocolSide.CLIENT)
+  override fun update(properties: QVariantMap) = super.update(properties)
+
+  @SyncedCall(target = ProtocolSide.CORE)
+  override fun requestUpdate(properties: QVariantMap) = super.requestUpdate(properties)
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index edcbd52885af4aedd064fb86a1b6426cfacac5c7..0f0d93227259cc79712cbf2ebf31204db827538a 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -13,5 +13,6 @@ rootProject.name = "libquassel"
 include(
   ":libquassel-annotations",
   ":libquassel-protocol",
+  ":libquassel-state",
   ":libquassel-client"
 )