diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreRule.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreRule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1879fce447a4641002594887ee895dbeb15d36eb
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreRule.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.rules
+
+import de.justjanne.libquassel.protocol.util.expression.ExpressionMatch
+
+data class IgnoreRule(
+  val type: IgnoreType,
+  val ignoreRule: String,
+  val isRegEx: Boolean = false,
+  val strictness: StrictnessType,
+  val isEnabled: Boolean = true,
+  val scope: ScopeType,
+  val scopeRule: String
+) {
+  val ignoreMatch = ExpressionMatch(
+    ignoreRule,
+    if (isRegEx) ExpressionMatch.MatchMode.MatchRegEx
+    else ExpressionMatch.MatchMode.MatchWildcard,
+    false
+  )
+  val scopeMatch = ExpressionMatch(
+    scopeRule,
+    ExpressionMatch.MatchMode.MatchMultiWildcard,
+    false
+  )
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9e6d215825bb9211265dabc5100430439844360e
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/IgnoreType.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.rules
+
+enum class IgnoreType(
+  val value: Int
+) {
+  SenderIgnore(0),
+  MessageIgnore(1),
+  CtcpIgnore(2);
+
+  companion object {
+    private val values = enumValues<IgnoreType>()
+      .associateBy(IgnoreType::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): IgnoreType? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/ScopeType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/ScopeType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e5444ddd0412085668f8fb8094b396bbdced8e16
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/ScopeType.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.rules
+
+enum class ScopeType(
+  val value: Int
+) {
+  GlobalScope(0),
+  NetworkScope(1),
+  ChannelScope(2);
+
+  companion object {
+    private val values = enumValues<ScopeType>()
+      .associateBy(ScopeType::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): ScopeType? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/StrictnessType.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/StrictnessType.kt
new file mode 100644
index 0000000000000000000000000000000000000000..472f2c74d94081966d1aa64cd87e3872e5089b4d
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/models/rules/StrictnessType.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.rules
+
+enum class StrictnessType(
+  val value: Int
+) {
+  UnmatchedStrictness(0),
+  SoftStrictness(1),
+  HardStrictness(2);
+
+  companion object {
+    private val values = enumValues<StrictnessType>()
+      .associateBy(StrictnessType::value)
+
+    /**
+     * Obtain from underlying representation
+     */
+    fun of(value: Int): StrictnessType? = values[value]
+  }
+}
diff --git a/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IgnoreListManagerState.kt b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IgnoreListManagerState.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f2396c95ae586860e86ab65bb33911eddd706271
--- /dev/null
+++ b/libquassel-protocol/src/main/kotlin/de/justjanne/libquassel/protocol/syncables/state/IgnoreListManagerState.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.syncables.state
+
+import de.justjanne.bitflags.of
+import de.justjanne.libquassel.protocol.models.flags.MessageType
+import de.justjanne.libquassel.protocol.models.flags.MessageTypes
+import de.justjanne.libquassel.protocol.models.rules.IgnoreRule
+import de.justjanne.libquassel.protocol.models.rules.IgnoreType
+import de.justjanne.libquassel.protocol.models.rules.ScopeType
+import de.justjanne.libquassel.protocol.models.rules.StrictnessType
+
+data class IgnoreListManagerState(
+  val rules: List<IgnoreRule> = emptyList()
+) {
+  fun indexOf(ignoreRule: String?): Int = rules.indexOfFirst { it.ignoreRule == ignoreRule }
+  fun contains(ignoreRule: String?) = rules.any { it.ignoreRule == ignoreRule }
+
+  fun isEmpty() = rules.isEmpty()
+  fun count() = rules.size
+
+  fun matchingRules(sender: String) = rules.filter {
+    it.type == IgnoreType.SenderIgnore && it.ignoreMatch.match(sender)
+  }
+
+  fun match(
+    msgContents: String,
+    msgSender: String,
+    msgType: MessageTypes,
+    network: String,
+    bufferName: String
+  ): StrictnessType {
+    if ((MessageType.of(MessageType.Plain, MessageType.Notice, MessageType.Action) intersect msgType).isEmpty()) {
+      return StrictnessType.UnmatchedStrictness
+    }
+
+    return rules.asSequence().filter {
+      it.isEnabled && it.type != IgnoreType.CtcpIgnore
+    }.filter {
+      it.scope == ScopeType.GlobalScope ||
+        it.scope == ScopeType.NetworkScope && it.scopeMatch.match(network) ||
+        it.scope == ScopeType.ChannelScope && it.scopeMatch.match(bufferName)
+    }.filter {
+      val content = if (it.type == IgnoreType.MessageIgnore) msgContents else msgSender
+      it.ignoreMatch.match(content)
+    }.map {
+      it.strictness
+    }.maxByOrNull {
+      it.value
+    } ?: StrictnessType.UnmatchedStrictness
+  }
+}