diff --git a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java
index c8cd4b051ae45aa570bf62ad79e85bdb59350e92..90d52d1ba3e8b8b02f5df5d8b0f8305bfadbd919 100644
--- a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java
@@ -13,13 +13,12 @@ import org.briarproject.android.util.ListLoadingProgressBar;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.db.DbException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
-import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
-import org.briarproject.api.event.SubscriptionAddedEvent;
-import org.briarproject.api.event.SubscriptionRemovedEvent;
+import org.briarproject.api.event.GroupAddedEvent;
+import org.briarproject.api.event.GroupRemovedEvent;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.sync.GroupId;
@@ -83,7 +82,7 @@ implements EventListener, OnItemClickListener {
 							Collection<Contact> c =
 									forumManager.getSubscribers(id);
 							available.add(new ForumContacts(f, c));
-						} catch (NoSuchSubscriptionException e) {
+						} catch (NoSuchGroupException e) {
 							// Continue
 						}
 					}
@@ -123,15 +122,19 @@ implements EventListener, OnItemClickListener {
 	}
 
 	public void eventOccurred(Event e) {
-		if (e instanceof RemoteSubscriptionsUpdatedEvent) {
-			LOG.info("Remote subscriptions changed, reloading");
-			loadForums();
-		} else if (e instanceof SubscriptionAddedEvent) {
-			LOG.info("Subscription added, reloading");
-			loadForums();
-		} else if (e instanceof SubscriptionRemovedEvent) {
-			LOG.info("Subscription removed, reloading");
-			loadForums();
+		// TODO: What other events are needed here?
+		if (e instanceof GroupAddedEvent) {
+			GroupAddedEvent g = (GroupAddedEvent) e;
+			if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
+				LOG.info("Forum added, reloading");
+				loadForums();
+			}
+		} else if (e instanceof GroupRemovedEvent) {
+			GroupRemovedEvent g = (GroupRemovedEvent) e;
+			if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
+				LOG.info("Forum removed, reloading");
+				loadForums();
+			}
 		}
 	}
 
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index c15d2667d9004d934987c8a10d1a1fbfb422592f..943db626978ece7bc94611220c5282c40d15a8ca 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -19,13 +19,13 @@ import org.briarproject.android.util.HorizontalBorder;
 import org.briarproject.android.util.ListLoadingProgressBar;
 import org.briarproject.api.android.AndroidNotificationManager;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.db.NoSuchMessageException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.GroupRemovedEvent;
 import org.briarproject.api.event.MessageValidatedEvent;
-import org.briarproject.api.event.SubscriptionRemovedEvent;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPostHeader;
@@ -159,7 +159,7 @@ public class ForumActivity extends BriarActivity implements EventListener,
 					if (LOG.isLoggable(INFO))
 						LOG.info("Loading forum " + duration + " ms");
 					displayForumName();
-				} catch (NoSuchSubscriptionException e) {
+				} catch (NoSuchGroupException e) {
 					finishOnUiThread();
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
@@ -188,7 +188,7 @@ public class ForumActivity extends BriarActivity implements EventListener,
 					if (LOG.isLoggable(INFO))
 						LOG.info("Load took " + duration + " ms");
 					displayHeaders(headers);
-				} catch (NoSuchSubscriptionException e) {
+				} catch (NoSuchGroupException e) {
 					finishOnUiThread();
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
@@ -319,10 +319,10 @@ public class ForumActivity extends BriarActivity implements EventListener,
 				LOG.info("Message added, reloading");
 				loadHeaders();
 			}
-		} else if (e instanceof SubscriptionRemovedEvent) {
-			SubscriptionRemovedEvent s = (SubscriptionRemovedEvent) e;
+		} else if (e instanceof GroupRemovedEvent) {
+			GroupRemovedEvent s = (GroupRemovedEvent) e;
 			if (s.getGroup().getId().equals(groupId)) {
-				LOG.info("Subscription removed");
+				LOG.info("Forum removed");
 				finishOnUiThread();
 			}
 		}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
index 55f34f75693021528fa1027914f591f6907489c6..0b1606d76819ac6ef72692f32776a3ba7a0c60a4 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java
@@ -24,17 +24,15 @@ import org.briarproject.android.util.HorizontalBorder;
 import org.briarproject.android.util.LayoutUtils;
 import org.briarproject.android.util.ListLoadingProgressBar;
 import org.briarproject.api.db.DbException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.event.Event;
+import org.briarproject.api.event.GroupAddedEvent;
+import org.briarproject.api.event.GroupRemovedEvent;
 import org.briarproject.api.event.MessageValidatedEvent;
-import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
-import org.briarproject.api.event.SubscriptionAddedEvent;
-import org.briarproject.api.event.SubscriptionRemovedEvent;
 import org.briarproject.api.forum.Forum;
 import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.sync.ClientId;
-import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
 
 import java.util.Collection;
@@ -169,7 +167,7 @@ public class ForumListFragment extends BaseEventFragment implements
 							Collection<ForumPostHeader> headers =
 									forumManager.getPostHeaders(f.getId());
 							displayHeaders(f, headers);
-						} catch (NoSuchSubscriptionException e) {
+						} catch (NoSuchGroupException e) {
 							// Continue
 						}
 					}
