diff --git a/briar-api/src/org/briarproject/api/privategroup/MessageType.java b/briar-api/src/org/briarproject/api/privategroup/MessageType.java
new file mode 100644
index 0000000000000000000000000000000000000000..afe6a07d1c3dd17b37fe7e4498b45b3fb3e06402
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/privategroup/MessageType.java
@@ -0,0 +1,30 @@
+package org.briarproject.api.privategroup;
+
+public enum MessageType {
+	NEW_MEMBER(0),
+	JOIN(1),
+	POST(2);
+
+	int value;
+
+	MessageType(int value) {
+		this.value = value;
+	}
+
+	public static MessageType valueOf(int value) {
+		switch (value) {
+			case 0:
+				return NEW_MEMBER;
+			case 1:
+				return JOIN;
+			case 2:
+				return POST;
+			default:
+				throw new IllegalArgumentException();
+		}
+	}
+
+	public int getInt() {
+		return value;
+	}
+}
\ No newline at end of file
diff --git a/briar-core/src/org/briarproject/privategroup/Constants.java b/briar-core/src/org/briarproject/privategroup/Constants.java
index 7a1a4495bf104b7c083cf96bbb5965f20024c7c1..b392abb8f181e3d35d938b6900b5fc3db3e4c8f8 100644
--- a/briar-core/src/org/briarproject/privategroup/Constants.java
+++ b/briar-core/src/org/briarproject/privategroup/Constants.java
@@ -1,8 +1,14 @@
 package org.briarproject.privategroup;
 
+import static org.briarproject.clients.BdfConstants.MSG_KEY_READ;
+
 interface Constants {
 
 	// Database keys
-	String KEY_READ = "read";
+	String KEY_TYPE = "type";
+	String KEY_TIMESTAMP = "timestamp";
+	String KEY_READ = MSG_KEY_READ;
+	String KEY_AUTHOR_NAME = "authorName";
+	String KEY_AUTHOR_PUBLIC_KEY = "authorPublicKey";
 
 }
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index 75a8913c07a677efdeb4d5ef01cb85487bb473bf..1bd94100b04a7c1a0c198d25b2d7a7001b6f7ffc 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -3,11 +3,12 @@ package org.briarproject.privategroup;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.BdfMessageContext;
 import org.briarproject.api.clients.ClientHelper;
-import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.MetadataEncoder;
-import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.privategroup.MessageType;
+import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.InvalidMessageException;
 import org.briarproject.api.sync.Message;
@@ -15,29 +16,178 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.system.Clock;
 import org.briarproject.clients.BdfMessageValidator;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 
+import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
+import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
+import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
+import static org.briarproject.privategroup.Constants.KEY_AUTHOR_NAME;
+import static org.briarproject.privategroup.Constants.KEY_AUTHOR_PUBLIC_KEY;
+import static org.briarproject.privategroup.Constants.KEY_READ;
+import static org.briarproject.privategroup.Constants.KEY_TIMESTAMP;
+import static org.briarproject.privategroup.Constants.KEY_TYPE;
+
 class GroupMessageValidator extends BdfMessageValidator {
 
-	private final CryptoComponent crypto;
-	private final AuthorFactory authorFactory;
+	private final PrivateGroupFactory groupFactory;
 
-	GroupMessageValidator(CryptoComponent crypto, AuthorFactory authorFactory,
+	GroupMessageValidator(PrivateGroupFactory groupFactory,
 			ClientHelper clientHelper, MetadataEncoder metadataEncoder,
 			Clock clock) {
 		super(clientHelper, metadataEncoder, clock);
-		this.crypto = crypto;
-		this.authorFactory = authorFactory;
+		this.groupFactory = groupFactory;
 	}
 
 	@Override
 	protected BdfMessageContext validateMessage(Message m, Group g,
 			BdfList body) throws InvalidMessageException, FormatException {
 
+		checkSize(body, 4, 7);
+
+		// message type (int)
+		int type = body.getLong(0).intValue();
+		body.removeElementAt(0);
+
+		// member_name (string)
+		String member_name = body.getString(0);
+		checkLength(member_name, 1, MAX_AUTHOR_NAME_LENGTH);
+
+		// member_public_key (raw)
+		byte[] member_public_key = body.getRaw(1);
+		checkLength(member_public_key, 1, MAX_PUBLIC_KEY_LENGTH);
+
+		BdfMessageContext c;
+		switch (MessageType.valueOf(type)) {
+			case NEW_MEMBER:
+				c = validateNewMember(m, g, body, member_name,
+						member_public_key);
+				addMessageMetadata(c, member_name, member_public_key,
+						m.getTimestamp());
+				break;
+			case JOIN:
+				c = validateJoin(m, g, body, member_name, member_public_key);
+				addMessageMetadata(c, member_name, member_public_key,
+						m.getTimestamp());
+				break;
+			case POST:
+				c = validatePost(m, g, body, member_name, member_public_key);
+				break;
+			default:
+				throw new InvalidMessageException("Unknown Message Type");
+		}
+		c.getDictionary().put(KEY_TYPE, type);
+		return c;
+	}
+
+	private BdfMessageContext validateNewMember(Message m, Group g,
+			BdfList body, String member_name, byte[] member_public_key)
+			throws InvalidMessageException, FormatException {
+
+		// The content is a BDF list with three elements
+		checkSize(body, 3);
+
+		// signature (raw)
+		// signature with the creator's private key over a list with 4 elements
+		byte[] signature = body.getRaw(2);
+		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
+
+		// Verify Signature
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
+				member_public_key);
+		PrivateGroup group = groupFactory.parsePrivateGroup(g);
+		byte[] creatorPublicKey = group.getAuthor().getPublicKey();
+		clientHelper.verifySignature(signature, creatorPublicKey, signed);
+
+		// Return the metadata and no dependencies
+		BdfDictionary meta = new BdfDictionary();
+		return new BdfMessageContext(meta);
+	}
+
+	private BdfMessageContext validateJoin(Message m, Group g, BdfList body,
+			String member_name, byte[] member_public_key)
+			throws InvalidMessageException, FormatException {
+
+		// The content is a BDF list with four elements
+		checkSize(body, 4);
+
+		// new_member_id (raw)
+		// the identifier of a new member message
+		// with the same member_name and member_public_key
+		byte[] new_member_id = body.getRaw(2);
+		checkLength(new_member_id, MessageId.LENGTH);
+
+		// signature (raw)
+		// a signature with the member's private key over a list with 5 elements
+		byte[] signature = body.getRaw(3);
+		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
+
+		// Verify Signature
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
+				member_public_key, new_member_id);
+		clientHelper.verifySignature(signature, member_public_key, signed);
+
+		// The new member message is a dependency
+		Collection<MessageId> dependencies =
+				Collections.singleton(new MessageId(new_member_id));
+
+		// Return the metadata and dependencies
 		BdfDictionary meta = new BdfDictionary();
-		Collection<MessageId> dependencies = Collections.emptyList();
 		return new BdfMessageContext(meta, dependencies);
 	}
 
