diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestDeterministicConversations.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestDeterministicConversations.kt
index 90736bbe4396f900e8e2b264b8c7fb046868f705..553588c8adfc195053090bd3ddc66a97d4d38364 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestDeterministicConversations.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestDeterministicConversations.kt
@@ -19,6 +19,6 @@
 package org.briarproject.briar.desktop
 
 fun main() = RunWithTemporaryAccount {
-    getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+    getDeterministicTestDataCreator().createTestData()
     getContactManager().addPendingContact("briar://aatkjq4seoualafpwh4cfckdzr4vpr4slk3bbvpxklf7y7lv4ajw6", "Faythe")
 }.run()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestMixedConversations.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestMixedConversations.kt
index 41647e12e712bd562a201f7d497ad1db8236106a..5b3d1b20ab79c94eb3230bdc14cad7d105dd71ea 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestMixedConversations.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestMixedConversations.kt
@@ -19,6 +19,6 @@
 package org.briarproject.briar.desktop
 
 fun main() = RunWithTemporaryAccount {
-    getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+    getDeterministicTestDataCreator().createTestData()
     getTestDataCreator().createTestData(5, 20, 50, 4, 10, 10)
 }.run()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestScreenshot.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestScreenshot.kt
index c9a31cdf70342ed2096f2098ab043230aef8fff0..fdea3c367f3c80d2e444ab3c55f75dd2e0e884f9 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestScreenshot.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestScreenshot.kt
@@ -27,7 +27,7 @@ import org.briarproject.bramble.api.plugin.LanTcpConstants
  * This includes faking connections to two contacts to make them appear as online.
  */
 fun main() = RunWithTemporaryAccount {
-    getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+    getDeterministicTestDataCreator().createTestData()
     getContactManager().addPendingContact("briar://aatkjq4seoualafpwh4cfckdzr4vpr4slk3bbvpxklf7y7lv4ajw6", "Faythe")
 
     getEventBus().addListener { e ->
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithInvitationAndThreeConnectedTemporaryAccounts.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithInvitationAndThreeConnectedTemporaryAccounts.kt
index 31a490e405e3eb8b662e6f8c9051605c9a101dd5..acdd7cd66a6c4fd6019b60e7a0b0f1896424849d 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithInvitationAndThreeConnectedTemporaryAccounts.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithInvitationAndThreeConnectedTemporaryAccounts.kt
@@ -28,7 +28,7 @@ fun main() = RunWithMultipleTemporaryAccounts(listOf("alice", "bob", "eve")) { /
     val eve = this[2]
 
     listOf(bob, eve).forEach {
-        it.getDeterministicTestDataCreator().createTestData(1, 2, 0, 0, 0)
+        it.getDeterministicTestDataCreator().createTestData()
         connectAppsInstantly(alice, it)
     }
 
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithThreeConnectedTemporaryAccounts.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithThreeConnectedTemporaryAccounts.kt
index db1857b22e2236f4c6914c97a4165f978187d82d..eb25db4e5e5f4de0a99843304d25ac552bb6e3bb 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithThreeConnectedTemporaryAccounts.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithThreeConnectedTemporaryAccounts.kt
@@ -23,7 +23,7 @@ import org.briarproject.briar.desktop.TestUtils.createForumForAll
 
 fun main() = RunWithMultipleTemporaryAccounts(listOf("alice", "bob", "eve")) { // NON-NLS
     forEach {
-        it.getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+        it.getDeterministicTestDataCreator().createTestData()
     }
     connectAllInstantly()
     createForumForAll()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedAsPendingTemporaryAccounts.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedAsPendingTemporaryAccounts.kt
index 3c6f9c5cd1e34e086c568b1357be75609507b6b2..1a36d2193ba53645e4a9c5593c5f4770269f28b8 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedAsPendingTemporaryAccounts.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedAsPendingTemporaryAccounts.kt
@@ -22,7 +22,7 @@ import org.briarproject.briar.desktop.TestUtils.connectAllPending
 
 fun main() = RunWithMultipleTemporaryAccounts(listOf("alice", "bob")) { // NON-NLS
     forEach {
-        it.getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+        it.getDeterministicTestDataCreator().createTestData()
     }
     connectAllPending()
 }.run()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedTemporaryAccounts.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedTemporaryAccounts.kt
index c13c5e994369c29f0007acff8d7747bb60c7a31c..9f3e91df15526f4f39b94a2a1e05ef18027f3531 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedTemporaryAccounts.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoConnectedTemporaryAccounts.kt
@@ -23,7 +23,7 @@ import org.briarproject.briar.desktop.TestUtils.createForumForAll
 
 fun main() = RunWithMultipleTemporaryAccounts(listOf("alice", "bob")) { // NON-NLS
     forEach {
-        it.getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+        it.getDeterministicTestDataCreator().createTestData()
     }
     connectAllInstantly()
     createForumForAll()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt
index aff1eef29b713b62c0bbf4782fa3ecba5ae731d2..be9a409a6a7f840be5a3a3b8bb1a5138a25cfa7c 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/TestWithTwoTemporaryAccounts.kt
@@ -20,6 +20,6 @@ package org.briarproject.briar.desktop
 
 fun main() = RunWithMultipleTemporaryAccounts(listOf("alice", "bob")) { // NON-NLS
     forEach {
-        it.getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20)
+        it.getDeterministicTestDataCreator().createTestData()
     }
 }.run()
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
index 89f8fab55b22d11e77422836833d848ac961497b..a0067b688d67968c1a152a8a04b18ebb97a02e5b 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreator.kt
@@ -18,22 +18,20 @@
 
 package org.briarproject.briar.desktop.testdata
 
+import org.briarproject.briar.desktop.testdata.conversation.Conversations
+import org.briarproject.briar.desktop.testdata.conversation.defaultConversations
+import org.briarproject.briar.desktop.testdata.forum.Forums
+import org.briarproject.briar.desktop.testdata.forum.defaultForums
+
 interface DeterministicTestDataCreator {
     /**
-     * Create fake test data on the IoExecutor
+     * Create deterministic test data on the IoExecutor
      *
-     * @param numContacts          Number of contacts to create. Must be >= 1
-     * @param numPrivateMsgs       Number of private messages to create for each contact.
-     * @param avatarPercent        Percentage of contacts that will use a random profile image.
-     *                             Between 0 and 100.
-     * @param numPrivateGroups     Number of private groups to create. Must be >= 1
-     * @param numPrivateGroupPosts Number of private group messages to create in each group
+     * @param conversations map of contacts and private messages
+     * @param forums list of forums
      */
     fun createTestData(
-        numContacts: Int,
-        numPrivateMsgs: Int,
-        avatarPercent: Int,
-        numPrivateGroups: Int,
-        numPrivateGroupPosts: Int,
+        conversations: Conversations = TestData.defaultConversations,
+        forums: Forums = TestData.defaultForums,
     )
 }
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
index 4c3ab340fe68d608959058af0dd8589462b31dd8..40ce4f54b7c73f7f2364a9fcea59cea3d68fa51b 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/DeterministicTestDataCreatorImpl.kt
@@ -51,6 +51,8 @@ import org.briarproject.briar.api.avatar.AvatarMessageEncoder
 import org.briarproject.briar.api.conversation.ConversationManager
 import org.briarproject.briar.api.forum.ForumFactory
 import org.briarproject.briar.api.forum.ForumManager
+import org.briarproject.briar.api.forum.ForumPostFactory
+import org.briarproject.briar.api.identity.AuthorManager
 import org.briarproject.briar.api.messaging.MessagingManager
 import org.briarproject.briar.api.messaging.PrivateMessageFactory
 import org.briarproject.briar.api.privategroup.GroupMessageFactory
@@ -60,12 +62,13 @@ import org.briarproject.briar.api.privategroup.PrivateGroupManager
 import org.briarproject.briar.api.test.TestAvatarCreator
 import org.briarproject.briar.desktop.GroupCountHelper
 import org.briarproject.briar.desktop.attachment.media.ImageCompressor
+import org.briarproject.briar.desktop.testdata.contact.Contact
+import org.briarproject.briar.desktop.testdata.conversation.Conversations
 import org.briarproject.briar.desktop.testdata.conversation.Direction
 import org.briarproject.briar.desktop.testdata.conversation.Message
-import org.briarproject.briar.desktop.testdata.conversation.conversations
+import org.briarproject.briar.desktop.testdata.forum.Forums
 import org.briarproject.briar.desktop.testdata.forum.Post
 import org.briarproject.briar.desktop.testdata.forum.PostAuthor
-import org.briarproject.briar.desktop.testdata.forum.forums
 import org.briarproject.briar.desktop.utils.KLoggerUtils.i
 import org.briarproject.briar.desktop.utils.KLoggerUtils.w
 import java.io.IOException
@@ -80,6 +83,7 @@ import javax.inject.Inject
 import kotlin.math.min
 
 class DeterministicTestDataCreatorImpl @Inject internal constructor(
+    private val authorManager: AuthorManager,
     private val authorFactory: AuthorFactory,
     private val clock: Clock,
     private val groupFactory: GroupFactory,
@@ -92,6 +96,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     private val privateGroupFactory: PrivateGroupFactory,
     private val forumManager: ForumManager,
     private val forumFactory: ForumFactory,
+    private val forumPostFactory: ForumPostFactory,
     private val transportPropertyManager: TransportPropertyManager,
     private val conversationManager: ConversationManager,
     private val messagingManager: MessagingManager,
@@ -112,23 +117,12 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     private val localAuthors: MutableMap<ContactId, LocalAuthor> = HashMap()
 
     override fun createTestData(
-        numContacts: Int,
-        numPrivateMsgs: Int,
-        avatarPercent: Int,
-        numPrivateGroups: Int,
-        numPrivateGroupPosts: Int,
+        conversations: Conversations,
+        forums: Forums,
     ) {
-        require(numContacts != 0 || numPrivateGroups != 0)
-        require(!(avatarPercent < 0 || avatarPercent > 100))
         ioExecutor.execute {
             try {
-                createTestDataOnIoExecutor(
-                    numContacts,
-                    numPrivateMsgs,
-                    avatarPercent,
-                    numPrivateGroups,
-                    numPrivateGroupPosts
-                )
+                createTestDataOnIoExecutor(conversations, forums)
             } catch (e: DbException) {
                 LOG.w(e) { }
             }
@@ -138,35 +132,31 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
     @IoExecutor
     @Throws(DbException::class)
     private fun createTestDataOnIoExecutor(
-        numContacts: Int,
-        numPrivateMsgs: Int,
-        avatarPercent: Int,
-        numPrivateGroups: Int,
-        numPrivateGroupPosts: Int
+        conversations: Conversations,
+        forums: Forums,
     ) {
-        val contacts = createContacts(numContacts, avatarPercent)
-        createPrivateMessages(contacts, numPrivateMsgs)
+        val contacts = createContacts(conversations)
+        createPrivateMessages(contacts, conversations)
 
-        val privateGroups = createPrivateGroups(contacts, numPrivateGroups)
+        /*val privateGroups = createPrivateGroups(contacts, numPrivateGroups)
         for (privateGroup in privateGroups) {
             createRandomPrivateGroupMessages(privateGroup, contacts, numPrivateGroupPosts)
-        }
+        }*/
 
-        createForums()
+        createForums(contacts, forums)
     }
 
     @Throws(DbException::class)
-    private fun createContacts(numContacts: Int, avatarPercent: Int): List<ContactId> {
-        val contacts: MutableList<ContactId> = ArrayList(numContacts)
+    private fun createContacts(conversations: Conversations): Map<Contact, ContactId> {
+        val contacts = mutableMapOf<Contact, ContactId>()
         val localAuthor = identityManager.localAuthor
 
-        for (i in 0 until min(numContacts, conversations.persons.size)) {
-            val person = conversations.persons[i]
+        for ((person, conversation) in conversations) {
             val remote = authorFactory.createLocalAuthor(person.name)
 
-            val date = person.messages.map { it.date }.sorted().last()
-            val contact = addContact(localAuthor.id, remote, null, avatarPercent, date)
-            contacts.add(contact)
+            val date = conversation.messages.map { it.date }.maxOf { it }
+            val contact = addContact(localAuthor.id, remote, person.alias, 100, date)
+            contacts[person] = contact
         }
         return contacts
     }
@@ -331,29 +321,24 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
 
     @Throws(DbException::class)
     private fun createPrivateMessages(
-        contacts: List<ContactId>,
-        numPrivateMsgs: Int
+        contacts: Map<Contact, ContactId>,
+        conversations: Conversations,
     ) {
-        for (i in contacts.indices) {
-            val contactId = contacts[i]
-            // this cannot cause an IndexOutOfBoundsException here with conversation.persons
-            // because we already made sure to only create as many contacts as we have
-            // conversation templates available.
-            val person = conversations.persons[i]
+        for ((person, conversation) in conversations) {
+            val contactId = contacts[person] ?: error("contact for ${person.name} not created")
             val groupId = messagingManager.getConversationId(contactId)
             shareGroup(contactId, groupId)
-            for (k in 0 until min(numPrivateMsgs, person.messages.size)) {
-                createPrivateMessage(contactId, groupId, person.messages[k])
+            for (message in conversation.messages) {
+                createPrivateMessage(contactId, groupId, message)
             }
         }
-        LOG.i { "Created $numPrivateMsgs private messages per contact." }
     }
 
     @Throws(DbException::class)
     private fun createPrivateMessage(
         contactId: ContactId,
         groupId: GroupId,
-        message: Message
+        message: Message,
     ) {
         val timestamp = message.date.toEpochSecond(ZoneOffset.UTC) * 1000
         val text = message.text
@@ -371,7 +356,7 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         images: List<String>,
         timestamp: Long,
         local: Boolean,
-        autoDelete: Boolean
+        autoDelete: Boolean,
     ) {
         val timer =
             if (autoDelete) AutoDeleteConstants.MIN_AUTO_DELETE_TIMER_MS else AutoDeleteConstants.NO_AUTO_DELETE_TIMER
@@ -431,41 +416,62 @@ class DeterministicTestDataCreatorImpl @Inject internal constructor(
         return privateGroups
     }
 
-    @Throws(DbException::class)
-    private fun createRandomPrivateGroupMessages(
-        privateGroup: PrivateGroup,
-        contacts: List<ContactId>,
-        numPrivateGroupMessages: Int
+    private fun createForums(
+        contacts: Map<Contact, ContactId>,
+        forums: Forums,
     ) {
-        // TODO
-    }
-
-    private fun createForums() {
-        for (f in forums.forums) {
+        for (f in forums) {
             // create forum
             val forum = forumManager.addForum(f.name)
 
-            val members = f.members.associateWith {
-                if (it is PostAuthor.RemoteAuthor) authorFactory.createLocalAuthor(it.name)
-                else identityManager.localAuthor
+            // handle members
+            var receiveMessagesVia: ContactId? = null
+            val membersStrangers = mutableMapOf<PostAuthor.StrangerAuthor, LocalAuthor>()
+            f.members.forEach { member ->
+                when (member) {
+                    is PostAuthor.ContactAuthor -> {
+                        val contactId = contacts[member.contact] ?: error("Contact not found.")
+                        if (member.sharedWith) {
+                            shareGroup(contactId, forum.id)
+                            if (receiveMessagesVia == null) receiveMessagesVia = contactId
+                        }
+                    }
+
+                    is PostAuthor.StrangerAuthor ->
+                        membersStrangers[member] = authorFactory.createLocalAuthor(member.name)
+
+                    else -> {}
+                }
             }
-            // todo: create real contact to also share forum!
 
             // add posts
             fun addPost(post: Post, parentId: MessageId?) {
+                val author = when (post.author) {
+                    is PostAuthor.Me -> identityManager.localAuthor
+                    is PostAuthor.StrangerAuthor -> membersStrangers[post.author]
+                    is PostAuthor.ContactAuthor -> localAuthors[contacts[post.author.contact]]
+                } ?: error("LocalAuthor not found.")
                 val m = forumManager.createLocalPost(
                     forum.id,
                     post.text,
                     post.date.toEpochSecond(ZoneOffset.UTC) * 1000,
                     parentId,
-                    members[post.author]!!
+                    author
                 )
-                // todo: add non-local posts using incoming message via some contact this forum is shared with
-                forumManager.addLocalPost(m)
+                if (post.author == PostAuthor.Me)
+                    forumManager.addLocalPost(m)
+                else {
+                    val receiveVia =
+                        (if (post.author is PostAuthor.ContactAuthor) contacts[post.author.contact] else receiveMessagesVia)
+                            ?: error("Contact to receive via not found.")
+                    db.transaction<RuntimeException>(false) { txn ->
+                        db.receiveMessage(txn, receiveVia, m.message)
+                    }
+                }
                 post.replies.forEach { addPost(it, m.message.id) }
             }
             f.posts.forEach { addPost(it, null) }
         }
-        LOG.i { "Created ${forums.forums.size} forums." }
+        LOG.i { "Created ${forums.size} forums." }
     }
 }
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestData.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8c1fec307b61268f9b1d65bef27e9ed98b66eedd
--- /dev/null
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestData.kt
@@ -0,0 +1,23 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.testdata
+
+object TestData {
+    object Contacts
+}
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestDeterministicData.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestDeterministicData.kt
index 8249d89353513576ba9fa8abf2b97090e275d419..a46349ef25b10c26ea5564ba05d9561e56f6b956 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestDeterministicData.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/TestDeterministicData.kt
@@ -18,24 +18,22 @@
 
 package org.briarproject.briar.desktop.testdata
 
-import org.briarproject.briar.desktop.testdata.conversation.conversations
+import org.briarproject.briar.desktop.testdata.conversation.defaultConversations
 import org.briarproject.briar.desktop.testdata.forum.Post
-import org.briarproject.briar.desktop.testdata.forum.PostAuthor
-import org.briarproject.briar.desktop.testdata.forum.forums
+import org.briarproject.briar.desktop.testdata.forum.defaultForums
 
 fun main() {
-    for (conversation in conversations.persons) {
-        println("conversation with: ${conversation.name}") // NON-NLS
+    for ((contact, conversation) in TestData.defaultConversations) {
+        println("conversation with: ${contact.name}") // NON-NLS
         for (message in conversation.messages) {
             println("  ${message.direction} ${message.text} ${message.read} ${message.date}")
         }
     }
 
-    for (forum in forums.forums) {
+    for (forum in TestData.defaultForums) {
         println("Forum: ${forum.name}") // NON-NLS
         fun printPost(post: Post, level: Int) {
-            val name = if (post.author is PostAuthor.RemoteAuthor) post.author.name else "me"
-            println("|".repeat(level) + "+ ${post.date} - $name: ${post.text}")
+            println("|".repeat(level) + "+ ${post.date} - ${post.author.name}: ${post.text}")
             post.replies.forEach { printPost(it, level + 1) }
         }
         forum.posts.forEach { printPost(it, 0) }
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/Contact.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/Contact.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4a77b865d1563c341a4a6cb09e09f5dc47c48f47
--- /dev/null
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/Contact.kt
@@ -0,0 +1,21 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.testdata.contact
+
+data class Contact(val name: String, val alias: String?)
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactData.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactData.kt
new file mode 100644
index 0000000000000000000000000000000000000000..04811ea94c2d72368a14f601b7f91e7a679c66cd
--- /dev/null
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactData.kt
@@ -0,0 +1,34 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.testdata.contact
+
+import org.briarproject.briar.desktop.testdata.TestData
+
+val TestData.Contacts.anna: Contact
+    get() = contact { name = "Anna" }
+val TestData.Contacts.bob: Contact
+    get() = contact { name = "Bob" }
+val TestData.Contacts.chuck: Contact
+    get() = contact { name = "Chuck" }
+val TestData.Contacts.polonius: Contact
+    get() = contact { name = "Polonius" }
+val TestData.Contacts.dan: Contact
+    get() = contact { name = "Dan" }
+val TestData.Contacts.george: Contact
+    get() = contact { name = "Georgy Voronoy" }
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactDsl.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactDsl.kt
new file mode 100644
index 0000000000000000000000000000000000000000..08cd683aa4c3c3aa9854f816b54d171086320115
--- /dev/null
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/contact/ContactDsl.kt
@@ -0,0 +1,33 @@
+/*
+ * Briar Desktop
+ * Copyright (C) 2021-2022 The Briar Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package org.briarproject.briar.desktop.testdata.contact
+
+fun contact(block: ContactBuilder.() -> Unit): Contact = ContactBuilder().apply(block).build()
+
+class ContactBuilder {
+    lateinit var name: String
+    var alias: String? = null
+    // todo: support avatar
+
+    // todo: contacts are only really created if conversation is called with this contact -> change
+    fun build(): Contact {
+        check(this::name.isInitialized) { "A contact needs a name to be valid." } // NON-NLS
+        return Contact(name, alias)
+    }
+}
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/Conversations.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/Conversations.kt
index 2734f8b52b207d6014b1ea46166e0db332978668..b93a83dbce77328914b320126fd1d48912884303 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/Conversations.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/Conversations.kt
@@ -18,15 +18,13 @@
 
 package org.briarproject.briar.desktop.testdata.conversation
 
+import org.briarproject.briar.desktop.testdata.contact.Contact
 import java.time.LocalDateTime
 
-data class Conversations(
-    val persons: List<Conversation>
-)
+typealias Conversations = Map<Contact, Conversation>
 
 data class Conversation(
-    val name: String,
-    var messages: List<Message>
+    val messages: List<Message>,
 )
 
 data class Message(
@@ -34,7 +32,7 @@ data class Message(
     val images: List<String>,
     val direction: Direction,
     val date: LocalDateTime,
-    val read: Boolean
+    val read: Boolean,
 )
 
 enum class Direction {
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsData.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsData.kt
index b3d4da40ee14a77e5963eb23aa5f7462c85a1a90..d6be5f644b13b2aeec0179f7c2ac17d2f739f12b 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsData.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsData.kt
@@ -19,149 +19,158 @@
 
 package org.briarproject.briar.desktop.testdata.conversation
 
+import org.briarproject.briar.desktop.testdata.TestData
+import org.briarproject.briar.desktop.testdata.contact.anna
+import org.briarproject.briar.desktop.testdata.contact.bob
+import org.briarproject.briar.desktop.testdata.contact.chuck
+import org.briarproject.briar.desktop.testdata.contact.dan
+import org.briarproject.briar.desktop.testdata.contact.george
+import org.briarproject.briar.desktop.testdata.contact.polonius
 import java.time.LocalDateTime.of as dt
 
-val conversations = conversations {
-    conversation {
-        contactName = "Bob"
-        outgoing {
-            text = "Hi, Bob"
-            read = true
-            date = "2022-01-21 10:10:15"
-        }
-        incoming {
-            text = "What's up?"
-            read = true
-            date = "2022-01-21 10:11:05"
-        }
-        outgoing {
-            text = "Nothing much, lately. Have you seen the new Briar Desktop release?"
-            read = true
-            date = "2022-01-21 10:12:34"
-        }
-        incoming {
-            text = "Oh, wow. I have to check it out! \uD83E\uDD2F"
-            read = true
-            date = "2022-01-21 10:13:05"
-        }
-        outgoing {
-            images = listOf("images/do-it-now.png")
-            read = true
-            date = "2022-01-21 10:13:15"
-        }
-    }
-    conversation {
-        contactName = "Chuck"
-        val start = dt(2020, 1, 12, 19, 43, 17)
-        outgoing {
-            text = "Hey Chuck!"
-            read = true
-            date = start
-        }
-        incoming {
-            text = "Good evening, Alice"
-            read = true
-            date = start.plusSeconds(100)
-        }
-    }
-    conversation {
-        contactName = "Polonius"
-        val start = dt(2019, 2, 24, 22, 22)
-        incoming {
-            text = "My lord, the queen would speak with you, and presently."
-            read = true
-            date = start
-        }
-        outgoing {
-            text = "Do you see yonder cloud that's almost in shape of a camel?"
-            read = true
-            date = start.plusSeconds(100)
-        }
-        incoming {
-            text = "By the mass, and 'tis like a camel, indeed."
-            read = true
-            date = start.plusSeconds(300)
-        }
-        outgoing {
-            text = "Methinks it is like a weasel."
-            read = true
-            date = start.plusSeconds(400)
-        }
-        incoming {
-            text = "It is backed like a weasel."
-            read = true
-            date = start.plusSeconds(500)
-        }
-        outgoing {
-            text = "Or like a whale?"
-            read = true
-            date = start.plusSeconds(550)
-        }
-        incoming {
-            text = "Very like a whale."
-            read = true
-            date = start.plusSeconds(600)
-        }
-        outgoing {
-            text = "Then I will come to my mother by and by. They fool me to the top of my bent. I will come by and by."
-            read = true
-            date = start.plusSeconds(620)
-        }
-        incoming {
-            text = "I will say so."
-            read = true
-            date = start.plusSeconds(680)
-        }
-        outgoing {
-            text = "By and by is easily said."
-            read = true
-            date = start.plusSeconds(777)
-        }
-    }
-    conversation {
-        contactName = "Dan"
-        outgoing {
-            text = "Welcome to Briar!"
-            read = true
-            date = "2019-02-13 13:15:00"
-        }
-    }
-    conversation {
-        contactName = "Georgy Voronoy"
-        val start = dt(2021, 3, 13, 10, 3, 1)
-        outgoing {
-            text = "Hey Georgy!"
-            read = true
-            date = start
-        }
-        incoming {
-            text = "Good morning, Alice"
-            read = true
-            date = start.plusSeconds(100)
-        }
-        incoming {
-            text = "Check out my latest diagrams"
-            images = listOf("images/voronoi1.png")
-            read = true
-            date = start.plusSeconds(120)
-        }
-        outgoing {
-            text = "Awesome, thanks!"
-            read = true
-            date = start.plusSeconds(240)
-        }
-        outgoing {
-            text = "I've also made some"
-            images = listOf(
-                "images/voronoi2.png",
-                "images/voronoi3.png",
-                "images/voronoi4.png",
-                "images/voronoi2.png",
-                "images/voronoi3.png",
-                "images/voronoi4.png",
-            )
-            read = true
-            date = start.plusSeconds(250)
+val TestData.defaultConversations: Conversations
+    get() = conversations {
+        conversation(TestData.Contacts.anna) {
+            incoming {
+                text = "Hi!"
+            }
+        } // todo: instead create all contacts / allow contacts without messages
+        conversation(TestData.Contacts.bob) {
+            outgoing {
+                text = "Hi, Bob"
+                read = true
+                date = "2022-01-21 10:10:15"
+            }
+            incoming {
+                text = "What's up?"
+                read = true
+                date = "2022-01-21 10:11:05"
+            }
+            outgoing {
+                text = "Nothing much, lately. Have you seen the new Briar Desktop release?"
+                read = true
+                date = "2022-01-21 10:12:34"
+            }
+            incoming {
+                text = "Oh, wow. I have to check it out! \uD83E\uDD2F"
+                read = true
+                date = "2022-01-21 10:13:05"
+            }
+            outgoing {
+                images = listOf("images/do-it-now.png")
+                read = true
+                date = "2022-01-21 10:13:15"
+            }
+        }
+        conversation(TestData.Contacts.chuck) {
+            val start = dt(2020, 1, 12, 19, 43, 17)
+            outgoing {
+                text = "Hey Chuck!"
+                read = true
+                date = start
+            }
+            incoming {
+                text = "Good evening, Alice"
+                read = true
+                date = start.plusSeconds(100)
+            }
+        }
+        conversation(TestData.Contacts.polonius) {
+            val start = dt(2019, 2, 24, 22, 22)
+            incoming {
+                text = "My lord, the queen would speak with you, and presently."
+                read = true
+                date = start
+            }
+            outgoing {
+                text = "Do you see yonder cloud that's almost in shape of a camel?"
+                read = true
+                date = start.plusSeconds(100)
+            }
+            incoming {
+                text = "By the mass, and 'tis like a camel, indeed."
+                read = true
+                date = start.plusSeconds(300)
+            }
+            outgoing {
+                text = "Methinks it is like a weasel."
+                read = true
+                date = start.plusSeconds(400)
+            }
+            incoming {
+                text = "It is backed like a weasel."
+                read = true
+                date = start.plusSeconds(500)
+            }
+            outgoing {
+                text = "Or like a whale?"
+                read = true
+                date = start.plusSeconds(550)
+            }
+            incoming {
+                text = "Very like a whale."
+                read = true
+                date = start.plusSeconds(600)
+            }
+            outgoing {
+                text =
+                    "Then I will come to my mother by and by. They fool me to the top of my bent. I will come by and by."
+                read = true
+                date = start.plusSeconds(620)
+            }
+            incoming {
+                text = "I will say so."
+                read = true
+                date = start.plusSeconds(680)
+            }
+            outgoing {
+                text = "By and by is easily said."
+                read = true
+                date = start.plusSeconds(777)
+            }
+        }
+        conversation(TestData.Contacts.dan) {
+            outgoing {
+                text = "Welcome to Briar!"
+                read = true
+                date = "2019-02-13 13:15:00"
+            }
+        }
+        conversation(TestData.Contacts.george) {
+            val start = dt(2021, 3, 13, 10, 3, 1)
+            outgoing {
+                text = "Hey Georgy!"
+                read = true
+                date = start
+            }
+            incoming {
+                text = "Good morning, Alice"
+                read = true
+                date = start.plusSeconds(100)
+            }
+            incoming {
+                text = "Check out my latest diagrams"
+                images = listOf("images/voronoi1.png")
+                read = true
+                date = start.plusSeconds(120)
+            }
+            outgoing {
+                text = "Awesome, thanks!"
+                read = true
+                date = start.plusSeconds(240)
+            }
+            outgoing {
+                text = "I've also made some"
+                images = listOf(
+                    "images/voronoi2.png",
+                    "images/voronoi3.png",
+                    "images/voronoi4.png",
+                    "images/voronoi2.png",
+                    "images/voronoi3.png",
+                    "images/voronoi4.png",
+                )
+                read = true
+                date = start.plusSeconds(250)
+            }
         }
     }
-}
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsDsl.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsDsl.kt
index e27bb923cfc21eca18e1252aacd1832b4eeb3d19..58bd3575fee24262bb4d7a98f57042ca6e588a4e 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsDsl.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/conversation/ConversationsDsl.kt
@@ -18,6 +18,7 @@
 
 package org.briarproject.briar.desktop.testdata.conversation
 
+import org.briarproject.briar.desktop.testdata.contact.Contact
 import org.briarproject.briar.desktop.testdata.conversation.Direction.INCOMING
 import org.briarproject.briar.desktop.testdata.conversation.Direction.OUTGOING
 import java.time.LocalDateTime
@@ -31,20 +32,19 @@ fun conversations(block: ConversationsBuilder.() -> Unit): Conversations = Conve
 @ConversationsDsl
 class ConversationsBuilder {
 
-    private val conversations = mutableListOf<Conversation>()
+    private val conversations = mutableMapOf<Contact, Conversation>()
 
-    fun conversation(block: ConversationBuilder.() -> Unit) {
-        conversations.add(ConversationBuilder().apply(block).build())
+    fun conversation(contact: Contact, block: ConversationBuilder.() -> Unit) {
+        check(!conversations.contains(contact)) { "A contact cannot be linked to two conversations." } // NON-NLS
+        conversations[contact] = ConversationBuilder().apply(block).build()
     }
 
-    fun build(): Conversations = Conversations(conversations)
+    fun build(): Conversations = conversations
 }
 
 @ConversationsDsl
 class ConversationBuilder {
 
-    var contactName: String = ""
-
     private val messages = mutableListOf<Message>()
 
     fun incoming(block: MessageBuilder.() -> Unit) {
@@ -55,7 +55,9 @@ class ConversationBuilder {
         messages.add(MessageBuilder(OUTGOING).apply(block).build())
     }
 
-    fun build(): Conversation = Conversation(contactName, messages)
+    fun build(): Conversation {
+        return Conversation(messages)
+    }
 }
 
 var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/Forums.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/Forums.kt
index 05909ed261086eef2d026f78eb6018e4dbe8e892..f3a73a67e601892374b10a5bfe7157fc07ea24dc 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/Forums.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/Forums.kt
@@ -18,16 +18,15 @@
 
 package org.briarproject.briar.desktop.testdata.forum
 
+import org.briarproject.briar.desktop.testdata.contact.Contact
 import java.time.LocalDateTime
 
-data class Forums(
-    val forums: List<Forum>
-)
+typealias Forums = List<Forum>
 
 data class Forum(
     val name: String,
-    var members: List<PostAuthor>,
-    var posts: List<Post>,
+    val members: List<PostAuthor>,
+    val posts: List<Post>,
 )
 
 data class Post(
@@ -38,10 +37,21 @@ data class Post(
 )
 
 sealed interface PostAuthor {
-    object Me : PostAuthor
 
-    data class RemoteAuthor(
-        val name: String,
-        val sharedWith: Boolean, // todo: not supported yet
+    val name: String
+
+    object Me : PostAuthor {
+        override val name: String = "" // todo: real name
+    }
+
+    data class ContactAuthor(
+        val contact: Contact,
+        val sharedWith: Boolean,
+    ) : PostAuthor {
+        override val name: String = contact.name
+    }
+
+    data class StrangerAuthor(
+        override val name: String,
     ) : PostAuthor
 }
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsData.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsData.kt
index 8db914762cfe76d1d976c050a1dcd29313f10166..b26c88b4a4c32900a0b135f8f446c941f8fee4a7 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsData.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsData.kt
@@ -19,108 +19,113 @@
 
 package org.briarproject.briar.desktop.testdata.forum
 
+import org.briarproject.briar.desktop.testdata.TestData
+import org.briarproject.briar.desktop.testdata.contact.anna
+import org.briarproject.briar.desktop.testdata.contact.chuck
 import java.time.LocalDateTime
 
-val forums = forums {
-    forum {
-        name = "Briar Friends"
+val TestData.defaultForums: Forums
+    get() = forums {
+        forum {
+            name = "Briar Friends"
 
-        val me = myself()
-        val anna = member("Anna", sharedForum = true)
-        val paul = member("Paul")
-        val claudia = member("Claudia", sharedForum = true)
-
-        post {
-            author = anna
-            text = "Hi everybody!"
+            val me = myself()
+            val anna = memberContact(TestData.Contacts.anna)
+            val paul = memberStranger("Paul") // todo: change one more to contacts, but not shared from start
+            val claudia = memberStranger("Claudia")
 
             post {
-                author = paul
-                text = "Hey Anna! It's me, Paul \uD83D\uDC4B️"
-
-                post {
-                    author = anna
-                    text = "Hi Paul! Nice to have you around!"
-                }
+                author = anna
+                text = "Hi everybody!"
 
                 post {
                     author = paul
-                    text = "Yes indeed. Thanks for sharing the forum with me! \uD83D\uDC99\uD83E\uDD17️️"
-                }
-            }
-
-            post {
-                author = me
-                text = "Hi Anna! Where can I see who else is in here?"
+                    text = "Hey ${anna.name}! It's me, ${paul.name} \uD83D\uDC4B️"
 
-                post {
-                    author = anna
-                    text = "Right now it's only Paul, you and me. " +
-                        "But it's actually not possible to have a list of all members of a forum, " +
-                        "since every person in the forum could share it with any of their contacts."
+                    post {
+                        author = anna
+                        text = "Hi ${paul.name}! Nice to have you around!"
+                    }
 
                     post {
-                        author = me
-                        text = "Oh, that's a bit unexpected!️️"
+                        author = paul
+                        text = "Yes indeed. Thanks for sharing the forum with me! \uD83D\uDC99\uD83E\uDD17️️"
                     }
+                }
+
+                post {
+                    author = me
+                    text = "Hi ${anna.name}! Where can I see who else is in here?"
 
                     post {
-                        author = claudia
-                        text = "That's actually how Paul slipped me in here. Hello everyone!️️"
+                        author = anna
+                        text = "Right now it's only ${paul.name}, you and me. " +
+                            "But it's actually not possible to have a list of all members of a forum, " +
+                            "since every person in the forum could share it with any of their contacts."
+
+                        post {
+                            author = me
+                            text = "Oh, that's a bit unexpected!️️"
+                        }
 
                         post {
-                            author = paul
-                            text = "️️\uD83D\uDE08️"
+                            author = claudia
+                            text = "That's actually how ${paul.name} slipped me in here. Hello everyone!️️"
+
+                            post {
+                                author = paul
+                                text = "️️\uD83D\uDE08️"
+                            }
                         }
                     }
                 }
             }
-        }
 
-        post {
-            author = anna
-            text = "What do you think about Briar?"
+            post {
+                author = anna
+                text = "What do you think about Briar?"
+            }
         }
-    }
 
-    forum {
-        name = "Let's try forums"
+        forum {
+            name = "Let's try forums"
 
-        val me = myself()
-        val brian = member("Brian", sharedForum = true)
-        val claudia = member("Claudia", sharedForum = true)
-        val claudia2 = member("Claudia")
-
-        post {
-            author = brian
-            text = "I've just started and shared this forum using Briar Desktop, " +
-                "did everything work as expected?"
-            date = LocalDateTime.of(2022, 11, 28, 16, 12, 38)
+            val me = myself()
+            val chuck = memberContact(TestData.Contacts.chuck)
+            val claudia = memberStranger("Claudia")
+            val claudia2 = memberStranger("Claudia")
 
             post {
-                author = me
-                text = "Wow nice, yes, everything seems to work!"
+                author = chuck
+                text = "I've just started and shared this forum using Briar Desktop, " +
+                    "did everything work as expected?"
+                date = LocalDateTime.of(2022, 11, 28, 16, 12, 38)
 
                 post {
-                    author = brian
-                    text = "Perfect, that's amazing!"
-                }
-            }
+                    author = me
+                    text = "Wow nice, yes, everything seems to work!"
 
-            post {
-                author = claudia
-                text = "Hey people! That's so exciting that forums work on Briar Desktop now, too!"
+                    post {
+                        author = chuck
+                        text = "Perfect, that's amazing!"
+                    }
+                }
 
                 post {
-                    author = claudia2
-                    text = "And it is fully compatible with Briar Android as well! I'm writing this on my phone just now."
+                    author = claudia
+                    text = "Hey people! That's so exciting that forums work on Briar Desktop now, too!"
 
                     post {
-                        author = me
-                        text = "Oh hi, can you introduce me to your Briar account on the phone, please?"
+                        author = claudia2
+                        text =
+                            "And it is fully compatible with Briar Android as well! I'm writing this on my phone just now."
+
+                        post {
+                            author = me
+                            text = "Oh hi, can you introduce me to your Briar account on the phone, please?"
+                        }
                     }
                 }
             }
         }
     }
-}
diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsDsl.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsDsl.kt
index 44d9a52a7bfaaf519ec9313d95eb6e868d19c867..5a9552bd23a4a529c6638b58cb3a2bc289db293b 100644
--- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsDsl.kt
+++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/testdata/forum/ForumsDsl.kt
@@ -18,6 +18,7 @@
 
 package org.briarproject.briar.desktop.testdata.forum
 
+import org.briarproject.briar.desktop.testdata.contact.Contact
 import java.time.LocalDateTime
 import java.time.format.DateTimeFormatter
 import kotlin.random.Random
@@ -42,7 +43,7 @@ class ForumsBuilder {
         forums.add(ForumBuilder().apply(block).build())
     }
 
-    fun build() = Forums(forums)
+    fun build() = forums
 }
 
 @PostsDsl
@@ -54,15 +55,24 @@ class ForumBuilder : PostHierarchyBuilder() {
      */
     lateinit var name: String
 
-    private var members = mutableListOf<PostAuthor>(PostAuthor.Me)
+    private var memberList = mutableListOf<PostAuthor>(PostAuthor.Me)
     override var lastReplySent: LocalDateTime = LocalDateTime.now()
+    override val members: Set<PostAuthor>
+        get() = memberList.toSet()
 
     /**
      * Create and return a new member for this forum.
      * You can use the return value as the `author` of a [post].
      */
-    fun member(name: String, sharedForum: Boolean = false) =
-        PostAuthor.RemoteAuthor(name, sharedForum).also { members.add(it) }
+    fun memberContact(contact: Contact, sharedForum: Boolean = true) =
+        PostAuthor.ContactAuthor(contact, sharedForum).also { memberList.add(it) }
+
+    /**
+     * Create and return a new member for this forum.
+     * You can use the return value as the `author` of a [post].
+     */
+    fun memberStranger(name: String) =
+        PostAuthor.StrangerAuthor(name).also { memberList.add(it) }
 
     /**
      * Reference to the local author.
@@ -72,19 +82,26 @@ class ForumBuilder : PostHierarchyBuilder() {
 
     fun build(): Forum {
         check(this::name.isInitialized) { "A forum needs a name to be valid." } // NON-NLS
-        return Forum(name, members, posts)
+        check(memberList.isEmpty() || memberList.find { it is PostAuthor.ContactAuthor && it.sharedWith } != null) {
+            "A forum needs to have no other members or at least one member who is a contact and with whom the forum is shared." // NON-NLS
+        }
+        return Forum(name, memberList, posts)
     }
 }
 
 @PostsDsl
-class PostBuilder(parentPostSent: LocalDateTime) : PostHierarchyBuilder() {
+class PostBuilder(override val members: Set<PostAuthor>, parentPostSent: LocalDateTime) : PostHierarchyBuilder() {
 
     /**
      * The author of the post.
-     * You have to create authors using `member` in a forum.
+     * You have to create authors using `memberContact` or `memberStranger` in a forum.
      * If not set, defaults to the local author.
      */
     var author: PostAuthor = PostAuthor.Me
+        set(value) {
+            check(value is PostAuthor.Me || value in members) { "$value is not member of this forum." } // NON-NLS
+            field = value
+        }
 
     /**
      * The text of the post.
@@ -126,12 +143,13 @@ class PostBuilder(parentPostSent: LocalDateTime) : PostHierarchyBuilder() {
 abstract class PostHierarchyBuilder {
     protected val posts = mutableListOf<Post>()
     protected abstract var lastReplySent: LocalDateTime
+    protected abstract val members: Set<PostAuthor>
 
     /**
      * Add a new post to the forum or a new reply to the enclosing post.
      */
     fun post(block: PostBuilder.() -> Unit) {
-        val post = PostBuilder(lastReplySent).apply(block).build()
+        val post = PostBuilder(members, lastReplySent).apply(block).build()
         posts.add(post)
         lastReplySent = post.date
     }