diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java
index 6081f1da9b5172bc1c1a6d2e7a74c47f37b6ad27..a436f9430fc9dc9d0e6b3582b8ed87f928160220 100644
--- a/briar-android/src/org/briarproject/android/AndroidComponent.java
+++ b/briar-android/src/org/briarproject/android/AndroidComponent.java
@@ -34,6 +34,8 @@ import org.briarproject.api.messaging.MessagingManager;
 import org.briarproject.api.messaging.PrivateMessageFactory;
 import org.briarproject.api.plugins.ConnectionRegistry;
 import org.briarproject.api.plugins.PluginManager;
+import org.briarproject.api.privategroup.GroupMessageFactory;
+import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.PrivateGroupManager;
 import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
 import org.briarproject.api.settings.SettingsManager;
@@ -99,6 +101,10 @@ public interface AndroidComponent extends CoreEagerSingletons {
 
 	GroupInvitationManager groupInvitationManager();
 
+	PrivateGroupFactory privateGroupFactory();
+
+	GroupMessageFactory groupMessageFactory();
+
 	ForumManager forumManager();
 
 	ForumSharingManager forumSharingManager();
diff --git a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
index 8a6644efddaebff7b71a226c7631604e28b6f284..24b2c6fb2945f683d531d4b05d068656bcfa55b5 100644
--- a/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/conversation/GroupControllerImpl.java
@@ -15,6 +15,7 @@ import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.privategroup.GroupMessage;
+import org.briarproject.api.privategroup.GroupMessageFactory;
 import org.briarproject.api.privategroup.GroupMessageHeader;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupManager;
@@ -35,16 +36,19 @@ public class GroupControllerImpl extends
 			Logger.getLogger(GroupControllerImpl.class.getName());
 
 	private final PrivateGroupManager privateGroupManager;
+	private final GroupMessageFactory groupMessageFactory;
 
 	@Inject
 	GroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
 			LifecycleManager lifecycleManager, IdentityManager identityManager,
 			@CryptoExecutor Executor cryptoExecutor,
-			PrivateGroupManager privateGroupManager, EventBus eventBus,
+			PrivateGroupManager privateGroupManager,
+			GroupMessageFactory groupMessageFactory, EventBus eventBus,
 			AndroidNotificationManager notificationManager, Clock clock) {
 		super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
 				eventBus, notificationManager, clock);
 		this.privateGroupManager = privateGroupManager;
+		this.groupMessageFactory = groupMessageFactory;
 	}
 
 	@Override
@@ -101,8 +105,10 @@ public class GroupControllerImpl extends
 	@Override
 	protected GroupMessage createLocalMessage(String body, long timestamp,
 			@Nullable MessageId parentId, LocalAuthor author) {
-		return privateGroupManager.createLocalMessage(getGroupId(), body,
-				timestamp, parentId, author);
+		MessageId previousMsgId = null; // TODO
+		return groupMessageFactory
+				.createGroupMessage(getGroupId(), timestamp, parentId,
+						author, body, previousMsgId);
 	}
 
 	@Override
diff --git a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
index a35c2ac20435a7486ecbc1407989c3216d1b42e1..db661c43da5863f4b5a5805e7434ab88e2775e8b 100644
--- a/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/privategroup/creation/CreateGroupControllerImpl.java
@@ -3,11 +3,19 @@ package org.briarproject.android.privategroup.creation;
 import org.briarproject.android.controller.DbControllerImpl;
 import org.briarproject.android.controller.handler.ResultExceptionHandler;
 import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.crypto.CryptoExecutor;
 import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.privategroup.GroupMessage;
+import org.briarproject.api.privategroup.GroupMessageFactory;
+import org.briarproject.api.privategroup.PrivateGroup;
+import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.PrivateGroupManager;
 import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.system.Clock;
 
 import java.util.Collection;
 import java.util.concurrent.Executor;
@@ -23,25 +31,81 @@ public class CreateGroupControllerImpl extends DbControllerImpl
 	private static final Logger LOG =
 			Logger.getLogger(CreateGroupControllerImpl.class.getName());
 
+	private final IdentityManager identityManager;
+	private final PrivateGroupFactory groupFactory;
+	private final GroupMessageFactory groupMessageFactory;
 	private final PrivateGroupManager groupManager;
