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/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index 123d8e4b16ae98100ca856778314eacfdc4c98de..3f123566f7ea4af22c31b18cd4de727850567a16 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -262,7 +262,7 @@ public interface DatabaseComponent {
 	byte[] getRawMessage(Transaction txn, MessageId m) throws DbException;
 
 	/**
-	 * Returns the metadata for all messages in the given group.
+	 * Returns the metadata for all delivered messages in the given group.
 	 * <p/>
 	 * Read-only.
 	 */
@@ -280,13 +280,22 @@ public interface DatabaseComponent {
 			Metadata query) throws DbException;
 
 	/**
-	 * Returns the metadata for the given message.
+	 * Returns the metadata for the given delivered message.
 	 * <p/>
 	 * Read-only.
 	 */
 	Metadata getMessageMetadata(Transaction txn, MessageId m)
 			throws DbException;
 
+	/**
+	 * Returns the metadata for the given delivered and pending message.
+	 * This is meant to be only used by the ValidationManager
+	 * <p/>
+	 * Read-only.
+	 */
+	Metadata getMessageMetadataForValidator(Transaction txn, MessageId m)
+			throws DbException;
+
 	/**
 	 * Returns the status of all messages in the given group with respect to
 	 * the given contact.
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/BdfIncomingMessageHook.java b/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java
index e8d0b5e2f7e8633fba85b00d79e21af9ddd0847d..f8211623d2c2012e1c4420b7760b286815a3136c 100644
--- a/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java
+++ b/briar-core/src/org/briarproject/clients/BdfIncomingMessageHook.java
@@ -12,7 +12,6 @@ import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.Transaction;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.ValidationManager.IncomingMessageHook;
-import org.briarproject.api.system.Clock;
 
 import static org.briarproject.api.clients.QueueMessage.QUEUE_MESSAGE_HEADER_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
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/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index 9779217904ba814b9aaa7deefb05edd6171300b8..a63f411cbb1fda7dad9faf094b6a8008e013d74f 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -310,7 +310,7 @@ interface Database<T> {
 			throws DbException;
 
 	/**
-	 * Returns the metadata for all messages in the given group.
+	 * Returns the metadata for all delivered messages in the given group.
 	 * <p/>
 	 * Read-only.
 	 */
@@ -327,6 +327,14 @@ interface Database<T> {
 	Map<MessageId, Metadata> getMessageMetadata(T txn, GroupId g,
 			Metadata query) throws DbException;
 
+	/**
+	 * Returns the metadata for the given delivered message.
+	 * <p/>
+	 * Read-only.
+	 */
+	Metadata getMessageMetadataForValidator(T txn, MessageId m)
+			throws DbException;
+
 	/**
 	 * Returns the metadata for the given message.
 	 * <p/>
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 4f84a04bd047e0a1054743cd7331a8e90ab7c3a0..f8eabd84c63453bfdc8bdbe6eb61acb3aec9a6a2 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -456,6 +456,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		return db.getMessageMetadata(txn, m);
 	}
 
+	public Metadata getMessageMetadataForValidator(Transaction transaction,
+			MessageId m)
+			throws DbException {
+		T txn = unbox(transaction);
+		if (!db.containsMessage(txn, m))
+			throw new NoSuchMessageException();
+		return db.getMessageMetadataForValidator(txn, m);
+	}
+
 	public Collection<MessageStatus> getMessageStatus(Transaction transaction,
 			ContactId c, GroupId g) throws DbException {
 		T txn = unbox(transaction);
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index a52fbe2a1da6c9729518c91fbb7e61cd5f39ddca..363075dfd6a6d33e67e20ff67020370b4f0ed5cc 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -1324,6 +1324,33 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public Metadata getMessageMetadataForValidator(Connection txn, MessageId m)
+			throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT key, value FROM messageMetadata AS md"
+					+ " JOIN messages AS m"
+					+ " ON m.messageId = md.messageId"
+					+ " WHERE (m.state = ? OR m.state = ?)"
+					+ " AND md.messageId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, DELIVERED.getValue());
+			ps.setInt(2, PENDING.getValue());
+			ps.setBytes(3, m.getBytes());
+			rs = ps.executeQuery();
+			Metadata metadata = new Metadata();
+			while (rs.next()) metadata.put(rs.getString(1), rs.getBytes(2));
+			rs.close();
+			ps.close();
+			return metadata;
+		} catch (SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public Collection<MessageStatus> getMessageStatus(Connection txn,
 			ContactId c, GroupId g) throws DbException {
 		PreparedStatement ps = null;
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;
 	}
 
diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
index f6aa74c3ab07fcd7bbb0ba5f211f027270980329..7853f0497d18cbb5b253a3e25ec74921f8ecfd75 100644
--- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
+++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
@@ -188,7 +188,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 						byte[] raw = db.getRawMessage(txn, id);
 						m = parseMessage(id, raw);
 						g = db.getGroup(txn, m.getGroupId());
-						meta = db.getMessageMetadata(txn, id);
+						meta = db.getMessageMetadataForValidator(txn, id);
 						txn.setComplete();
 					} finally {
 						db.endTransaction(txn);
diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
index dbf48a82fd5ef2c23b345c95c1b9e802481e7c83..f50dcb2bcb36aa3e61b9749128e2e3f61b6e1221 100644
--- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
+++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
@@ -1006,6 +1006,10 @@ public class H2DatabaseTest extends BriarTestCase {
 		map = db.getMessageMetadata(txn, groupId);
 		assertTrue(map.isEmpty());
 
+		// validator gets also metadata for pending messages
+		retrieved = db.getMessageMetadataForValidator(txn, messageId);
+		assertFalse(retrieved.isEmpty());
+
 		db.commitTransaction(txn);
 		db.close();
 	}