@@ -254,6 +252,7 @@ public class ForumListFragment extends BaseEventFragment implements
 	}
 
 	public void eventOccurred(Event e) {
+		// TODO: What other events are needed here?
 		if (e instanceof MessageValidatedEvent) {
 			MessageValidatedEvent m = (MessageValidatedEvent) e;
 			ClientId c = m.getClientId();
@@ -261,16 +260,16 @@ public class ForumListFragment extends BaseEventFragment implements
 				LOG.info("Message added, reloading");
 				loadHeaders(m.getMessage().getGroupId());
 			}
-		} else if (e instanceof RemoteSubscriptionsUpdatedEvent) {
-			LOG.info("Remote subscriptions changed, reloading");
-			loadAvailable();
-		} else if (e instanceof SubscriptionAddedEvent) {
-			LOG.info("Group added, reloading");
-			loadHeaders();
-		} else if (e instanceof SubscriptionRemovedEvent) {
-			Group g = ((SubscriptionRemovedEvent) e).getGroup();
-			if (g.getClientId().equals(forumManager.getClientId())) {
-				LOG.info("Group removed, reloading");
+		} else if (e instanceof GroupAddedEvent) {
+			GroupAddedEvent g = (GroupAddedEvent) e;
+			if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
+				LOG.info("Forum added, reloading");
+				loadHeaders();
+			}
+		} else if (e instanceof GroupRemovedEvent) {
+			GroupRemovedEvent g = (GroupRemovedEvent) e;
+			if (g.getGroup().getClientId().equals(forumManager.getClientId())) {
+				LOG.info("Forum removed, reloading");
 				loadHeaders();
 			}
 		}
@@ -288,7 +287,7 @@ public class ForumListFragment extends BaseEventFragment implements
 					if (LOG.isLoggable(INFO))
 						LOG.info("Partial load took " + duration + " ms");
 					displayHeaders(f, headers);
