diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
index 7e3d83c9df06f3710fdb934af8c1a44502bba1f9..069d9473f07f7f3f2918a5ac68944986efd447c2 100644
--- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
+++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java
@@ -21,18 +21,17 @@ import org.briarproject.api.db.DbException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.ForumInvitationReceivedEvent;
+import org.briarproject.api.event.ForumPostReceivedEvent;
 import org.briarproject.api.event.IntroductionRequestReceivedEvent;
 import org.briarproject.api.event.IntroductionResponseReceivedEvent;
 import org.briarproject.api.event.IntroductionSucceededEvent;
-import org.briarproject.api.event.MessageStateChangedEvent;
+import org.briarproject.api.event.PrivateMessageReceivedEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
-import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.lifecycle.Service;
 import org.briarproject.api.lifecycle.ServiceException;
 import org.briarproject.api.messaging.MessagingManager;
 import org.briarproject.api.settings.Settings;
 import org.briarproject.api.settings.SettingsManager;
-import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.util.StringUtils;
 
@@ -59,7 +58,6 @@ import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.android.BriarActivity.GROUP_ID;
 import static org.briarproject.android.fragment.SettingsFragment.SETTINGS_NAMESPACE;
-import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
 
 class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		Service, EventListener {
@@ -78,7 +76,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	private final Executor dbExecutor;
 	private final SettingsManager settingsManager;
 	private final MessagingManager messagingManager;
-	private final ForumManager forumManager;
 	private final AndroidExecutor androidExecutor;
 	private final Context appContext;
 
@@ -94,14 +91,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 	private volatile Settings settings = new Settings();
 
 	@Inject
-	public AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
+	AndroidNotificationManagerImpl(@DatabaseExecutor Executor dbExecutor,
 			SettingsManager settingsManager, MessagingManager messagingManager,
-			ForumManager forumManager, AndroidExecutor androidExecutor,
-			Application app) {
+			AndroidExecutor androidExecutor, Application app) {
 		this.dbExecutor = dbExecutor;
 		this.settingsManager = settingsManager;
 		this.messagingManager = messagingManager;
-		this.forumManager = forumManager;
 		this.androidExecutor = androidExecutor;
 		appContext = app.getApplicationContext();
 	}
@@ -157,15 +152,12 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
 		if (e instanceof SettingsUpdatedEvent) {
 			SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
 			if (s.getNamespace().equals(SETTINGS_NAMESPACE)) loadSettings();
-		} else if (e instanceof MessageStateChangedEvent) {
-			MessageStateChangedEvent m = (MessageStateChangedEvent) e;
-			if (!m.isLocal() && m.getState() == DELIVERED) {
-				ClientId c = m.getClientId();
-				if (c.equals(messagingManager.getClientId()))
-					showPrivateMessageNotification(m.getMessage().getGroupId());
-				else if (c.equals(forumManager.getClientId()))
-					showForumPostNotification(m.getMessage().getGroupId());
-			}
+		} else if (e instanceof PrivateMessageReceivedEvent) {
+			PrivateMessageReceivedEvent m = (PrivateMessageReceivedEvent) e;
+			showPrivateMessageNotification(m.getGroupId());
+		} else if (e instanceof ForumPostReceivedEvent) {
+			ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
+			showForumPostNotification(m.getGroupId());
 		} else if (e instanceof IntroductionRequestReceivedEvent) {
 			ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
 			showNotificationForPrivateConversation(c);
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 0fd7ab99244f8d20f3d9a60de6bd9afdff8db4e2..77f574f20f4a2a18f4471c01e0d9e1e63006fd06 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -65,7 +65,6 @@ public class ForumActivity extends BriarActivity implements
 	private static final Logger LOG =
 			Logger.getLogger(ForumActivity.class.getName());
 
-	public static final String MIN_TIMESTAMP = "briar.MIN_TIMESTAMP";
 	static final String FORUM_NAME = "briar.FORUM_NAME";
 	private static final int REQUEST_FORUM_SHARED = 3;
 
@@ -341,7 +340,6 @@ public class ForumActivity extends BriarActivity implements
 		final View chevron, replyButton;
 		final ViewGroup cell;
 		final View topDivider;
-		public ValueAnimator highlightAnimator;
 
 		ForumViewHolder(View v) {
 			super(v);
@@ -438,7 +436,7 @@ public class ForumActivity extends BriarActivity implements
 			// TODO This loop doesn't really loop. @ernir please review!
 			for (int i = visiblePos + 1; i < getItemCount(); i++) {
 				ForumEntry entry = getVisibleEntry(i);
-				if (entry.getLevel() <= levelLimit)
+				if (entry != null && entry.getLevel() <= levelLimit)
 					break;
 				return true;
 			}
@@ -486,7 +484,7 @@ public class ForumActivity extends BriarActivity implements
 
 			for (int i = pos + 1; i < getItemCount(); i++) {
 				ForumEntry entry = getVisibleEntry(i);
-				if (entry.getLevel() > levelLimit) {
+				if (entry != null && entry.getLevel() > levelLimit) {
 					indexList.add(i);
 				} else {
 					break;
@@ -613,6 +611,8 @@ public class ForumActivity extends BriarActivity implements
 		public void onBindViewHolder(
 				final ForumViewHolder ui, final int position) {
 			final ForumEntry data = getVisibleEntry(position);
+			if (data == null) return;
+
 			if (!data.isRead()) {
 				data.setRead(true);
 				forumController.entryRead(data);
diff --git a/briar-android/src/org/briarproject/android/forum/ForumController.java b/briar-android/src/org/briarproject/android/forum/ForumController.java
index 2fa4f0dab0c58cbcd7017f717a44468f97bf7317..a3419b812f84edf39839f587186c2bcb2adff167 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumController.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumController.java
@@ -19,7 +19,7 @@ public interface ForumController extends ActivityLifecycleController {
 	void createPost(byte[] body);
 	void createPost(byte[] body, MessageId parentId);
 
-	public interface ForumPostListener {
+	interface ForumPostListener {
 		void addLocalEntry(int index, ForumEntry entry);
 		void addForeignEntry(int index, ForumEntry entry);
 	}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
index 29ee3609e922ca6f91da3494ba94aebe87db2e2a..b5e83f8f4fa1c317eefc0130c2aed156e0cc2f87 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumControllerImpl.java
@@ -13,12 +13,13 @@ import org.briarproject.api.db.DbException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.ForumPostReceivedEvent;
 import org.briarproject.api.event.GroupRemovedEvent;
-import org.briarproject.api.event.MessageStateChangedEvent;
 import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPost;
 import org.briarproject.api.forum.ForumPostFactory;
 import org.briarproject.api.forum.ForumPostHeader;
+import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.GroupId;
@@ -38,7 +39,7 @@ import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
+import static org.briarproject.api.identity.Author.Status.VERIFIED;
 
 public class ForumControllerImpl extends DbControllerImpl
 		implements ForumController, EventListener {
@@ -52,7 +53,7 @@ public class ForumControllerImpl extends DbControllerImpl
 	@CryptoExecutor
 	protected Executor cryptoExecutor;
 	@Inject
-	protected volatile ForumPostFactory forumPostFactory;
+	volatile ForumPostFactory forumPostFactory;
 	@Inject
 	protected volatile CryptoComponent crypto;
 	@Inject
@@ -65,7 +66,6 @@ public class ForumControllerImpl extends DbControllerImpl
 	protected ForumPersistentData data;
 
 	private ForumPostListener listener;
-	private MessageId localAdd = null;
 
 	@Inject
 	ForumControllerImpl() {
@@ -100,51 +100,13 @@ public class ForumControllerImpl extends DbControllerImpl
 		}
 	}
 
-	private void findSingleNewEntry() {
-		runOnDbThread(new Runnable() {
-			@Override
-			public void run() {
-				List<ForumEntry> oldEntries = getForumEntries();
-				data.clearHeaders();
-				try {
-					loadPosts();
-					List<ForumEntry> allEntries = getForumEntries();
-					int i = 0;
-					for (ForumEntry entry : allEntries) {
-						boolean isNew = true;
-						for (ForumEntry oldEntry : oldEntries) {
-							if (entry.getMessageId()
-									.equals(oldEntry.getMessageId())) {
-								isNew = false;
-								break;
-							}
-						}
-						if (isNew) {
-							if (localAdd != null &&
-									entry.getMessageId().equals(localAdd)) {
-								addLocalEntry(i, entry);
-							} else {
-								addForeignEntry(i, entry);
-							}
-							break;
-						}
-						i++;
-					}
-				} catch (DbException e) {
-					e.printStackTrace();
-				}
-			}
-		});
-	}
-
 	@Override
 	public void eventOccurred(Event e) {
-		if (e instanceof MessageStateChangedEvent) {
-			MessageStateChangedEvent m = (MessageStateChangedEvent) e;
-			if (m.getState() == DELIVERED &&
-					m.getMessage().getGroupId().equals(data.getGroupId())) {
-				LOG.info("Message added, reloading");
-				findSingleNewEntry();
+		if (e instanceof ForumPostReceivedEvent) {
+			ForumPostReceivedEvent pe = (ForumPostReceivedEvent) e;
+			if (pe.getGroupId().equals(data.getGroupId())) {
+				LOG.info("Forum Post received, adding...");
+				addNewPost(pe.getForumPostHeader());
 			}
 		} else if (e instanceof GroupRemovedEvent) {
 			GroupRemovedEvent s = (GroupRemovedEvent) e;
@@ -160,6 +122,40 @@ public class ForumControllerImpl extends DbControllerImpl
 		}
 	}
 
+	private void addNewPost(final ForumPostHeader h) {
+		if (data == null) return;
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				data.addHeader(h);
+				data.clearForumEntries();
+
+				try {
+					byte[] body = forumManager.getPostBody(h.getId());
+					data.addBody(h.getId(), body);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+				}
+
+				Author a = data.getLocalAuthor();
+				// FIXME we should not need to calculate the index here
+				//       the index is essentially stored in two different locations
+				int i = 0;
+				for (ForumEntry entry : getForumEntries()) {
+					if (entry.getMessageId().equals(h.getId())) {
+						if (a != null && a.equals(h.getAuthor())) {
+							addLocalEntry(i, entry);
+						} else {
+							addForeignEntry(i, entry);
+						}
+					}
+					i++;
+				}
+			}
+		});
+	}
+
 	private void loadAuthor() throws DbException {
 		Collection<LocalAuthor> localAuthors =
 				identityManager.getLocalAuthors();
@@ -343,6 +339,7 @@ public class ForumControllerImpl extends DbControllerImpl
 					throw new RuntimeException(e);
 				}
 				storePost(p);
+				addNewPost(p);
 			}
 		});
 	}
@@ -369,7 +366,6 @@ public class ForumControllerImpl extends DbControllerImpl
 		runOnDbThread(new Runnable() {
 			public void run() {
 				try {
-					localAdd = p.getMessage().getId();
 					long now = System.currentTimeMillis();
 					forumManager.addLocalPost(p);
 					long duration = System.currentTimeMillis() - now;
@@ -384,4 +380,12 @@ public class ForumControllerImpl extends DbControllerImpl
 		});
 	}
 
+	private void addNewPost(final ForumPost p) {
+		ForumPostHeader h =
+				new ForumPostHeader(p.getMessage().getId(), p.getParent(),
+						p.getMessage().getTimestamp(), p.getAuthor(), VERIFIED,
+						p.getContentType(), false);
+		addNewPost(h);
+	}
+
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ForumEntry.java b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
index a24d94bbcf457d57eb5dc140878895f24c734a28..b3f2aed40ba472efd6256072799288f36525b61c 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumEntry.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
@@ -17,7 +17,7 @@ public class ForumEntry {
 	private boolean isShowingDescendants = true;
 	private boolean isRead = true;
 
-	public ForumEntry(ForumPostHeader h, String text, int level) {
+	ForumEntry(ForumPostHeader h, String text, int level) {
 		this(h.getId(), text, level, h.getTimestamp(), h.getAuthor().getName(),
 				h.getAuthor().getId(), h.getAuthorStatus());
 		this.isRead = h.isRead();
@@ -50,7 +50,7 @@ public class ForumEntry {
 		return author;
 	}
 
-	public AuthorId getAuthorId() {
+	AuthorId getAuthorId() {
 		return authorId;
 	}
 
@@ -58,15 +58,15 @@ public class ForumEntry {
 		return status;
 	}
 
-	public boolean isShowingDescendants() {
+	boolean isShowingDescendants() {
 		return isShowingDescendants;
 	}
 
-	public void setShowingDescendants(boolean showingDescendants) {
+	void setShowingDescendants(boolean showingDescendants) {
 		this.isShowingDescendants = showingDescendants;
 	}
 
-	public MessageId getMessageId() {
+	MessageId getMessageId() {
 		return messageId;
 	}
 
@@ -74,7 +74,7 @@ public class ForumEntry {
 		return isRead;
 	}
 
-	public void setRead(boolean read) {
+	void setRead(boolean read) {
 		isRead = read;
 	}
 }
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index c3dc8ebe40e82cc5c07f81038f33032704b8c0b9..1916bce322bd3fbc27663b28aadbf2d07f5f6fc5 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -22,14 +22,13 @@ import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.event.ContactRemovedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.ForumInvitationReceivedEvent;
+import org.briarproject.api.event.ForumPostReceivedEvent;
 import org.briarproject.api.event.GroupAddedEvent;
 import org.briarproject.api.event.GroupRemovedEvent;
-import org.briarproject.api.event.MessageStateChangedEvent;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.forum.ForumSharingManager;
-import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.GroupId;
 
 import java.util.ArrayList;
@@ -41,7 +40,6 @@ import javax.inject.Inject;
 import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
-import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
 
 public class ForumListFragment extends BaseEventFragment implements
 		View.OnClickListener {
@@ -234,14 +232,10 @@ public class ForumListFragment extends BaseEventFragment implements
 				LOG.info("Forum removed, removing from list");
 				removeForum(g.getGroup().getId());
 			}
-		} else if (e instanceof MessageStateChangedEvent) {
-			MessageStateChangedEvent m = (MessageStateChangedEvent) e;
-			ClientId c = m.getClientId();
-			if (m.getState() == DELIVERED &&
-					c.equals(forumManager.getClientId())) {
-				LOG.info("Forum post added, reloading");
-				loadForumHeaders(m.getMessage().getGroupId());
-			}
+		} else if (e instanceof ForumPostReceivedEvent) {
+			ForumPostReceivedEvent m = (ForumPostReceivedEvent) e;
+			LOG.info("Forum post added, reloading");
+			loadForumHeaders(m.getGroupId());
 		} else if (e instanceof ForumInvitationReceivedEvent) {
 			loadAvailableForums();
 		}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java b/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java
index 19aaa33f071b44b42134c48dfe4c642b15f57514..6464be24ead03476e301674648cee386da43b0df 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumPersistentData.java
@@ -12,7 +12,6 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.logging.Logger;
 
 /**
  * This class is a singleton that defines the data that should persist, i.e.
@@ -21,7 +20,7 @@ import java.util.logging.Logger;
  */
 public class ForumPersistentData {
 
-	protected volatile MessageTree<ForumPostHeader> tree =
+	private volatile MessageTree<ForumPostHeader> tree =
 			new MessageTreeImpl<>();
 	private volatile Map<MessageId, byte[]> bodyCache = new HashMap<>();
 	private volatile LocalAuthor localAuthor;
@@ -29,36 +28,36 @@ public class ForumPersistentData {
 	private volatile GroupId groupId;
 	private List<ForumEntry> forumEntries;
 
-	private static final Logger LOG =
-			Logger.getLogger(ForumControllerImpl.class.getName());
-
-
-	public void clearAll() {
-		clearHeaders();
+	void clearAll() {
+		clearForumEntries();
+		tree.clear();
 		bodyCache.clear();
 		localAuthor = null;
 		forum = null;
 		groupId = null;
 	}
 
-	public void clearHeaders() {
-		tree.clear();
+	void clearForumEntries() {
 		forumEntries = null;
 	}
 
-	public void addHeaders(Collection<ForumPostHeader> headers) {
+	void addHeaders(Collection<ForumPostHeader> headers) {
 		tree.add(headers);
 	}
 
+	void addHeader(ForumPostHeader header) {
+		tree.add(header);
+	}
+
 	public Collection<ForumPostHeader> getHeaders() {
 		return tree.depthFirstOrder();
 	}
 
-	public void addBody(MessageId messageId, byte[] body) {
+	void addBody(MessageId messageId, byte[] body) {
 		bodyCache.put(messageId, body);
 	}
 
-	public byte[] getBody(MessageId messageId) {
+	byte[] getBody(MessageId messageId) {
 		return bodyCache.get(messageId);
 	}
 
@@ -87,11 +86,11 @@ public class ForumPersistentData {
 		this.groupId = groupId;
 	}
 
-	public List<ForumEntry> getForumEntries() {
+	List<ForumEntry> getForumEntries() {
 		return forumEntries;
 	}
 
-	public void setForumEntries(List<ForumEntry> forumEntries) {
+	void setForumEntries(List<ForumEntry> forumEntries) {
 		this.forumEntries = forumEntries;
 	}
 }
diff --git a/briar-api/src/org/briarproject/api/clients/MessageTree.java b/briar-api/src/org/briarproject/api/clients/MessageTree.java
index 500db6a583435f5371ff4e403b7b37778a7f3098..1c0c92e9feb378353fe96796dc6cb9a49257f418 100644
--- a/briar-api/src/org/briarproject/api/clients/MessageTree.java
+++ b/briar-api/src/org/briarproject/api/clients/MessageTree.java
@@ -8,9 +8,10 @@ import java.util.Comparator;
 public interface MessageTree<T extends MessageTree.MessageNode> {
 
 	void add(Collection<T> nodes);
+	void add(T node);
+	void setComparator(Comparator<T> comparator);
 	void clear();
 	Collection<T> depthFirstOrder();
-	void setComparator(Comparator<T> comparator);
 
 	interface MessageNode {
 		MessageId getId();
diff --git a/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java b/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..b300f9ee276f063e6dd4c63de6e607b05907ea30
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/event/ForumPostReceivedEvent.java
@@ -0,0 +1,29 @@
+package org.briarproject.api.event;
+
+import org.briarproject.api.forum.ForumPostHeader;
+import org.briarproject.api.messaging.PrivateMessageHeader;
+import org.briarproject.api.sync.GroupId;
+
+/**
+ * An event that is broadcast when a new forum post was received.
+ */
+public class ForumPostReceivedEvent extends Event {
+
+	private final ForumPostHeader forumPostHeader;
+	private final GroupId groupId;
+
+	public ForumPostReceivedEvent(ForumPostHeader forumPostHeader,
+			GroupId groupId) {
+
+		this.forumPostHeader = forumPostHeader;
+		this.groupId = groupId;
+	}
+
+	public ForumPostHeader getForumPostHeader() {
+		return forumPostHeader;
+	}
+
+	public GroupId getGroupId() {
+		return groupId;
+	}
+}
diff --git a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
index 4759f39fdb9ec291ef08f9912b6048caba7067c6..58e5c65a47ebd6f0498929f184684bbcfb7c8ffb 100644
--- a/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
+++ b/briar-core/src/org/briarproject/clients/MessageTreeImpl.java
@@ -14,8 +14,9 @@ import java.util.Map;
 public class MessageTreeImpl<T extends MessageTree.MessageNode>
 		implements MessageTree<T> {
 
-	Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>();
-	List<T> roots = new ArrayList<T>();
+	private final Map<MessageId, List<T>> nodeMap = new HashMap<MessageId, List<T>>();
+	private final List<T> roots = new ArrayList<T>();
+	private final List<List<T>> unsortedLists = new ArrayList<List<T>>();
 
 	private Comparator<T> comparator = new Comparator<T>() {
 		@Override
@@ -38,26 +39,44 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 		}
 		// parse the nodes for dependencies
 		for (T node : nodes) {
-			if (node.getParentId() == null) {
-				roots.add(node);
-			}
-			else {
-				// retrieve the parent's children
-				List<T> pChildren = nodeMap.get(node.getParentId());
-				pChildren.add(node);
-			}
+			parseNode(node);
 		}
-		sortAll();
+		sortUnsorted();
 	}
 
-	private void sortAll() {
-		Collections.sort(roots, comparator);
-		// Sort all the sub-lists
-		for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) {
-			Collections.sort(entry.getValue(), comparator);
+	@Override
+	public void add(T node) {
+		add(Collections.singletonList(node));
+	}
+
+	private void markAsUnsorted(List<T> list) {
+		if (!unsortedLists.contains(list))
+			unsortedLists.add(list);
+	}
+
+	private void parseNode(T node) {
+		if (node.getParentId() == null) {
+			roots.add(node);
+			markAsUnsorted(roots);
+		} else {
+			// retrieve the parent's children
+			List<T> pChildren = nodeMap.get(node.getParentId());
+			pChildren.add(node);
+			markAsUnsorted(pChildren);
 		}
 	}
 
+	private void sortUnsorted() {
+		// leave unsorted if there is no comparator
+		if (comparator != null) {
+			for (List<T> list : unsortedLists) {
+				Collections.sort(list, comparator);
+			}
+			unsortedLists.clear();
+		}
+	}
+
+
 	private void traverse(List<T> list, T node) {
 		list.add(node);
 		for (T child : nodeMap.get(node.getId())) {
@@ -65,6 +84,16 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 		}
 	}
 
+	@Override
+	public void setComparator(Comparator<T> comparator) {
+		this.comparator = comparator;
+		// Sort all lists with the new comparator
+		Collections.sort(roots, comparator);
+		for (Map.Entry<MessageId, List<T>> entry: nodeMap.entrySet()) {
+			Collections.sort(entry.getValue(), comparator);
+		}
+	}
+
 	@Override
 	public Collection<T> depthFirstOrder() {
 		List<T> orderedList = new ArrayList<T>();
@@ -74,9 +103,4 @@ public class MessageTreeImpl<T extends MessageTree.MessageNode>
 		return Collections.unmodifiableList(orderedList);
 	}
 
-	@Override
-	public void setComparator(Comparator<T> comparator) {
-		this.comparator = comparator;
-	}
-
 }
diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
index 36f8357bafdfecf063c21a8f270d91271ad24b98..2574ee80da3fd3e05bc0669f30e456a784328643 100644
--- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
@@ -4,9 +4,11 @@ import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
+import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.event.ForumPostReceivedEvent;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumFactory;
 import org.briarproject.api.forum.ForumManager;
@@ -19,7 +21,9 @@ import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
+import org.briarproject.clients.BdfIncomingMessageHook;
 import org.briarproject.util.StringUtils;
 
 import java.util.ArrayList;
@@ -44,7 +48,7 @@ import static org.briarproject.api.forum.ForumConstants.KEY_READ;
 import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP;
 import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
 
-class ForumManagerImpl implements ForumManager {
+class ForumManagerImpl extends BdfIncomingMessageHook implements ForumManager {
 
 	private static final Logger LOG =
 			Logger.getLogger(ForumManagerImpl.class.getName());
@@ -55,21 +59,31 @@ class ForumManagerImpl implements ForumManager {
 
 	private final DatabaseComponent db;
 	private final IdentityManager identityManager;
-	private final ClientHelper clientHelper;
 	private final ForumFactory forumFactory;
 	private final List<RemoveForumHook> removeHooks;
 
 	@Inject
 	ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
-			ClientHelper clientHelper, ForumFactory forumFactory) {
+			ClientHelper clientHelper, MetadataParser metadataParser,
+			ForumFactory forumFactory) {
 
+		super(clientHelper, metadataParser);
 		this.db = db;
 		this.identityManager = identityManager;
-		this.clientHelper = clientHelper;
 		this.forumFactory = forumFactory;
 		removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
 	}
 
+	@Override
+	protected void incomingMessage(Transaction txn, Message m, BdfList body,
+			BdfDictionary meta) throws DbException, FormatException {
+
+		ForumPostHeader post = getForumPostHeader(txn, m.getId(), meta);
+		ForumPostReceivedEvent event =
+				new ForumPostReceivedEvent(post, m.getGroupId());
+		txn.attach(event);
+	}
+
 	@Override
 	public ClientId getClientId() {
 		return CLIENT_ID;
@@ -193,25 +207,7 @@ class ForumManagerImpl implements ForumManager {
 		for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
 			try {
 				BdfDictionary meta = entry.getValue();
-				long timestamp = meta.getLong(KEY_TIMESTAMP);
-				Author author = null;
-				Status authorStatus = ANONYMOUS;
-				MessageId parentId = null;
-				if (meta.containsKey(KEY_PARENT))
-					parentId = new MessageId(meta.getRaw(KEY_PARENT));
-				BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null);
-				if (d1 != null) {
-					AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID));
-					String name = d1.getString(KEY_NAME);
-					byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
-					author = new Author(authorId, name, publicKey);
-					authorStatus =
-							identityManager.getAuthorStatus(author.getId());
-				}
-				String contentType = meta.getString(KEY_CONTENT_TYPE);
-				boolean read = meta.getBoolean(KEY_READ);
-				headers.add(new ForumPostHeader(entry.getKey(), parentId,
-						timestamp, author, authorStatus, contentType, read));
+				headers.add(getForumPostHeader(entry.getKey(), meta));
 			} catch (FormatException e) {
 				throw new DbException(e);
 			}
@@ -242,4 +238,39 @@ class ForumManagerImpl implements ForumManager {
 		return new Forum(g, forum.getString(0), forum.getRaw(1));
 	}
 
+	private ForumPostHeader getForumPostHeader(Transaction txn, MessageId id,
+			BdfDictionary meta) throws DbException, FormatException {
+
+		long timestamp = meta.getLong(KEY_TIMESTAMP);
+		Author author = null;
+		Status authorStatus = ANONYMOUS;
+		MessageId parentId = null;
+		if (meta.containsKey(KEY_PARENT))
+			parentId = new MessageId(meta.getRaw(KEY_PARENT));
+		BdfDictionary d1 = meta.getDictionary(KEY_AUTHOR, null);
+		if (d1 != null) {
+			AuthorId authorId = new AuthorId(d1.getRaw(KEY_ID));
+			String name = d1.getString(KEY_NAME);
+			byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
+			author = new Author(authorId, name, publicKey);
+			if (txn == null) {
+				authorStatus = identityManager.getAuthorStatus(author.getId());
+			} else {
+				authorStatus =
+						identityManager.getAuthorStatus(txn, author.getId());
+			}
+		}
+		String contentType = meta.getString(KEY_CONTENT_TYPE);
+		boolean read = meta.getBoolean(KEY_READ);
+
+		return new ForumPostHeader(id, parentId, timestamp, author,
+				authorStatus, contentType, read);
+	}
+
+	private ForumPostHeader getForumPostHeader(MessageId id,
+			BdfDictionary meta) throws DbException, FormatException {
+
+		return getForumPostHeader(null, id, meta);
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/forum/ForumModule.java b/briar-core/src/org/briarproject/forum/ForumModule.java
index 8c84509b143bf3c45a082b9829d3acdb9e016fa3..40bfd01381fe8b1972fb253ec389c7b18107e006 100644
--- a/briar-core/src/org/briarproject/forum/ForumModule.java
+++ b/briar-core/src/org/briarproject/forum/ForumModule.java
@@ -29,7 +29,13 @@ public class ForumModule {
 
 	@Provides
 	@Singleton
-	ForumManager provideForumManager(ForumManagerImpl forumManager) {
+	ForumManager provideForumManager(ForumManagerImpl forumManager,
+			ValidationManager validationManager) {
+
+		validationManager
+				.registerIncomingMessageHook(forumManager.getClientId(),
+						forumManager);
+
 		return forumManager;
 	}