+	private BdfMessageContext validatePost(Message m, Group g, BdfList body,
+			String member_name, byte[] member_public_key)
+			throws InvalidMessageException, FormatException {
+
+		// The content is a BDF list with six elements
+		checkSize(body, 6);
+
+		// parent_id (raw or null)
+		// the identifier of the post to which this is a reply, if any
+		byte[] parent_id = body.getOptionalRaw(2);
+		if (parent_id != null) {
+			checkLength(parent_id, MessageId.LENGTH);
+		}
+
+		// previous_message_id (raw)
+		// the identifier of the member's previous post or join message
+		byte[] previous_message_id = body.getRaw(3);
+		checkLength(previous_message_id, MessageId.LENGTH);
+
+		// content (string)
+		String content = body.getString(4);
+		checkLength(content, 0, MAX_GROUP_POST_BODY_LENGTH);
+
+		// signature (raw)
+		// a signature with the member's private key over a list with 7 elements
+		byte[] signature = body.getRaw(5);
+		checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
+
+		// Verify Signature
+		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), member_name,
+				member_public_key, parent_id, previous_message_id, content);
+		clientHelper.verifySignature(signature, member_public_key, signed);
+
+		// The parent post, if any,
+		// and the member's previous message are dependencies
+		Collection<MessageId> dependencies = new ArrayList<MessageId>();
+		if (parent_id != null) dependencies.add(new MessageId(parent_id));
+		dependencies.add(new MessageId(previous_message_id));
+
+		// Return the metadata and dependencies
+		BdfDictionary meta = new BdfDictionary();
+		return new BdfMessageContext(meta, dependencies);
+	}
+
+	private void addMessageMetadata(BdfMessageContext c, String authorName,
+			byte[] pubKey, long time) {
+		c.getDictionary().put(KEY_TIMESTAMP, time);
+		c.getDictionary().put(KEY_READ, false);
+		c.getDictionary().put(KEY_AUTHOR_NAME, authorName);
+		c.getDictionary().put(KEY_AUTHOR_PUBLIC_KEY, pubKey);
+	}
+
 }
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index 05c2d4021c4c766a2cdbcb89baea7e792a594aeb..ee861267e916d5f870017ae105e4b2ac1de228f6 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -198,6 +198,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 
 		trackIncomingMessage(txn, m);
 
+
+		// TODO POST timestamp must be greater than the timestamps of the parent post, if any, and the member's previous message
+
+		// TODO JOIN timestamp must be equal to the timestamp of the new member message.
+		// TODO JOIN new_member_id must be the identifier of a NEW_MEMBER message with the same member_name and member_public_key
+
 		return true;
 	}
 
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java
index 570f697a629d952be8790dd5b27e04e1a21a2a90..55fa0894a653e4541dec26253ef99db850684608 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java
@@ -2,9 +2,7 @@ package org.briarproject.privategroup;
 
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.contact.ContactManager;
-import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.data.MetadataEncoder;
-import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.messaging.ConversationManager;
 import org.briarproject.api.privategroup.GroupMessageFactory;
@@ -59,11 +57,11 @@ public class PrivateGroupModule {
 	@Provides
 	@Singleton
 	GroupMessageValidator provideGroupMessageValidator(
-			ValidationManager validationManager, CryptoComponent crypto,
-			AuthorFactory authorFactory, ClientHelper clientHelper,
+			PrivateGroupFactory groupFactory,
+			ValidationManager validationManager, ClientHelper clientHelper,
 			MetadataEncoder metadataEncoder, Clock clock) {
-		GroupMessageValidator validator = new GroupMessageValidator(crypto,
-				authorFactory, clientHelper, metadataEncoder, clock);
+		GroupMessageValidator validator = new GroupMessageValidator(
+				groupFactory, clientHelper, metadataEncoder, clock);
 		validationManager.registerMessageValidator(
 				PrivateGroupManagerImpl.CLIENT_ID, validator);
 		return validator;