+	private final Clock clock;
+	@CryptoExecutor
+	private final Executor cryptoExecutor;
 
 	@Inject
 	CreateGroupControllerImpl(@DatabaseExecutor Executor dbExecutor,
-			LifecycleManager lifecycleManager,
-			PrivateGroupManager groupManager) {
+			@CryptoExecutor Executor cryptoExecutor,
+			LifecycleManager lifecycleManager, IdentityManager identityManager,
+			PrivateGroupFactory groupFactory,
+			GroupMessageFactory groupMessageFactory,
+			PrivateGroupManager groupManager, Clock clock) {
 		super(dbExecutor, lifecycleManager);
+		this.identityManager = identityManager;
+		this.groupFactory = groupFactory;
+		this.groupMessageFactory = groupMessageFactory;
 		this.groupManager = groupManager;
+		this.clock = clock;
+		this.cryptoExecutor = cryptoExecutor;
 	}
 
 	@Override
 	public void createGroup(final String name,
 			final ResultExceptionHandler<GroupId, DbException> handler) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					LocalAuthor author = identityManager.getLocalAuthor();
+					createGroupAndMessages(author, name, handler);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					handler.onException(e);
+				}
+			}
+		});
+	}
+
+	private void createGroupAndMessages(final LocalAuthor author,
+			final String name,
+			final ResultExceptionHandler<GroupId, DbException> handler) {
+		cryptoExecutor.execute(new Runnable() {
+			@Override
+			public void run() {
+				LOG.info("Creating group...");
+				PrivateGroup group =
+						groupFactory.createPrivateGroup(name, author);
+				LOG.info("Creating new member announcement...");
+				GroupMessage newMemberMsg = groupMessageFactory
+						.createNewMemberMessage(group.getId(),
+								clock.currentTimeMillis(), author, author);
+				LOG.info("Creating new join announcement...");
+				GroupMessage joinMsg = groupMessageFactory
+						.createJoinMessage(group.getId(),
+								newMemberMsg.getMessage().getTimestamp(),
+								author, newMemberMsg.getMessage().getId());
+				storeGroup(group, newMemberMsg, joinMsg, handler);
+			}
+		});
+	}
+
+	private void storeGroup(final PrivateGroup group,
+			final GroupMessage newMemberMsg, final GroupMessage joinMsg,
+			final ResultExceptionHandler<GroupId, DbException> handler) {
 		runOnDbThread(new Runnable() {
 			@Override
 			public void run() {
 				LOG.info("Adding group to database...");
 				try {
-					handler.onResult(groupManager.addPrivateGroup(name));
+					groupManager.addPrivateGroup(group, newMemberMsg, joinMsg);
+					handler.onResult(group.getId());
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
 						LOG.log(WARNING, e.toString(), e);
diff --git a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
index 80a5b44ea2ab4a968dc897dcd5d6c6f517c7b84a..4f348280edee261b2784b4256cb6c87c263a56dd 100644
--- a/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/threaded/ThreadListControllerImpl.java
@@ -44,6 +44,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
 			Logger.getLogger(ThreadListControllerImpl.class.getName());
 
 	private final IdentityManager identityManager;
+	@CryptoExecutor
 	private final Executor cryptoExecutor;
 	protected final AndroidNotificationManager notificationManager;
 	private final EventBus eventBus;
diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
index f2253c99a7c0c1999c1910146602e586ba4cf153..b01cd88c7f6d03c41d2593aebe5229aa326850fe 100644
--- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
+++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroupManager.java
@@ -3,57 +3,57 @@ package org.briarproject.api.privategroup;
 import org.briarproject.api.clients.MessageTracker;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
-import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.Collection;
 
 public interface PrivateGroupManager extends MessageTracker {
 
 	/** Returns the unique ID of the private group client. */
-	@NotNull
 	ClientId getClientId();
 
-	/** Adds a new private group. */
-	GroupId addPrivateGroup(String name) throws DbException;
+	/**
+	 * Adds a new private group and joins it.
+	 *
+	 * @param group        The private group to add
+	 * @param newMemberMsg The creator's message announcing the first new member
+	 * @param joinMsg      The first new member's join message
+	 */
+	void addPrivateGroup(PrivateGroup group, GroupMessage newMemberMsg,
+			GroupMessage joinMsg) throws DbException;
 
 	/** Removes a dissolved private group. */
 	void removePrivateGroup(GroupId g) throws DbException;
 
-	/** Creates a local group message. */
-	GroupMessage createLocalMessage(GroupId groupId, String body,
-			long timestamp, @Nullable MessageId parentId, LocalAuthor author);
+	/** Gets the MessageId of the  */
+	MessageId getPreviousMsgId(GroupId g) throws DbException;
+
+	/** Returns the timestamp of the message with the given ID */
+	long getMessageTimestamp(MessageId id) throws DbException;
 
 	/** Stores (and sends) a local group message. */
 	GroupMessageHeader addLocalMessage(GroupMessage p) throws DbException;
 
 	/** Returns the private group with the given ID. */
-	@NotNull
 	PrivateGroup getPrivateGroup(GroupId g) throws DbException;
 
 	/**
 	 * Returns the private group with the given ID within the given transaction.
 	 */
-	@NotNull
 	PrivateGroup getPrivateGroup(Transaction txn, GroupId g) throws DbException;
 
 	/** Returns all private groups the user is a member of. */
-	@NotNull
 	Collection<PrivateGroup> getPrivateGroups() throws DbException;
 
 	/** Returns true if the private group has been dissolved. */
 	boolean isDissolved(GroupId g) throws DbException;
 
 	/** Returns the body of the group message with the given ID. */
-	@NotNull
 	String getMessageBody(MessageId m) throws DbException;
 
 	/** Returns the headers of all group messages in the given group. */
-	@NotNull
 	Collection<GroupMessageHeader> getHeaders(GroupId g) throws DbException;
 
 }
diff --git a/briar-core/src/org/briarproject/privategroup/Constants.java b/briar-core/src/org/briarproject/privategroup/Constants.java
index b392abb8f181e3d35d938b6900b5fc3db3e4c8f8..0f28ce927165b868bd784867d101856f39099e26 100644
--- a/briar-core/src/org/briarproject/privategroup/Constants.java
+++ b/briar-core/src/org/briarproject/privategroup/Constants.java
@@ -11,4 +11,6 @@ interface Constants {
 	String KEY_AUTHOR_NAME = "authorName";
 	String KEY_AUTHOR_PUBLIC_KEY = "authorPublicKey";
 
+	// Messaging Group Metadata
+	String KEY_PREVIOUS_MSG_ID = "previousMsgId";
 }
diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
index ee861267e916d5f870017ae105e4b2ac1de228f6..78c2693f6968e9859afa9fdc80fb878627b89412 100644
--- a/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupManagerImpl.java
@@ -3,16 +3,15 @@ package org.briarproject.privategroup;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfEntry;
 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.identity.IdentityManager;
-import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.privategroup.GroupMessage;
-import org.briarproject.api.privategroup.GroupMessageFactory;
 import org.briarproject.api.privategroup.GroupMessageHeader;
+import org.briarproject.api.privategroup.MessageType;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.PrivateGroupManager;
@@ -21,13 +20,10 @@ 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.api.system.Clock;
 import org.briarproject.clients.BdfIncomingMessageHook;
 import org.briarproject.util.StringUtils;
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
-import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -36,6 +32,12 @@ import java.util.logging.Logger;
 import javax.inject.Inject;
 
 import static org.briarproject.api.identity.Author.Status.OURSELVES;
+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_PREVIOUS_MSG_ID;
+import static org.briarproject.privategroup.Constants.KEY_READ;
+import static org.briarproject.privategroup.Constants.KEY_TIMESTAMP;
+import static org.briarproject.privategroup.Constants.KEY_TYPE;
 
 public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		PrivateGroupManager {
@@ -46,23 +48,15 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 			StringUtils.fromHexString("5072697661746547726f75704d616e61"
 					+ "67657220627920546f727374656e2047"));
 
-	private final IdentityManager identityManager;
 	private final PrivateGroupFactory privateGroupFactory;
-	private final GroupMessageFactory groupMessageFactory;
-	private final Clock clock;
 
 	@Inject
 	PrivateGroupManagerImpl(ClientHelper clientHelper,
 			MetadataParser metadataParser, DatabaseComponent db,
-			IdentityManager identityManager,
-			PrivateGroupFactory privateGroupFactory,
-			GroupMessageFactory groupMessageFactory, Clock clock) {
+			PrivateGroupFactory privateGroupFactory) {
 		super(db, clientHelper, metadataParser);
 
-		this.identityManager = identityManager;
 		this.privateGroupFactory = privateGroupFactory;
-		this.groupMessageFactory = groupMessageFactory;
-		this.clock = clock;
 	}
 
 	@NotNull
@@ -72,36 +66,81 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 	}
 
 	@Override
-	public GroupId addPrivateGroup(String name) throws DbException {
-		PrivateGroup group;
+	public void addPrivateGroup(PrivateGroup group,
+			GroupMessage newMemberMsg, GroupMessage joinMsg)
+			throws DbException {
 		Transaction txn = db.startTransaction(false);
 		try {
-			LocalAuthor a = identityManager.getLocalAuthor(txn);
-			group = privateGroupFactory.createPrivateGroup(name, a);
 			db.addGroup(txn, group.getGroup());
+			announceNewMember(txn, newMemberMsg);
+			joinPrivateGroup(txn, joinMsg);
 			txn.setComplete();
+		} catch (FormatException e) {
+			throw new DbException(e);
 		} finally {
 			db.endTransaction(txn);
 		}
-		return group.getId();
+	}
+
+	private void announceNewMember(Transaction txn, GroupMessage m)
+			throws DbException, FormatException {
+		BdfDictionary meta = new BdfDictionary();
+		meta.put(KEY_TYPE, MessageType.NEW_MEMBER.getInt());
+		clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
+	}
+
+	private void joinPrivateGroup(Transaction txn, GroupMessage m)
+			throws DbException, FormatException {
+		BdfDictionary meta = new BdfDictionary();
+		meta.put(KEY_TYPE, MessageType.JOIN.getInt());
+		addMessageMetadata(meta, m, true);
+		clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
+		trackOutgoingMessage(txn, m.getMessage());
+		setPreviousMsgId(txn, m.getMessage().getGroupId(),
+				m.getMessage().getId());
 	}
 
 	@Override
 	public void removePrivateGroup(GroupId g) throws DbException {
-
+		// TODO
 	}
 
 	@Override
-	public GroupMessage createLocalMessage(GroupId groupId, String body,
-			long timestamp, @Nullable MessageId parentId, LocalAuthor author) {
+	public MessageId getPreviousMsgId(GroupId g) throws DbException {
+		MessageId previousMsgId;
+		Transaction txn = db.startTransaction(true);
+		try {
+			previousMsgId = getPreviousMsgId(txn, g);
+			txn.setComplete();
+		} catch (FormatException e) {
+			throw new DbException(e);
+		} finally {
+			db.endTransaction(txn);
+		}
+		return previousMsgId;
+	}
+
+	private MessageId getPreviousMsgId(Transaction txn, GroupId g)
+			throws DbException, FormatException {
+		BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
+		byte[] previousMsgIdBytes = d.getOptionalRaw(KEY_PREVIOUS_MSG_ID);
+		if (previousMsgIdBytes == null) throw new DbException();
+		return new MessageId(previousMsgIdBytes);
+	}
+
+	private void setPreviousMsgId(Transaction txn, GroupId g,
+			MessageId previousMsgId) throws DbException, FormatException {
+		BdfDictionary d = BdfDictionary
+				.of(new BdfEntry(KEY_PREVIOUS_MSG_ID, previousMsgId));
+		clientHelper.mergeGroupMetadata(txn, g, d);
+	}
+
+	public long getMessageTimestamp(MessageId id) throws DbException {
 		try {
-			return groupMessageFactory
-					.createGroupMessage(groupId, timestamp, parentId, author,
-							body);
+			BdfDictionary d = clientHelper.getMessageMetadataAsDictionary(id);
+			return d.getLong(KEY_TIMESTAMP);
 		} catch (FormatException e) {
-			throw new RuntimeException(e);
-		} catch (GeneralSecurityException e) {
-			throw new RuntimeException(e);
+			throw new DbException(e);
 		}
 	}
 
@@ -111,6 +150,8 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		Transaction txn = db.startTransaction(false);
 		try {
 			BdfDictionary meta = new BdfDictionary();
+			meta.put(KEY_TYPE, MessageType.POST.getInt());
+			addMessageMetadata(meta, m, true);
 			clientHelper.addLocalMessage(txn, m.getMessage(), meta, true);
 			trackOutgoingMessage(txn, m.getMessage());
 			txn.setComplete();
@@ -121,7 +162,7 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		}
 		return new GroupMessageHeader(m.getMessage().getGroupId(),
 				m.getMessage().getId(), m.getParent(),
-				m.getMessage().getTimestamp(), m.getAuthor(), OURSELVES, true);
+				m.getMessage().getTimestamp(), m.getMember(), OURSELVES, true);
 	}
 
 	@NotNull
@@ -198,7 +239,6 @@ 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.
@@ -207,4 +247,12 @@ public class PrivateGroupManagerImpl extends BdfIncomingMessageHook implements
 		return true;
 	}
 
+	private void addMessageMetadata(BdfDictionary meta, GroupMessage m,
+			boolean read) {
+		meta.put(KEY_TIMESTAMP, m.getMessage().getTimestamp());
+		meta.put(KEY_READ, read);
+		meta.put(KEY_AUTHOR_NAME, m.getMember().getName());
+		meta.put(KEY_AUTHOR_PUBLIC_KEY, m.getMember().getPublicKey());
+	}
+
 }