-				} catch (NoSuchSubscriptionException e) {
+				} catch (NoSuchGroupException e) {
 					removeForum(g);
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index b67ff2fd484d975ee5f5b83ede9db76016d29b95..7d50be928295542ae86e1d5477e5cd4c7658ac50 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -45,11 +45,8 @@ public interface DatabaseComponent {
 	 */
 	ContactId addContact(Author remote, AuthorId local) throws DbException;
 
-	/**
-	 * Subscribes to a group, or returns false if the user already has the
-	 * maximum number of subscriptions.
-	 */
-	boolean addGroup(Group g) throws DbException;
+	/** Stores a group. */
+	void addGroup(Group g) throws DbException;
 
 	/** Stores a local pseudonym. */
 	void addLocalAuthor(LocalAuthor a) throws DbException;
@@ -120,16 +117,13 @@ public interface DatabaseComponent {
 	/** Returns the unique ID for this device. */
 	DeviceId getDeviceId() throws DbException;
 
-	/** Returns the group with the given ID, if the user subscribes to it. */
+	/** Returns the group with the given ID. */
 	Group getGroup(GroupId g) throws DbException;
 
 	/** Returns the metadata for the given group. */
 	Metadata getGroupMetadata(GroupId g) throws DbException;
 
-	/**
-	 * Returns all groups belonging to the given client to which the user
-	 * subscribes.
-	 */
+	/** Returns all groups belonging to the given client. */
 	Collection<Group> getGroups(ClientId c) throws DbException;
 
 	/** Returns the local pseudonym with the given ID. */
@@ -221,10 +215,7 @@ public interface DatabaseComponent {
 	/** Removes a contact (and all associated state) from the database. */
 	void removeContact(ContactId c) throws DbException;
 
-	/**
-	 * Unsubscribes from a group. Any messages belonging to the group
-	 * are deleted from the database.
-	 */
+	/** Removes a group (and all associated state) from the database. */
 	void removeGroup(Group g) throws DbException;
 
 	/**
@@ -232,10 +223,7 @@ public interface DatabaseComponent {
 	 */
 	void removeLocalAuthor(AuthorId a) throws DbException;
 
-	/**
-	 * Removes a transport (and any associated configuration and local
-	 * properties) from the database.
-	 */
+	/** Removes a transport (and all associated state) from the database. */
 	void removeTransport(TransportId t) throws DbException;
 
 	/** Sets the status of the given contact. */
diff --git a/briar-api/src/org/briarproject/api/db/NoSuchGroupException.java b/briar-api/src/org/briarproject/api/db/NoSuchGroupException.java
new file mode 100644
index 0000000000000000000000000000000000000000..17e67f5648d86fcce61275f01fdcd89a3d1b564b
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/db/NoSuchGroupException.java
@@ -0,0 +1,11 @@
+package org.briarproject.api.db;
+
+/**
+ * Thrown when a database operation is attempted for a group that is not in the
+ * database. This exception may occur due to concurrent updates and does not
+ * indicate a database error.
+ */
+public class NoSuchGroupException extends DbException {
+
+	private static final long serialVersionUID = -5494178507342571697L;
+}
diff --git a/briar-api/src/org/briarproject/api/db/NoSuchSubscriptionException.java b/briar-api/src/org/briarproject/api/db/NoSuchSubscriptionException.java
deleted file mode 100644
index fc19dc1e50570cc7e757ea24a20d5c905c043c23..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/db/NoSuchSubscriptionException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package org.briarproject.api.db;
-
-/**
- * Thrown when a database operation is attempted for a group to which the user
- * does not subscribe. This exception may occur due to concurrent updates and
- * does not indicate a database error.
- */
-public class NoSuchSubscriptionException extends DbException {
-
-	private static final long serialVersionUID = -5494178507342571697L;
-
-}
diff --git a/briar-api/src/org/briarproject/api/event/SubscriptionAddedEvent.java b/briar-api/src/org/briarproject/api/event/GroupAddedEvent.java
similarity index 51%
rename from briar-api/src/org/briarproject/api/event/SubscriptionAddedEvent.java
rename to briar-api/src/org/briarproject/api/event/GroupAddedEvent.java
index a9065b28b29184ac2e314414eb47e82aa22f3949..d900388bc451bb3662bd44ebc798a7735b3b656b 100644
--- a/briar-api/src/org/briarproject/api/event/SubscriptionAddedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/GroupAddedEvent.java
@@ -2,12 +2,12 @@ package org.briarproject.api.event;
 
 import org.briarproject.api.sync.Group;
 
-/** An event that is broadcast when the user subscribes to a group. */
-public class SubscriptionAddedEvent extends Event {
+/** An event that is broadcast when a group is added. */
+public class GroupAddedEvent extends Event {
 
 	private final Group group;
 
-	public SubscriptionAddedEvent(Group group) {
+	public GroupAddedEvent(Group group) {
 		this.group = group;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/SubscriptionRemovedEvent.java b/briar-api/src/org/briarproject/api/event/GroupRemovedEvent.java
similarity index 50%
rename from briar-api/src/org/briarproject/api/event/SubscriptionRemovedEvent.java
rename to briar-api/src/org/briarproject/api/event/GroupRemovedEvent.java
index a8e7524571643bd6364916f12dab5626c7e66db8..25838e610140469d870b99b48087c64c49163b59 100644
--- a/briar-api/src/org/briarproject/api/event/SubscriptionRemovedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/GroupRemovedEvent.java
@@ -2,12 +2,12 @@ package org.briarproject.api.event;
 
 import org.briarproject.api.sync.Group;
 
-/** An event that is broadcast when the user unsubscribes from a group. */
-public class SubscriptionRemovedEvent extends Event {
+/** An event that is broadcast when a group is removed. */
+public class GroupRemovedEvent extends Event {
 
 	private final Group group;
 
-	public SubscriptionRemovedEvent(Group group) {
+	public GroupRemovedEvent(Group group) {
 		this.group = group;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/LocalSubscriptionsUpdatedEvent.java b/briar-api/src/org/briarproject/api/event/GroupVisibilityUpdatedEvent.java
similarity index 56%
rename from briar-api/src/org/briarproject/api/event/LocalSubscriptionsUpdatedEvent.java
rename to briar-api/src/org/briarproject/api/event/GroupVisibilityUpdatedEvent.java
index ce7a2d909463c678319dd30b4747d6e6b213f748..368865217c487ef898e32dbfbc81ccf0e6500d86 100644
--- a/briar-api/src/org/briarproject/api/event/LocalSubscriptionsUpdatedEvent.java
+++ b/briar-api/src/org/briarproject/api/event/GroupVisibilityUpdatedEvent.java
@@ -4,15 +4,12 @@ import org.briarproject.api.contact.ContactId;
 
 import java.util.Collection;
 
-/**
- * An event that is broadcast when the set of subscriptions visible to one or
- * more contacts is updated.
- */
-public class LocalSubscriptionsUpdatedEvent extends Event {
+/** An event that is broadcast when the visibility of a group is updated. */
+public class GroupVisibilityUpdatedEvent extends Event {
 
 	private final Collection<ContactId> affected;
 
-	public LocalSubscriptionsUpdatedEvent(Collection<ContactId> affected) {
+	public GroupVisibilityUpdatedEvent(Collection<ContactId> affected) {
 		this.affected = affected;
 	}
 
diff --git a/briar-api/src/org/briarproject/api/event/RemoteSubscriptionsUpdatedEvent.java b/briar-api/src/org/briarproject/api/event/RemoteSubscriptionsUpdatedEvent.java
deleted file mode 100644
index cd7a17ec34e12e3d61c2aa4cc696686fc47f04bc..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/event/RemoteSubscriptionsUpdatedEvent.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.briarproject.api.event;
-
-import org.briarproject.api.contact.ContactId;
-
-/**  An event that is broadcast when a contact's subscriptions are updated. */
-public class RemoteSubscriptionsUpdatedEvent extends Event {
-
-	private final ContactId contactId;
-
-	public RemoteSubscriptionsUpdatedEvent(ContactId contactId) {
-		this.contactId = contactId;
-	}
-
-	public ContactId getContactId() {
-		return contactId;
-	}
-}
diff --git a/briar-api/src/org/briarproject/api/forum/ForumManager.java b/briar-api/src/org/briarproject/api/forum/ForumManager.java
index f256eedd64ef56f923fe12fe71ac9574789a0abb..76464bac400574d8f1fe6fc3d063321d5887aaec 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumManager.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumManager.java
@@ -17,11 +17,8 @@ public interface ForumManager {
 	/** Creates a forum with the given name. */
 	Forum createForum(String name);
 
-	/**
-	 * Subscribes to a forum, or returns false if the user already has the
-	 * maximum number of forum subscriptions.
-	 */
-	boolean addForum(Forum f) throws DbException;
+	/** Subscribes to a forum. */
+	void addForum(Forum f) throws DbException;
 
 	/** Stores a local forum post. */
 	void addLocalPost(ForumPost p) throws DbException;
@@ -29,7 +26,7 @@ public interface ForumManager {
 	/** Returns all forums to which the user could subscribe. */
 	Collection<Forum> getAvailableForums() throws DbException;
 
-	/** Returns the forum with the given ID, if the user subscribes to it. */
+	/** Returns the forum with the given ID. */
 	Forum getForum(GroupId g) throws DbException;
 
 	/** Returns all forums to which the user subscribes. */
@@ -47,10 +44,7 @@ public interface ForumManager {
 	/** Returns the IDs of all contacts to which the given forum is visible. */
 	Collection<ContactId> getVisibility(GroupId g) throws DbException;
 
-	/**
-	 * Unsubscribes from a forum. Any messages belonging to the forum are
-	 * deleted.
-	 */
+	/** Unsubscribes from a forum. */
 	void removeForum(Forum f) throws DbException;
 
 	/** Marks a forum post as read or unread. */
diff --git a/briar-api/src/org/briarproject/api/sync/Group.java b/briar-api/src/org/briarproject/api/sync/Group.java
index 51818a3822ba93f08455d7f62ada6e6b9f880b4e..711cde85b7e04ae2b1497f59dd005bd42fb87e6c 100644
--- a/briar-api/src/org/briarproject/api/sync/Group.java
+++ b/briar-api/src/org/briarproject/api/sync/Group.java
@@ -2,7 +2,6 @@ package org.briarproject.api.sync;
 
 import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
 
-/** A group to which users may subscribe. */
 public class Group {
 
 	private final GroupId id;
diff --git a/briar-api/src/org/briarproject/api/sync/SyncConstants.java b/briar-api/src/org/briarproject/api/sync/SyncConstants.java
index 7687edf255a65ba922da936f09527f31e62ef9d1..999b458ebf53507687118f9cdeda58c463c59e3c 100644
--- a/briar-api/src/org/briarproject/api/sync/SyncConstants.java
+++ b/briar-api/src/org/briarproject/api/sync/SyncConstants.java
@@ -13,11 +13,8 @@ public interface SyncConstants {
 	/** The maximum length of the packet payload in bytes. */
 	int MAX_PACKET_PAYLOAD_LENGTH = 32 * 1024; // 32 KiB
 
-	/** The maximum number of groups a user may subscribe to. */
-	int MAX_SUBSCRIPTIONS = 200; // TODO: Remove
-
 	/** The maximum length of a group descriptor in bytes. */
-	int MAX_GROUP_DESCRIPTOR_LENGTH = 100; // TODO: Remove
+	int MAX_GROUP_DESCRIPTOR_LENGTH = 1000; // TODO: Remove
 
 	/** The maximum length of a message in bytes. */
 	int MAX_MESSAGE_LENGTH = MAX_PACKET_PAYLOAD_LENGTH - PACKET_HEADER_LENGTH;
diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index 10d95b4e97f044391bf96dd311a683e3e4176c40..4d147ccf46103707dd5acf0c97d9f21aac9f0279 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -84,12 +84,11 @@ interface Database<T> {
 			throws DbException;
 
 	/**
-	 * Subscribes to a group, or returns false if the user already has the
-	 * maximum number of subscriptions.
+	 * Stores a group.
 	 * <p>
 	 * Locking: write.
 	 */
-	boolean addGroup(T txn, Group g) throws DbException;
+	void addGroup(T txn, Group g) throws DbException;
 
 	/**
 	 * Stores a local pseudonym.
@@ -164,7 +163,7 @@ interface Database<T> {
 	boolean containsContact(T txn, ContactId c) throws DbException;
 
 	/**
-	 * Returns true if the user subscribes to the given group.
+	 * Returns true if the database contains the given group.
 	 * <p>
 	 * Locking: read.
 	 */
@@ -192,7 +191,7 @@ interface Database<T> {
 	boolean containsTransport(T txn, TransportId t) throws DbException;
 
 	/**
-	 * Returns true if the user subscribes to the given group and the group is
+	 * Returns true if the database contains the given group and the group is
 	 * visible to the given contact.
 	 * <p>
 	 * Locking: read.
@@ -259,7 +258,7 @@ interface Database<T> {
 	long getFreeSpace() throws DbException;
 
 	/**
-	 * Returns the group with the given ID, if the user subscribes to it.
+	 * Returns the group with the given ID.
 	 * <p>
 	 * Locking: read.
 	 */
@@ -273,8 +272,7 @@ interface Database<T> {
 	Metadata getGroupMetadata(T txn, GroupId g) throws DbException;
 
 	/**
-	 * Returns all groups belonging to the given client to which the user
-	 * subscribes.
+	 * Returns all groups belonging to the given client.
 	 * <p>
 	 * Locking: read.
 	 */
@@ -500,16 +498,14 @@ interface Database<T> {
 	void removeContact(T txn, ContactId c) throws DbException;
 
 	/**
-	 * Unsubscribes from a group. Any messages belonging to the group are
-	 * deleted from the database.
+	 * Removes a group (and all associated state) from the database.
 	 * <p>
 	 * Locking: write.
 	 */
 	void removeGroup(T txn, GroupId g) throws DbException;
 
 	/**
-	 * Removes a local pseudonym (and all associated contacts) from the
-	 * database.
+	 * Removes a local pseudonym (and all associated state) from the database.
 	 * <p>
 	 * Locking: write.
 	 */
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 4334226663cc7ad91625bcb4ba8d507e67a10092..649e47b2adcf7f41477a4f59795b4220a71e0a5c 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -11,13 +11,15 @@ import org.briarproject.api.db.LocalAuthorExistsException;
 import org.briarproject.api.db.MessageExistsException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.NoSuchContactException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.db.NoSuchLocalAuthorException;
 import org.briarproject.api.db.NoSuchMessageException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
 import org.briarproject.api.db.NoSuchTransportException;
 import org.briarproject.api.db.StorageStatus;
 import org.briarproject.api.event.EventBus;
-import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
+import org.briarproject.api.event.GroupAddedEvent;
+import org.briarproject.api.event.GroupRemovedEvent;
+import org.briarproject.api.event.GroupVisibilityUpdatedEvent;
 import org.briarproject.api.event.MessageAddedEvent;
 import org.briarproject.api.event.MessageRequestedEvent;
 import org.briarproject.api.event.MessageSharedEvent;
@@ -27,8 +29,6 @@ import org.briarproject.api.event.MessageValidatedEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
-import org.briarproject.api.event.SubscriptionAddedEvent;
-import org.briarproject.api.event.SubscriptionRemovedEvent;
 import org.briarproject.api.event.TransportAddedEvent;
 import org.briarproject.api.event.TransportRemovedEvent;
 import org.briarproject.api.identity.Author;
@@ -161,14 +161,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		}
 	}
 
-	public boolean addGroup(Group g) throws DbException {
+	public void addGroup(Group g) throws DbException {
 		boolean added = false;
 		lock.writeLock().lock();
 		try {
 			T txn = db.startTransaction();
 			try {
-				if (!db.containsGroup(txn, g.getId()))
-					added = db.addGroup(txn, g);
+				if (!db.containsGroup(txn, g.getId())) {
+					added = true;
+					db.addGroup(txn, g);
+				}
 				db.commitTransaction(txn);
 			} catch (DbException e) {
 				db.abortTransaction(txn);
@@ -177,8 +179,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		} finally {
 			lock.writeLock().unlock();
 		}
-		if (added) eventBus.broadcast(new SubscriptionAddedEvent(g));
-		return added;
+		if (added) eventBus.broadcast(new GroupAddedEvent(g));
 	}
 
 	public void addLocalAuthor(LocalAuthor a) throws DbException {
@@ -208,7 +209,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 				if (db.containsMessage(txn, m.getId()))
 					throw new MessageExistsException();
 				if (!db.containsGroup(txn, m.getGroupId()))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				addMessage(txn, m, VALID, shared, null);
 				db.mergeMessageMetadata(txn, m.getId(), meta);
 				db.commitTransaction(txn);
@@ -493,7 +494,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				Group group = db.getGroup(txn, g);
 				db.commitTransaction(txn);
 				return group;
@@ -512,7 +513,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				Metadata metadata = db.getGroupMetadata(txn, g);
 				db.commitTransaction(txn);
 				return metadata;
@@ -622,7 +623,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				Map<MessageId, Metadata> metadata =
 						db.getMessageMetadata(txn, g);
 				db.commitTransaction(txn);
@@ -664,7 +665,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 				if (!db.containsContact(txn, c))
 					throw new NoSuchContactException();
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				Collection<MessageStatus> statuses =
 						db.getMessageStatus(txn, c, g);
 				db.commitTransaction(txn);
@@ -763,7 +764,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				Collection<ContactId> visible = db.getVisibility(txn, g);
 				db.commitTransaction(txn);
 				return visible;
@@ -804,7 +805,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				db.mergeGroupMetadata(txn, g, meta);
 				db.commitTransaction(txn);
 			} catch (DbException e) {
@@ -998,7 +999,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			try {
 				GroupId id = g.getId();
 				if (!db.containsGroup(txn, id))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				affected = db.getVisibility(txn, id);
 				db.removeGroup(txn, id);
 				db.commitTransaction(txn);
@@ -1009,8 +1010,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		} finally {
 			lock.writeLock().unlock();
 		}
-		eventBus.broadcast(new SubscriptionRemovedEvent(g));
-		eventBus.broadcast(new LocalSubscriptionsUpdatedEvent(affected));
+		eventBus.broadcast(new GroupRemovedEvent(g));
+		eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected));
 	}
 
 	public void removeLocalAuthor(AuthorId a) throws DbException {
@@ -1157,7 +1158,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				// Use HashSets for O(1) lookups, O(n) overall running time
 				HashSet<ContactId> now = new HashSet<ContactId>(visible);
 				Collection<ContactId> before = db.getVisibility(txn, g);
@@ -1185,7 +1186,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			lock.writeLock().unlock();
 		}
 		if (!affected.isEmpty())
-			eventBus.broadcast(new LocalSubscriptionsUpdatedEvent(affected));
+			eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected));
 	}
 
 	public void setVisibleToAll(GroupId g, boolean all) throws DbException {
@@ -1195,7 +1196,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			T txn = db.startTransaction();
 			try {
 				if (!db.containsGroup(txn, g))
-					throw new NoSuchSubscriptionException();
+					throw new NoSuchGroupException();
 				// Make the group visible or invisible to future contacts
 				db.setVisibleToAll(txn, g, all);
 				if (all) {
@@ -1218,7 +1219,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 			lock.writeLock().unlock();
 		}
 		if (!affected.isEmpty())
-			eventBus.broadcast(new LocalSubscriptionsUpdatedEvent(affected));
+			eventBus.broadcast(new GroupVisibilityUpdatedEvent(affected));
 	}
 
 	public void updateTransportKeys(Map<ContactId, TransportKeys> keys)
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index 92c808167f2f4bd4119775f5b8e4f4c38e1f8a34..30a4f32e4440630bde158c1a2aae2a7d4f6cea25 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -50,7 +50,6 @@ import java.util.logging.Logger;
 import static java.util.logging.Level.WARNING;
 import static org.briarproject.api.db.Metadata.REMOVE;
 import static org.briarproject.api.db.StorageStatus.ADDING;
-import static org.briarproject.api.sync.SyncConstants.MAX_SUBSCRIPTIONS;
 import static org.briarproject.api.sync.ValidationManager.Validity.INVALID;
 import static org.briarproject.api.sync.ValidationManager.Validity.UNKNOWN;
 import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
@@ -544,21 +543,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public boolean addGroup(Connection txn, Group g) throws DbException {
+	public void addGroup(Connection txn, Group g) throws DbException {
 		PreparedStatement ps = null;
-		ResultSet rs = null;
 		try {
-			String sql = "SELECT COUNT (groupId) FROM groups";
-			ps = txn.prepareStatement(sql);
-			rs = ps.executeQuery();
-			if (!rs.next()) throw new DbStateException();
-			int count = rs.getInt(1);
-			if (rs.next()) throw new DbStateException();
-			rs.close();
-			ps.close();
-			if (count > MAX_SUBSCRIPTIONS) throw new DbStateException();
-			if (count == MAX_SUBSCRIPTIONS) return false;
-			sql = "INSERT INTO groups"
+			String sql = "INSERT INTO groups"
 					+ " (groupId, clientId, descriptor, visibleToAll)"
 					+ " VALUES (?, ?, ?, FALSE)";
 			ps = txn.prepareStatement(sql);
@@ -568,9 +556,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			int affected = ps.executeUpdate();
 			if (affected != 1) throw new DbStateException();
 			ps.close();
-			return true;
 		} catch (SQLException e) {
-			tryToClose(rs);
 			tryToClose(ps);
 			throw new DbException(e);
 		}
diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
index 77f4f7dd4fcaa9cdbc6854ae2dd060c8b5148628..872974c299ad37372f299cc14dbe2ec6aa187405 100644
--- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
@@ -113,8 +113,8 @@ class ForumManagerImpl implements ForumManager {
 	}
 
 	@Override
-	public boolean addForum(Forum f) throws DbException {
-		return db.addGroup(f.getGroup());
+	public void addForum(Forum f) throws DbException {
+		db.addGroup(f.getGroup());
 	}
 
 	@Override
diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
index 286d97f5485a76b91135570a6d7f5065e1822e41..1260fdff1b7c89fa0b76b512e0ad3454db45e8d3 100644
--- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
+++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
@@ -73,7 +73,7 @@ class MessagingManagerImpl implements MessagingManager, AddContactHook,
 		try {
 			// Create the conversation group
 			Group g = getConversationGroup(db.getContact(c));
-			// Subscribe to the group and share it with the contact
+			// Store the group and share it with the contact
 			db.addGroup(g);
 			db.setVisibility(g.getId(), Collections.singletonList(c));
 			// Attach the contact ID to the group
diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
index c2a0c25e8255f89d132116eea5abd27e40203387..58e04b4e1dbcca3001b2fda5b65a707fe2af9710 100644
--- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
+++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
@@ -19,7 +19,7 @@ import org.briarproject.api.data.MetadataParser;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
-import org.briarproject.api.db.NoSuchSubscriptionException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.properties.TransportProperties;
 import org.briarproject.api.properties.TransportPropertyManager;
 import org.briarproject.api.sync.ClientId;
@@ -96,7 +96,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 		try {
 			// Create a group to share with the contact
 			Group g = getContactGroup(db.getContact(c));
-			// Subscribe to the group and share it with the contact
+			// Store the group and share it with the contact
 			db.addGroup(g);
 			db.setVisibility(g.getId(), Collections.singletonList(c));
 			// Copy the latest local properties into the group
@@ -197,7 +197,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 				local.put(e.getKey(), decodeProperties(raw));
 			}
 			return Collections.unmodifiableMap(local);
-		} catch (NoSuchSubscriptionException e) {
+		} catch (NoSuchGroupException e) {
 			// Local group doesn't exist - there are no local properties
 			return Collections.emptyMap();
 		} catch (IOException e) {
@@ -254,7 +254,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 			if (latest == null) return null;
 			// Retrieve and decode the latest local properties
 			return decodeProperties(db.getRawMessage(latest.messageId));
-		} catch (NoSuchSubscriptionException e) {
+		} catch (NoSuchGroupException e) {
 			// Local group doesn't exist - there are no local properties
 			return null;
 		} catch (IOException e) {
diff --git a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
index 2f548fff76e16fae1cf7a739fa4ecf56ae5c6cf3..dfb93ec3fe7deab3e663d1bf0bf11db715249353 100644
--- a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
@@ -8,13 +8,12 @@ import org.briarproject.api.event.ContactRemovedEvent;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventBus;
 import org.briarproject.api.event.EventListener;
-import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
+import org.briarproject.api.event.GroupVisibilityUpdatedEvent;
 import org.briarproject.api.event.MessageRequestedEvent;
 import org.briarproject.api.event.MessageSharedEvent;
 import org.briarproject.api.event.MessageToAckEvent;
 import org.briarproject.api.event.MessageToRequestEvent;
 import org.briarproject.api.event.MessageValidatedEvent;
-import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.ShutdownEvent;
 import org.briarproject.api.event.TransportRemovedEvent;
 import org.briarproject.api.sync.Ack;
@@ -155,10 +154,9 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 		} else if (e instanceof MessageValidatedEvent) {
 			if (((MessageValidatedEvent) e).isValid())
 				dbExecutor.execute(new GenerateOffer());
-		} else if (e instanceof LocalSubscriptionsUpdatedEvent) {
-			LocalSubscriptionsUpdatedEvent l =
-					(LocalSubscriptionsUpdatedEvent) e;
-			if (l.getAffectedContacts().contains(contactId))
+		} else if (e instanceof GroupVisibilityUpdatedEvent) {
+			GroupVisibilityUpdatedEvent g = (GroupVisibilityUpdatedEvent) e;
+			if (g.getAffectedContacts().contains(contactId))
 				dbExecutor.execute(new GenerateOffer());
 		} else if (e instanceof MessageRequestedEvent) {
 			if (((MessageRequestedEvent) e).getContactId().equals(contactId))
@@ -169,11 +167,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 		} else if (e instanceof MessageToRequestEvent) {
 			if (((MessageToRequestEvent) e).getContactId().equals(contactId))
 				dbExecutor.execute(new GenerateRequest());
-		} else if (e instanceof RemoteSubscriptionsUpdatedEvent) {
-			RemoteSubscriptionsUpdatedEvent r =
-					(RemoteSubscriptionsUpdatedEvent) e;
-			if (r.getContactId().equals(contactId))
-				dbExecutor.execute(new GenerateOffer());
 		} else if (e instanceof ShutdownEvent) {
 			interrupt();
 		} else if (e instanceof TransportRemovedEvent) {
diff --git a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
index 7a5202c293ed6c98ce04068f4204f41d4401a7f9..855f59b0df73b98739ebfe3ce858b3f339478a8f 100644
--- a/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
+++ b/briar-core/src/org/briarproject/sync/ValidationManagerImpl.java
@@ -8,8 +8,8 @@ import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DatabaseExecutor;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Metadata;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.db.NoSuchMessageException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
 import org.briarproject.api.event.Event;
 import org.briarproject.api.event.EventListener;
 import org.briarproject.api.event.MessageAddedEvent;
@@ -158,7 +158,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
 				try {
 					ClientId c = db.getGroup(m.getGroupId()).getClientId();
 					validateMessage(m, c);
-				} catch (NoSuchSubscriptionException e) {
+				} catch (NoSuchGroupException e) {
 					LOG.info("Group removed before validation");
 				} catch (DbException e) {
 					if (LOG.isLoggable(WARNING))
diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
index 6d8a2a61fb3d6edfce564d3de2f52d00a728aa08..a4128df5600883fa2b60e346ff5c781bfcb11552 100644
--- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
+++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
@@ -10,13 +10,15 @@ import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.MessageExistsException;
 import org.briarproject.api.db.Metadata;
 import org.briarproject.api.db.NoSuchContactException;
+import org.briarproject.api.db.NoSuchGroupException;
 import org.briarproject.api.db.NoSuchLocalAuthorException;
 import org.briarproject.api.db.NoSuchMessageException;
-import org.briarproject.api.db.NoSuchSubscriptionException;
 import org.briarproject.api.db.NoSuchTransportException;
 import org.briarproject.api.db.StorageStatus;
 import org.briarproject.api.event.EventBus;
-import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
+import org.briarproject.api.event.GroupAddedEvent;
+import org.briarproject.api.event.GroupRemovedEvent;
+import org.briarproject.api.event.GroupVisibilityUpdatedEvent;
 import org.briarproject.api.event.MessageAddedEvent;
 import org.briarproject.api.event.MessageRequestedEvent;
 import org.briarproject.api.event.MessageToAckEvent;
@@ -25,8 +27,6 @@ import org.briarproject.api.event.MessageValidatedEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
-import org.briarproject.api.event.SubscriptionAddedEvent;
-import org.briarproject.api.event.SubscriptionRemovedEvent;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.identity.LocalAuthor;
@@ -146,8 +146,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).containsGroup(txn, groupId);
 			will(returnValue(false));
 			oneOf(database).addGroup(txn, group);
-			will(returnValue(true));
-			oneOf(eventBus).broadcast(with(any(SubscriptionAddedEvent.class)));
+			oneOf(eventBus).broadcast(with(any(GroupAddedEvent.class)));
 			// addGroup() again
 			oneOf(database).containsGroup(txn, groupId);
 			will(returnValue(true));
@@ -160,10 +159,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).getVisibility(txn, groupId);
 			will(returnValue(Collections.emptyList()));
 			oneOf(database).removeGroup(txn, groupId);
+			oneOf(eventBus).broadcast(with(any(GroupRemovedEvent.class)));
 			oneOf(eventBus).broadcast(with(any(
-					SubscriptionRemovedEvent.class)));
-			oneOf(eventBus).broadcast(with(any(
-					LocalSubscriptionsUpdatedEvent.class)));
+					GroupVisibilityUpdatedEvent.class)));
 			// removeContact()
 			oneOf(database).containsContact(txn, contactId);
 			will(returnValue(true));
@@ -222,7 +220,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testLocalMessagesAreNotStoredUnlessSubscribed()
+	public void testLocalMessagesAreNotStoredUnlessGroupExists()
 			throws Exception {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
@@ -244,7 +242,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		try {
 			db.addLocalMessage(message, clientId, metadata, true);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
@@ -446,7 +444,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testVariousMethodsThrowExceptionIfSubscriptionIsMissing()
+	public void testVariousMethodsThrowExceptionIfGroupIsMissing()
 			throws Exception {
 		Mockery context = new Mockery();
 		@SuppressWarnings("unchecked")
@@ -454,7 +452,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
-			// Check whether the subscription is in the DB (which it's not)
+			// Check whether the group is in the DB (which it's not)
 			exactly(7).of(database).startTransaction();
 			will(returnValue(txn));
 			exactly(7).of(database).containsGroup(txn, groupId);
@@ -470,49 +468,49 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		try {
 			db.getGroup(groupId);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.getGroupMetadata(groupId);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.getMessageStatus(contactId, groupId);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.getVisibility(groupId);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.mergeGroupMetadata(groupId, metadata);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.removeGroup(group);
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
 		try {
 			db.setVisibility(groupId, Collections.<ContactId>emptyList());
 			fail();
-		} catch (NoSuchSubscriptionException expected) {
+		} catch (NoSuchGroupException expected) {
 			// Expected
 		}
 
@@ -1039,7 +1037,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).setVisibleToAll(txn, groupId, false);
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(
-					LocalSubscriptionsUpdatedEvent.class)));
+					GroupVisibilityUpdatedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
@@ -1103,7 +1101,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).setVisibleToAll(txn, groupId, false);
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(
-					LocalSubscriptionsUpdatedEvent.class)));
+					GroupVisibilityUpdatedEvent.class)));
 			// setVisibleToAll()
 			oneOf(database).startTransaction();
 			will(returnValue(txn));
@@ -1117,7 +1115,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			oneOf(database).addVisibility(txn, contactId1, groupId);
 			oneOf(database).commitTransaction(txn);
 			oneOf(eventBus).broadcast(with(any(
-					LocalSubscriptionsUpdatedEvent.class)));
+					GroupVisibilityUpdatedEvent.class)));
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
index 46d6e274c985b0c848ea79cae586ccf58a299482..5fe90fdd13bda3868762419945369308203a7f49 100644
--- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
+++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
@@ -146,15 +146,15 @@ public class H2DatabaseTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testUnsubscribingRemovesMessage() throws Exception {
+	public void testRemovingGroupRemovesMessage() throws Exception {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Subscribe to a group and store a message
+		// Add a group and a message
 		db.addGroup(txn, group);
 		db.addMessage(txn, message, VALID, true);
 
-		// Unsubscribing from the group should remove the message
+		// Removing the group should remove the message
 		assertTrue(db.containsMessage(txn, messageId));
 		db.removeGroup(txn, groupId);
 		assertFalse(db.containsMessage(txn, messageId));
@@ -168,7 +168,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact, subscribe to a group and store a message
+		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -281,7 +281,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact, subscribe to a group and store a message
+		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -307,14 +307,14 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact, subscribe to a group and store a message
+		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
 		db.addMessage(txn, message, VALID, true);
 		db.addStatus(txn, contactId, messageId, false, false);
 
-		// The subscription is not visible to the contact, so the message
+		// The group is not visible to the contact, so the message
 		// should not be sendable
 		Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
 				ONE_MEGABYTE);
@@ -322,7 +322,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		ids = db.getMessagesToOffer(txn, contactId, 100);
 		assertTrue(ids.isEmpty());
 
-		// Making the subscription visible should make the message sendable
+		// Making the group visible should make the message sendable
 		db.addVisibility(txn, contactId, groupId);
 		ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
 		assertEquals(Collections.singletonList(messageId), ids);
@@ -345,7 +345,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact and subscribe to a group
+		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -381,7 +381,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact, subscribe to a group and store a message
+		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -545,7 +545,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact and subscribe to a group
+		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -559,7 +559,7 @@ public class H2DatabaseTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testContainsVisibleMessageRequiresLocalSubscription()
+	public void testContainsVisibleMessageRequiresGroupInDatabase()
 			throws Exception {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
@@ -568,7 +568,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 
-		// There's no local subscription for the group
+		// The group is not in the database
 		assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
 
 		db.commitTransaction(txn);
@@ -576,19 +576,19 @@ public class H2DatabaseTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testContainsVisibleMessageRequiresVisibileSubscription()
+	public void testContainsVisibleMessageRequiresVisibileGroup()
 			throws Exception {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact, subscribe to a group and store a message
+		// Add a contact, a group and a message
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
 		db.addMessage(txn, message, VALID, true);
 		db.addStatus(txn, contactId, messageId, false, false);
 
-		// The subscription is not visible
+		// The group is not visible
 		assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
 
 		db.commitTransaction(txn);
@@ -600,7 +600,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact and subscribe to a group
+		// Add a contact and a group
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		db.addGroup(txn, group);
@@ -622,7 +622,7 @@ public class H2DatabaseTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testMultipleSubscriptionsAndUnsubscriptions() throws Exception {
+	public void testMultipleGroupChanges() throws Exception {
 		// Create some groups
 		List<Group> groups = new ArrayList<Group>();
 		for (int i = 0; i < 100; i++) {
@@ -635,7 +635,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		Database<Connection> db = open(false);
 		Connection txn = db.startTransaction();
 
-		// Add a contact and subscribe to the groups
+		// Add a contact and the groups
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 		for (Group g : groups) db.addGroup(txn, g);
@@ -951,7 +951,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addLocalAuthor(txn, localAuthor);
 		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
 
-		// Subscribe to a group and make it visible to the contact
+		// Add a group and make it visible to the contact
 		db.addGroup(txn, group);
 		db.addVisibility(txn, contactId, groupId);