diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
index 8f65c0d4874e1e31134406a959be45863d049ce5..ab1bc0a9dc55ae0bdeee22e7ca79e0ff1aeb2f69 100644
--- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
+++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java
@@ -17,8 +17,6 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.MessageStatus;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.transport.TransportKeys;
 
 import java.io.IOException;
@@ -113,20 +111,6 @@ public interface DatabaseComponent {
 	Collection<byte[]> generateRequestedBatch(ContactId c, int maxLength,
 			int maxLatency) throws DbException;
 
-	/**
-	 * Returns a subscription ack for the given contact, or null if no
-	 * subscription ack is due.
-	 */
-	SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException;
-
-	/**
-	 * Returns a subscription update for the given contact, for transmission
-	 * over a transport with the given latency. Returns null if no update is
-	 * due.
-	 */
-	SubscriptionUpdate generateSubscriptionUpdate(ContactId c, int maxLatency)
-			throws DbException;
-
 	/**
 	 * Returns all groups belonging to the given client to which the user could
 	 * subscribe.
@@ -246,14 +230,6 @@ public interface DatabaseComponent {
 	/** Processes a request from the given contact. */
 	void receiveRequest(ContactId c, Request r) throws DbException;
 
-	/** Processes a subscription ack from the given contact. */
-	void receiveSubscriptionAck(ContactId c, SubscriptionAck a)
-			throws DbException;
-
-	/** Processes a subscription update from the given contact. */
-	void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate u)
-			throws DbException;
-
 	/** Removes a contact (and all associated state) from the database. */
 	void removeContact(ContactId c) throws DbException;
 
diff --git a/briar-api/src/org/briarproject/api/sync/PacketReader.java b/briar-api/src/org/briarproject/api/sync/PacketReader.java
index 8986dd81612383ed9471c17e68e8c98a584f99f4..e8893fbf9ddd687a69e179508deb64809005887d 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketReader.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketReader.java
@@ -17,10 +17,4 @@ public interface PacketReader {
 
 	boolean hasRequest() throws IOException;
 	Request readRequest() throws IOException;
-
-	boolean hasSubscriptionAck() throws IOException;
-	SubscriptionAck readSubscriptionAck() throws IOException;
-
-	boolean hasSubscriptionUpdate() throws IOException;
-	SubscriptionUpdate readSubscriptionUpdate() throws IOException;
 }
diff --git a/briar-api/src/org/briarproject/api/sync/PacketTypes.java b/briar-api/src/org/briarproject/api/sync/PacketTypes.java
index 2bdc1b66af9c26e7e9b987ef80ec22d8d7f4ca10..214af877a32b19839d4cad8378f56ccc13d60ddc 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketTypes.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketTypes.java
@@ -7,6 +7,4 @@ public interface PacketTypes {
 	byte MESSAGE = 1;
 	byte OFFER = 2;
 	byte REQUEST = 3;
-	byte SUBSCRIPTION_ACK = 6;
-	byte SUBSCRIPTION_UPDATE = 7;
 }
diff --git a/briar-api/src/org/briarproject/api/sync/PacketWriter.java b/briar-api/src/org/briarproject/api/sync/PacketWriter.java
index 7777a42943433e89e029372324f0acb34d92986f..7cc4e7436e11a6a480cbc6919719f4d56717fdeb 100644
--- a/briar-api/src/org/briarproject/api/sync/PacketWriter.java
+++ b/briar-api/src/org/briarproject/api/sync/PacketWriter.java
@@ -18,9 +18,5 @@ public interface PacketWriter {
 
 	void writeRequest(Request r) throws IOException;
 
-	void writeSubscriptionAck(SubscriptionAck a) throws IOException;
-
-	void writeSubscriptionUpdate(SubscriptionUpdate u) throws IOException;
-
 	void flush() throws IOException;
 }
diff --git a/briar-api/src/org/briarproject/api/sync/SubscriptionAck.java b/briar-api/src/org/briarproject/api/sync/SubscriptionAck.java
deleted file mode 100644
index b65ccf1c279116b8bc14aa7656265d8aa6436014..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/sync/SubscriptionAck.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.briarproject.api.sync;
-
-/** A packet acknowledging a {@link SubscriptionUpdate}. */
-public class SubscriptionAck {
-
-	private final long version;
-
-	public SubscriptionAck(long version) {
-		this.version = version;
-	}
-
-	/** Returns the version number of the acknowledged update. */
-	public long getVersion() {
-		return version;
-	}
-}
diff --git a/briar-api/src/org/briarproject/api/sync/SubscriptionUpdate.java b/briar-api/src/org/briarproject/api/sync/SubscriptionUpdate.java
deleted file mode 100644
index 0fabdf27836282b556aef156a22f3965f516f923..0000000000000000000000000000000000000000
--- a/briar-api/src/org/briarproject/api/sync/SubscriptionUpdate.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.briarproject.api.sync;
-
-import java.util.Collection;
-
-/** A packet updating the recipient's view of the sender's subscriptions. */
-public class SubscriptionUpdate {
-
-	private final Collection<Group> groups;
-	private final long version;
-
-	public SubscriptionUpdate(Collection<Group> groups, long version) {
-		this.groups = groups;
-		this.version = version;
-	}
-
-	/**
-	 * Returns the groups to which the sender subscribes, and which the sender
-	 * has made visible to the recipient.
-	 */
-	public Collection<Group> getGroups() {
-		return groups;
-	}
-
-	/** Returns the update's version number. */
-	public long getVersion() {
-		return version;
-	}
-}
diff --git a/briar-api/src/org/briarproject/api/sync/SyncConstants.java b/briar-api/src/org/briarproject/api/sync/SyncConstants.java
index 3cb297a4bd30e6ba51521f4cc1006768e06d06d4..7687edf255a65ba922da936f09527f31e62ef9d1 100644
--- a/briar-api/src/org/briarproject/api/sync/SyncConstants.java
+++ b/briar-api/src/org/briarproject/api/sync/SyncConstants.java
@@ -14,10 +14,10 @@ public interface SyncConstants {
 	int MAX_PACKET_PAYLOAD_LENGTH = 32 * 1024; // 32 KiB
 
 	/** The maximum number of groups a user may subscribe to. */
-	int MAX_SUBSCRIPTIONS = 200;
+	int MAX_SUBSCRIPTIONS = 200; // TODO: Remove
 
 	/** The maximum length of a group descriptor in bytes. */
-	int MAX_GROUP_DESCRIPTOR_LENGTH = 100;
+	int MAX_GROUP_DESCRIPTOR_LENGTH = 100; // 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 22cc9357f61a4065de80172b6cfad88449d67c8f..bc502f42e74a254116fd4d73ee7de6d03ef49c42 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -17,8 +17,6 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.MessageStatus;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.ValidationManager.Validity;
 import org.briarproject.api.transport.TransportKeys;
 
@@ -420,23 +418,6 @@ interface Database<T> {
 	 */
 	Collection<Contact> getSubscribers(T txn, GroupId g) throws DbException;
 
-	/**
-	 * Returns a subscription ack for the given contact, or null if no ack is
-	 * due.
-	 * <p>
-	 * Locking: write.
-	 */
-	SubscriptionAck getSubscriptionAck(T txn, ContactId c) throws DbException;
-
-	/**
-	 * Returns a subscription update for the given contact and updates its
-	 * expiry time using the given latency, or returns null if no update is due.
-	 * <p>
-	 * Locking: write.
-	 */
-	SubscriptionUpdate getSubscriptionUpdate(T txn, ContactId c,
-			int maxLatency) throws DbException;
-
 	/**
 	 * Returns all transport keys for the given transport.
 	 * <p>
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index 505a288cd8988628bd8fb40a6473a06d2a960547..c75793fc33929a6bcff51ca28a64d980f1dca0fe 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -26,7 +26,6 @@ import org.briarproject.api.event.MessageToRequestEvent;
 import org.briarproject.api.event.MessageValidatedEvent;
 import org.briarproject.api.event.MessagesAckedEvent;
 import org.briarproject.api.event.MessagesSentEvent;
-import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
 import org.briarproject.api.event.SettingsUpdatedEvent;
 import org.briarproject.api.event.SubscriptionAddedEvent;
 import org.briarproject.api.event.SubscriptionRemovedEvent;
@@ -46,8 +45,6 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.MessageStatus;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.ValidationManager.Validity;
 import org.briarproject.api.transport.TransportKeys;
 
@@ -434,47 +431,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		return Collections.unmodifiableList(messages);
 	}
 
-	public SubscriptionAck generateSubscriptionAck(ContactId c)
-			throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				SubscriptionAck a = db.getSubscriptionAck(txn, c);
-				db.commitTransaction(txn);
-				return a;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
-	public SubscriptionUpdate generateSubscriptionUpdate(ContactId c,
-			int maxLatency) throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				SubscriptionUpdate u =
-						db.getSubscriptionUpdate(txn, c, maxLatency);
-				db.commitTransaction(txn);
-				return u;
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
 	public Collection<Group> getAvailableGroups(ClientId c) throws DbException {
 		lock.readLock().lock();
 		try {
@@ -1066,46 +1022,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		if (requested) eventBus.broadcast(new MessageRequestedEvent(c));
 	}
 
-	public void receiveSubscriptionAck(ContactId c, SubscriptionAck a)
-			throws DbException {
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				db.setSubscriptionUpdateAcked(txn, c, a.getVersion());
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-	}
-
-	public void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate u)
-			throws DbException {
-		boolean updated;
-		lock.writeLock().lock();
-		try {
-			T txn = db.startTransaction();
-			try {
-				if (!db.containsContact(txn, c))
-					throw new NoSuchContactException();
-				updated = db.setGroups(txn, c, u.getGroups(), u.getVersion());
-				db.commitTransaction(txn);
-			} catch (DbException e) {
-				db.abortTransaction(txn);
-				throw e;
-			}
-		} finally {
-			lock.writeLock().unlock();
-		}
-		if (updated) eventBus.broadcast(new RemoteSubscriptionsUpdatedEvent(c));
-	}
-
 	public void removeContact(ContactId c) throws DbException {
 		lock.writeLock().lock();
 		try {
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index 850ad42487f939bdcba15229a376647a07f5dee2..cc7a74a318e71d231291bbee468ce478ecc6e279 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -20,8 +20,6 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.MessageStatus;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.ValidationManager.Validity;
 import org.briarproject.api.system.Clock;
 import org.briarproject.api.transport.IncomingKeys;
@@ -1718,94 +1716,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
-	public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c)
-			throws DbException {
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT remoteVersion FROM groupVersions"
-					+ " WHERE contactId = ? AND remoteAcked = FALSE";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			rs = ps.executeQuery();
-			if (!rs.next()) {
-				rs.close();
-				ps.close();
-				return null;
-			}
-			long version = rs.getLong(1);
-			if (rs.next()) throw new DbStateException();
-			rs.close();
-			ps.close();
-			sql = "UPDATE groupVersions SET remoteAcked = TRUE"
-					+ " WHERE contactId = ?";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			int affected = ps.executeUpdate();
-			if (affected != 1) throw new DbStateException();
-			ps.close();
-			return new SubscriptionAck(version);
-		} catch (SQLException e) {
-			tryToClose(ps);
-			tryToClose(rs);
-			throw new DbException(e);
-		}
-	}
-
-	public SubscriptionUpdate getSubscriptionUpdate(Connection txn, ContactId c,
-			int maxLatency) throws DbException {
-		long now = clock.currentTimeMillis();
-		PreparedStatement ps = null;
-		ResultSet rs = null;
-		try {
-			String sql = "SELECT g.groupId, clientId, descriptor,"
-					+ " localVersion, txCount"
-					+ " FROM groups AS g"
-					+ " JOIN groupVisibilities AS gvis"
-					+ " ON g.groupId = gvis.groupId"
-					+ " JOIN groupVersions AS gver"
-					+ " ON gvis.contactId = gver.contactId"
-					+ " WHERE gvis.contactId = ?"
-					+ " AND localVersion > localAcked"
-					+ " AND expiry < ?";
-			ps = txn.prepareStatement(sql);
-			ps.setInt(1, c.getInt());
-			ps.setLong(2, now);
-			rs = ps.executeQuery();
-			List<Group> groups = new ArrayList<Group>();
-			Set<GroupId> ids = new HashSet<GroupId>();
-			long version = 0;
-			int txCount = 0;
-			while (rs.next()) {
-				GroupId id = new GroupId(rs.getBytes(1));
-				if (!ids.add(id)) throw new DbStateException();
-				ClientId clientId = new ClientId(rs.getBytes(2));
-				byte[] descriptor = rs.getBytes(3);
-				groups.add(new Group(id, clientId, descriptor));
-				version = rs.getLong(4);
-				txCount = rs.getInt(5);
-			}
-			rs.close();
-			ps.close();
-			if (groups.isEmpty()) return null;
-			sql = "UPDATE groupVersions"
-					+ " SET expiry = ?, txCount = txCount + 1"
-					+ " WHERE contactId = ?";
-			ps = txn.prepareStatement(sql);
-			ps.setLong(1, calculateExpiry(now, maxLatency, txCount));
-			ps.setInt(2, c.getInt());
-			int affected = ps.executeUpdate();
-			if (affected != 1) throw new DbStateException();
-			ps.close();
-			groups = Collections.unmodifiableList(groups);
-			return new SubscriptionUpdate(groups, version);
-		} catch (SQLException e) {
-			tryToClose(ps);
-			tryToClose(rs);
-			throw new DbException(e);
-		}
-	}
-
 	public Map<ContactId, TransportKeys> getTransportKeys(Connection txn,
 			TransportId t) throws DbException {
 		PreparedStatement ps = null;
diff --git a/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java b/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java
index da217728a3221daacd937b8d617f14a6e275b8fa..bfb768802179b112b345d8d675b9d8acc211c79d 100644
--- a/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java
+++ b/briar-core/src/org/briarproject/sync/AuthorFactoryImpl.java
@@ -16,6 +16,7 @@ import javax.inject.Inject;
 
 import static org.briarproject.api.db.StorageStatus.ADDING;
 
+// TODO: Move this class to the identity package
 class AuthorFactoryImpl implements AuthorFactory {
 
 	private final CryptoComponent crypto;
diff --git a/briar-core/src/org/briarproject/sync/AuthorReader.java b/briar-core/src/org/briarproject/sync/AuthorReader.java
index 8a4b85300200ef9993d3a8b04aa6cb2f0213f497..cb697681eebfdf0d318679625fdb6a98d87a6c7b 100644
--- a/briar-core/src/org/briarproject/sync/AuthorReader.java
+++ b/briar-core/src/org/briarproject/sync/AuthorReader.java
@@ -11,6 +11,7 @@ import java.io.IOException;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 
+// TODO: Move this class to the identity package
 class AuthorReader implements ObjectReader<Author> {
 
 	private final AuthorFactory authorFactory;
diff --git a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
index 29725fe81741c293093578e302692231cee403e3..2f548fff76e16fae1cf7a739fa4ecf56ae5c6cf3 100644
--- a/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/sync/DuplexOutgoingSession.java
@@ -21,8 +21,6 @@ import org.briarproject.api.sync.Ack;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.SyncSession;
 import org.briarproject.api.system.Clock;
 
@@ -87,9 +85,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 	public void run() throws IOException {
 		eventBus.addListener(this);
 		try {
-			// Start a query for each type of packet, in order of urgency
-			dbExecutor.execute(new GenerateSubscriptionAck());
-			dbExecutor.execute(new GenerateSubscriptionUpdate());
+			// Start a query for each type of packet
 			dbExecutor.execute(new GenerateAck());
 			dbExecutor.execute(new GenerateBatch());
 			dbExecutor.execute(new GenerateOffer());
@@ -118,7 +114,6 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 						now = clock.currentTimeMillis();
 						if (now >= nextRetxQuery) {
 							// Check for retransmittable packets
-							dbExecutor.execute(new GenerateSubscriptionUpdate());
 							dbExecutor.execute(new GenerateBatch());
 							dbExecutor.execute(new GenerateOffer());
 							nextRetxQuery = now + RETX_QUERY_INTERVAL;
@@ -163,10 +158,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 		} else if (e instanceof LocalSubscriptionsUpdatedEvent) {
 			LocalSubscriptionsUpdatedEvent l =
 					(LocalSubscriptionsUpdatedEvent) e;
-			if (l.getAffectedContacts().contains(contactId)) {
-				dbExecutor.execute(new GenerateSubscriptionUpdate());
+			if (l.getAffectedContacts().contains(contactId))
 				dbExecutor.execute(new GenerateOffer());
-			}
 		} else if (e instanceof MessageRequestedEvent) {
 			if (((MessageRequestedEvent) e).getContactId().equals(contactId))
 				dbExecutor.execute(new GenerateBatch());
@@ -179,10 +172,8 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 		} else if (e instanceof RemoteSubscriptionsUpdatedEvent) {
 			RemoteSubscriptionsUpdatedEvent r =
 					(RemoteSubscriptionsUpdatedEvent) e;
-			if (r.getContactId().equals(contactId)) {
-				dbExecutor.execute(new GenerateSubscriptionAck());
+			if (r.getContactId().equals(contactId))
 				dbExecutor.execute(new GenerateOffer());
-			}
 		} else if (e instanceof ShutdownEvent) {
 			interrupt();
 		} else if (e instanceof TransportRemovedEvent) {
@@ -332,75 +323,4 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
 			dbExecutor.execute(new GenerateRequest());
 		}
 	}
-
-	// This task runs on the database thread
-	private class GenerateSubscriptionAck implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				SubscriptionAck a = db.generateSubscriptionAck(contactId);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated subscription ack: " + (a != null));
-				if (a != null) writerTasks.add(new WriteSubscriptionAck(a));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This tasks runs on the writer thread
-	private class WriteSubscriptionAck
-	implements ThrowingRunnable<IOException> {
-
-		private final SubscriptionAck ack;
-
-		private WriteSubscriptionAck(SubscriptionAck ack) {
-			this.ack = ack;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeSubscriptionAck(ack);
-			LOG.info("Sent subscription ack");
-			dbExecutor.execute(new GenerateSubscriptionAck());
-		}
-	}
-
-	// This task runs on the database thread
-	private class GenerateSubscriptionUpdate implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				SubscriptionUpdate u =
-						db.generateSubscriptionUpdate(contactId, maxLatency);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated subscription update: " + (u != null));
-				if (u != null) writerTasks.add(new WriteSubscriptionUpdate(u));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This task runs on the writer thread
-	private class WriteSubscriptionUpdate
-	implements ThrowingRunnable<IOException> {
-
-		private final SubscriptionUpdate update;
-
-		private WriteSubscriptionUpdate(SubscriptionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeSubscriptionUpdate(update);
-			LOG.info("Sent subscription update");
-			dbExecutor.execute(new GenerateSubscriptionUpdate());
-		}
-	}
 }
diff --git a/briar-core/src/org/briarproject/sync/GroupReader.java b/briar-core/src/org/briarproject/sync/GroupReader.java
deleted file mode 100644
index 2500c8b1c968ed69d628fcdc0d4f6575fe90b4ec..0000000000000000000000000000000000000000
--- a/briar-core/src/org/briarproject/sync/GroupReader.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.briarproject.sync;
-
-import org.briarproject.api.FormatException;
-import org.briarproject.api.UniqueId;
-import org.briarproject.api.data.BdfReader;
-import org.briarproject.api.data.ObjectReader;
-import org.briarproject.api.sync.ClientId;
-import org.briarproject.api.sync.Group;
-import org.briarproject.api.sync.GroupFactory;
-
-import java.io.IOException;
-
-import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
-
-class GroupReader implements ObjectReader<Group> {
-
-	private final GroupFactory groupFactory;
-
-	GroupReader(GroupFactory groupFactory) {
-		this.groupFactory = groupFactory;
-	}
-
-	public Group readObject(BdfReader r) throws IOException {
-		r.readListStart();
-		byte[] id = r.readRaw(UniqueId.LENGTH);
-		if (id.length != UniqueId.LENGTH) throw new FormatException();
-		byte[] descriptor = r.readRaw(MAX_GROUP_DESCRIPTOR_LENGTH);
-		r.readListEnd();
-		return groupFactory.createGroup(new ClientId(id), descriptor);
-	}
-}
diff --git a/briar-core/src/org/briarproject/sync/IncomingSession.java b/briar-core/src/org/briarproject/sync/IncomingSession.java
index 7c7444664ca84f99c426ab93dca6e8e333711dc0..6fbfdd2426ce1938f665f83d7dad59124b0dd22d 100644
--- a/briar-core/src/org/briarproject/sync/IncomingSession.java
+++ b/briar-core/src/org/briarproject/sync/IncomingSession.java
@@ -16,8 +16,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketReader;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.SyncSession;
 
 import java.io.IOException;
@@ -69,12 +67,6 @@ class IncomingSession implements SyncSession, EventListener {
 				} else if (packetReader.hasRequest()) {
 					Request r = packetReader.readRequest();
 					dbExecutor.execute(new ReceiveRequest(r));
-				} else if (packetReader.hasSubscriptionAck()) {
-					SubscriptionAck a = packetReader.readSubscriptionAck();
-					dbExecutor.execute(new ReceiveSubscriptionAck(a));
-				} else if (packetReader.hasSubscriptionUpdate()) {
-					SubscriptionUpdate u = packetReader.readSubscriptionUpdate();
-					dbExecutor.execute(new ReceiveSubscriptionUpdate(u));
 				} else {
 					throw new FormatException();
 				}
@@ -172,40 +164,4 @@ class IncomingSession implements SyncSession, EventListener {
 			}
 		}
 	}
-
-	private class ReceiveSubscriptionAck implements Runnable {
-
-		private final SubscriptionAck ack;
-
-		private ReceiveSubscriptionAck(SubscriptionAck ack) {
-			this.ack = ack;
-		}
-
-		public void run() {
-			try {
-				db.receiveSubscriptionAck(contactId, ack);
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	private class ReceiveSubscriptionUpdate implements Runnable {
-
-		private final SubscriptionUpdate update;
-
-		private ReceiveSubscriptionUpdate(SubscriptionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() {
-			try {
-				db.receiveSubscriptionUpdate(contactId, update);
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
 }
diff --git a/briar-core/src/org/briarproject/sync/PacketReaderFactoryImpl.java b/briar-core/src/org/briarproject/sync/PacketReaderFactoryImpl.java
index eac4b931f9906d6e3a34cc105ea16713ac1a1148..aad376cc406eda6817cac9c35854d8b2a9574687 100644
--- a/briar-core/src/org/briarproject/sync/PacketReaderFactoryImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketReaderFactoryImpl.java
@@ -1,11 +1,8 @@
 package org.briarproject.sync;
 
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.data.BdfReaderFactory;
-import org.briarproject.api.data.ObjectReader;
 import org.briarproject.api.sync.PacketReader;
 import org.briarproject.api.sync.PacketReaderFactory;
-import org.briarproject.api.sync.SubscriptionUpdate;
 
 import java.io.InputStream;
 
@@ -14,20 +11,13 @@ import javax.inject.Inject;
 class PacketReaderFactoryImpl implements PacketReaderFactory {
 
 	private final CryptoComponent crypto;
-	private final BdfReaderFactory bdfReaderFactory;
-	private final ObjectReader<SubscriptionUpdate> subscriptionUpdateReader;
 
 	@Inject
-	PacketReaderFactoryImpl(CryptoComponent crypto,
-			BdfReaderFactory bdfReaderFactory,
-			ObjectReader<SubscriptionUpdate> subscriptionUpdateReader) {
+	PacketReaderFactoryImpl(CryptoComponent crypto) {
 		this.crypto = crypto;
-		this.bdfReaderFactory = bdfReaderFactory;
-		this.subscriptionUpdateReader = subscriptionUpdateReader;
 	}
 
 	public PacketReader createPacketReader(InputStream in) {
-		return new PacketReaderImpl(crypto, bdfReaderFactory,
-				subscriptionUpdateReader, in);
+		return new PacketReaderImpl(crypto, in);
 	}
 }
diff --git a/briar-core/src/org/briarproject/sync/PacketReaderImpl.java b/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
index 8c9a10ada622c317903e837b5cdbda11397e2b71..4eea8831e8bce33edff90f80fefd2afbc2c45505 100644
--- a/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketReaderImpl.java
@@ -3,9 +3,6 @@ package org.briarproject.sync;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.UniqueId;
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.data.BdfReader;
-import org.briarproject.api.data.BdfReaderFactory;
-import org.briarproject.api.data.ObjectReader;
 import org.briarproject.api.sync.Ack;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
@@ -13,11 +10,8 @@ import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketReader;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.util.ByteUtils;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -28,8 +22,6 @@ import static org.briarproject.api.sync.PacketTypes.ACK;
 import static org.briarproject.api.sync.PacketTypes.MESSAGE;
 import static org.briarproject.api.sync.PacketTypes.OFFER;
 import static org.briarproject.api.sync.PacketTypes.REQUEST;
-import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_ACK;
-import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_UPDATE;
 import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.PACKET_HEADER_LENGTH;
@@ -41,20 +33,14 @@ class PacketReaderImpl implements PacketReader {
 	private enum State { BUFFER_EMPTY, BUFFER_FULL, EOF }
 
 	private final CryptoComponent crypto;
-	private final BdfReaderFactory bdfReaderFactory;
-	private final ObjectReader<SubscriptionUpdate> subscriptionUpdateReader;
 	private final InputStream in;
 	private final byte[] header, payload;
 
 	private State state = State.BUFFER_EMPTY;
 	private int payloadLength = 0;
 
-	PacketReaderImpl(CryptoComponent crypto, BdfReaderFactory bdfReaderFactory,
-			ObjectReader<SubscriptionUpdate> subscriptionUpdateReader,
-			InputStream in) {
+	PacketReaderImpl(CryptoComponent crypto, InputStream in) {
 		this.crypto = crypto;
-		this.bdfReaderFactory = bdfReaderFactory;
-		this.subscriptionUpdateReader = subscriptionUpdateReader;
 		this.in = in;
 		header = new byte[PACKET_HEADER_LENGTH];
 		payload = new byte[MAX_PACKET_PAYLOAD_LENGTH];
@@ -156,42 +142,4 @@ class PacketReaderImpl implements PacketReader {
 		if (!hasRequest()) throw new FormatException();
 		return new Request(Collections.unmodifiableList(readMessageIds()));
 	}
-
-	public boolean hasSubscriptionAck() throws IOException {
-		return !eof() && header[1] == SUBSCRIPTION_ACK;
-	}
-
-	public SubscriptionAck readSubscriptionAck() throws IOException {
-		if (!hasSubscriptionAck()) throw new FormatException();
-		// Set up the reader
-		InputStream bais = new ByteArrayInputStream(payload, 0, payloadLength);
-		BdfReader r = bdfReaderFactory.createReader(bais);
-		// Read the start of the payload
-		r.readListStart();
-		// Read the version
-		long version = r.readInteger();
-		if (version < 0) throw new FormatException();
-		// Read the end of the payload
-		r.readListEnd();
-		if (!r.eof()) throw new FormatException();
-		state = State.BUFFER_EMPTY;
-		// Build and return the subscription ack
-		return new SubscriptionAck(version);
-	}
-
-	public boolean hasSubscriptionUpdate() throws IOException {
-		return !eof() && header[1] == SUBSCRIPTION_UPDATE;
-	}
-
-	public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
-		if (!hasSubscriptionUpdate()) throw new FormatException();
-		// Set up the reader
-		InputStream bais = new ByteArrayInputStream(payload, 0, payloadLength);
-		BdfReader r = bdfReaderFactory.createReader(bais);
-		// Read and build the subscription update
-		SubscriptionUpdate u = subscriptionUpdateReader.readObject(r);
-		if (!r.eof()) throw new FormatException();
-		state = State.BUFFER_EMPTY;
-		return u;
-	}
 }
diff --git a/briar-core/src/org/briarproject/sync/PacketWriterFactoryImpl.java b/briar-core/src/org/briarproject/sync/PacketWriterFactoryImpl.java
index 4c1b3fbb89660c943cc3a6f8dada5c62dcc37585..b5da5e653a1f279a115fff335bb61ae55a133b5a 100644
--- a/briar-core/src/org/briarproject/sync/PacketWriterFactoryImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketWriterFactoryImpl.java
@@ -1,23 +1,13 @@
 package org.briarproject.sync;
 
-import org.briarproject.api.data.BdfWriterFactory;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.PacketWriterFactory;
 
 import java.io.OutputStream;
 
-import javax.inject.Inject;
-
 class PacketWriterFactoryImpl implements PacketWriterFactory {
 
-	private final BdfWriterFactory bdfWriterFactory;
-
-	@Inject
-	PacketWriterFactoryImpl(BdfWriterFactory bdfWriterFactory) {
-		this.bdfWriterFactory = bdfWriterFactory;
-	}
-
 	public PacketWriter createPacketWriter(OutputStream out) {
-		return new PacketWriterImpl(bdfWriterFactory, out);
+		return new PacketWriterImpl(out);
 	}
 }
diff --git a/briar-core/src/org/briarproject/sync/PacketWriterImpl.java b/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
index 6ecc62eefd7607d2be9cb4c578720d1a36096fe8..f2e8817710e9d4147932c94cec9c4810995a17f0 100644
--- a/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
+++ b/briar-core/src/org/briarproject/sync/PacketWriterImpl.java
@@ -1,17 +1,12 @@
 package org.briarproject.sync;
 
 import org.briarproject.api.UniqueId;
-import org.briarproject.api.data.BdfWriter;
-import org.briarproject.api.data.BdfWriterFactory;
 import org.briarproject.api.sync.Ack;
-import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketTypes;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.util.ByteUtils;
 
 import java.io.ByteArrayOutputStream;
@@ -21,8 +16,6 @@ import java.io.OutputStream;
 import static org.briarproject.api.sync.PacketTypes.ACK;
 import static org.briarproject.api.sync.PacketTypes.OFFER;
 import static org.briarproject.api.sync.PacketTypes.REQUEST;
-import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_ACK;
-import static org.briarproject.api.sync.PacketTypes.SUBSCRIPTION_UPDATE;
 import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.PACKET_HEADER_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.PROTOCOL_VERSION;
@@ -30,13 +23,11 @@ import static org.briarproject.api.sync.SyncConstants.PROTOCOL_VERSION;
 // This class is not thread-safe
 class PacketWriterImpl implements PacketWriter {
 
-	private final BdfWriterFactory bdfWriterFactory;
 	private final OutputStream out;
 	private final byte[] header;
 	private final ByteArrayOutputStream payload;
 
-	PacketWriterImpl(BdfWriterFactory bdfWriterFactory, OutputStream out) {
-		this.bdfWriterFactory = bdfWriterFactory;
+	PacketWriterImpl(OutputStream out) {
 		this.out = out;
 		header = new byte[PACKET_HEADER_LENGTH];
 		header[0] = PROTOCOL_VERSION;
@@ -94,33 +85,6 @@ class PacketWriterImpl implements PacketWriter {
 		writePacket(REQUEST);
 	}
 
-	public void writeSubscriptionAck(SubscriptionAck a) throws IOException {
-		if (payload.size() != 0) throw new IllegalStateException();
-		BdfWriter w = bdfWriterFactory.createWriter(payload);
-		w.writeListStart();
-		w.writeInteger(a.getVersion());
-		w.writeListEnd();
-		writePacket(SUBSCRIPTION_ACK);
-	}
-
-	public void writeSubscriptionUpdate(SubscriptionUpdate u)
-			throws IOException {
-		if (payload.size() != 0) throw new IllegalStateException();
-		BdfWriter w = bdfWriterFactory.createWriter(payload);
-		w.writeListStart();
-		w.writeListStart();
-		for (Group g : u.getGroups()) {
-			w.writeListStart();
-			w.writeRaw(g.getClientId().getBytes());
-			w.writeRaw(g.getDescriptor());
-			w.writeListEnd();
-		}
-		w.writeListEnd();
-		w.writeInteger(u.getVersion());
-		w.writeListEnd();
-		writePacket(SUBSCRIPTION_UPDATE);
-	}
-
 	public void flush() throws IOException {
 		out.flush();
 	}
diff --git a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
index 68e319abd262ec2ba892fd84c9755fc2577d96d5..0290b7d83503cd676ace8f8aaebb9d15875ae778 100644
--- a/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
+++ b/briar-core/src/org/briarproject/sync/SimplexOutgoingSession.java
@@ -12,8 +12,6 @@ import org.briarproject.api.event.ShutdownEvent;
 import org.briarproject.api.event.TransportRemovedEvent;
 import org.briarproject.api.sync.Ack;
 import org.briarproject.api.sync.PacketWriter;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.SyncSession;
 
 import java.io.IOException;
@@ -66,16 +64,14 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
 		this.transportId = transportId;
 		this.maxLatency = maxLatency;
 		this.packetWriter = packetWriter;
-		outstandingQueries = new AtomicInteger(4); // One per type of packet
+		outstandingQueries = new AtomicInteger(2); // One per type of packet
 		writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
 	}
 
 	public void run() throws IOException {
 		eventBus.addListener(this);
 		try {
-			// Start a query for each type of packet, in order of urgency
-			dbExecutor.execute(new GenerateSubscriptionAck());
-			dbExecutor.execute(new GenerateSubscriptionUpdate());
+			// Start a query for each type of packet
 			dbExecutor.execute(new GenerateAck());
 			dbExecutor.execute(new GenerateBatch());
 			// Write packets until interrupted or no more packets to write
@@ -187,77 +183,4 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
 			dbExecutor.execute(new GenerateBatch());
 		}
 	}
-
-	// This task runs on the database thread
-	private class GenerateSubscriptionAck implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				SubscriptionAck a = db.generateSubscriptionAck(contactId);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated subscription ack: " + (a != null));
-				if (a == null) decrementOutstandingQueries();
-				else writerTasks.add(new WriteSubscriptionAck(a));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This tasks runs on the writer thread
-	private class WriteSubscriptionAck
-	implements ThrowingRunnable<IOException> {
-
-		private final SubscriptionAck ack;
-
-		private WriteSubscriptionAck(SubscriptionAck ack) {
-			this.ack = ack;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeSubscriptionAck(ack);
-			LOG.info("Sent subscription ack");
-			dbExecutor.execute(new GenerateSubscriptionAck());
-		}
-	}
-
-	// This task runs on the database thread
-	private class GenerateSubscriptionUpdate implements Runnable {
-
-		public void run() {
-			if (interrupted) return;
-			try {
-				SubscriptionUpdate u =
-						db.generateSubscriptionUpdate(contactId, maxLatency);
-				if (LOG.isLoggable(INFO))
-					LOG.info("Generated subscription update: " + (u != null));
-				if (u == null) decrementOutstandingQueries();
-				else writerTasks.add(new WriteSubscriptionUpdate(u));
-			} catch (DbException e) {
-				if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
-				interrupt();
-			}
-		}
-	}
-
-	// This task runs on the writer thread
-	private class WriteSubscriptionUpdate
-	implements ThrowingRunnable<IOException> {
-
-		private final SubscriptionUpdate update;
-
-		private WriteSubscriptionUpdate(SubscriptionUpdate update) {
-			this.update = update;
-		}
-
-		public void run() throws IOException {
-			if (interrupted) return;
-			packetWriter.writeSubscriptionUpdate(update);
-			LOG.info("Sent subscription update");
-			dbExecutor.execute(new GenerateSubscriptionUpdate());
-		}
-	}
 }
diff --git a/briar-core/src/org/briarproject/sync/SubscriptionUpdateReader.java b/briar-core/src/org/briarproject/sync/SubscriptionUpdateReader.java
deleted file mode 100644
index 38a297f214d4aa5938038ea2b4a3ad074f01eeb9..0000000000000000000000000000000000000000
--- a/briar-core/src/org/briarproject/sync/SubscriptionUpdateReader.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.briarproject.sync;
-
-import org.briarproject.api.FormatException;
-import org.briarproject.api.data.BdfReader;
-import org.briarproject.api.data.ObjectReader;
-import org.briarproject.api.sync.Group;
-import org.briarproject.api.sync.GroupId;
-import org.briarproject.api.sync.SubscriptionUpdate;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import static org.briarproject.api.sync.SyncConstants.MAX_SUBSCRIPTIONS;
-
-class SubscriptionUpdateReader implements ObjectReader<SubscriptionUpdate> {
-
-	private final ObjectReader<Group> groupReader;
-
-	SubscriptionUpdateReader(ObjectReader<Group> groupReader) {
-		this.groupReader = groupReader;
-	}
-
-	public SubscriptionUpdate readObject(BdfReader r) throws IOException {
-		r.readListStart();
-		List<Group> groups = new ArrayList<Group>();
-		Set<GroupId> ids = new HashSet<GroupId>();
-		r.readListStart();
-		for (int i = 0; i < MAX_SUBSCRIPTIONS && !r.hasListEnd(); i++) {
-			Group g = groupReader.readObject(r);
-			if (!ids.add(g.getId())) throw new FormatException(); // Duplicate
-			groups.add(g);
-		}
-		r.readListEnd();
-		long version = r.readInteger();
-		if (version < 0) throw new FormatException();
-		r.readListEnd();
-		groups = Collections.unmodifiableList(groups);
-		return new SubscriptionUpdate(groups, version);
-	}
-}
diff --git a/briar-core/src/org/briarproject/sync/SyncModule.java b/briar-core/src/org/briarproject/sync/SyncModule.java
index 0b4a9a2c3b1bb8033d85925a00e2b64408fbc8a5..7a61c68a4e174f8848a270e8b7db9a4d7bbf18da 100644
--- a/briar-core/src/org/briarproject/sync/SyncModule.java
+++ b/briar-core/src/org/briarproject/sync/SyncModule.java
@@ -8,13 +8,11 @@ import org.briarproject.api.event.EventBus;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.lifecycle.LifecycleManager;
-import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.PacketReaderFactory;
 import org.briarproject.api.sync.PacketWriterFactory;
 import org.briarproject.api.sync.PrivateGroupFactory;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.sync.SyncSessionFactory;
 import org.briarproject.api.sync.ValidationManager;
 
@@ -39,17 +37,6 @@ public class SyncModule extends AbstractModule {
 		return new AuthorReader(authorFactory);
 	}
 
-	@Provides
-	ObjectReader<Group> getGroupReader(GroupFactory groupFactory) {
-		return new GroupReader(groupFactory);
-	}
-
-	@Provides
-	ObjectReader<SubscriptionUpdate> getSubscriptionUpdateReader(
-			ObjectReader<Group> groupReader) {
-		return new SubscriptionUpdateReader(groupReader);
-	}
-
 	@Provides @Singleton
 	ValidationManager getValidationManager(LifecycleManager lifecycleManager,
 			EventBus eventBus, ValidationManagerImpl validationManager) {
diff --git a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
index bcd2d4acb85402f78f8b2a4c454c497f88676439..e8c939d4cfe0811c6e71f3af8945e08dd8d52eed 100644
--- a/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/ProtocolIntegrationTest.java
@@ -19,7 +19,6 @@ import org.briarproject.api.sync.PacketReaderFactory;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.PacketWriterFactory;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.transport.StreamContext;
 import org.briarproject.api.transport.StreamReaderFactory;
 import org.briarproject.api.transport.StreamWriterFactory;
@@ -37,7 +36,6 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 
 import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
 import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
@@ -56,7 +54,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 	private final ContactId contactId;
 	private final TransportId transportId;
 	private final SecretKey tagKey, headerKey;
-	private final Group group;
 	private final Message message, message1;
 	private final Collection<MessageId> messageIds;
 
@@ -79,7 +76,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 		GroupFactory groupFactory = i.getInstance(GroupFactory.class);
 		ClientId clientId = new ClientId(TestUtils.getRandomId());
 		byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
-		group = groupFactory.createGroup(clientId, descriptor);
+		Group group = groupFactory.createGroup(clientId, descriptor);
 		// Add two messages to the group
 		MessageFactory messageFactory = i.getInstance(MessageFactory.class);
 		long timestamp = System.currentTimeMillis();
@@ -114,10 +111,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 
 		packetWriter.writeRequest(new Request(messageIds));
 
-		SubscriptionUpdate su = new SubscriptionUpdate(
-				Collections.singletonList(group), 1);
-		packetWriter.writeSubscriptionUpdate(su);
-
 		streamWriter.flush();
 		return out.toByteArray();
 	}
@@ -158,12 +151,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
 		Request req = packetReader.readRequest();
 		assertEquals(messageIds, req.getMessageIds());
 
-		// Read the subscription update
-		assertTrue(packetReader.hasSubscriptionUpdate());
-		SubscriptionUpdate su = packetReader.readSubscriptionUpdate();
-		assertEquals(Collections.singletonList(group), su.getGroups());
-		assertEquals(1, su.getVersion());
-
 		in.close();
 	}
 
diff --git a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
index abc95eda18a4abfe3813e63a9c3299fa9563ac1b..6d8a2a61fb3d6edfce564d3de2f52d00a728aa08 100644
--- a/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
+++ b/briar-tests/src/org/briarproject/db/DatabaseComponentImplTest.java
@@ -40,8 +40,6 @@ import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionAck;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.api.transport.IncomingKeys;
 import org.briarproject.api.transport.OutgoingKeys;
 import org.briarproject.api.transport.TransportKeys;
@@ -60,7 +58,6 @@ import static org.briarproject.api.sync.ValidationManager.Validity.VALID;
 import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
 public class DatabaseComponentImplTest extends BriarTestCase {
@@ -300,11 +297,11 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		final EventBus eventBus = context.mock(EventBus.class);
 		context.checking(new Expectations() {{
 			// Check whether the contact is in the DB (which it's not)
-			exactly(17).of(database).startTransaction();
+			exactly(13).of(database).startTransaction();
 			will(returnValue(txn));
-			exactly(17).of(database).containsContact(txn, contactId);
+			exactly(13).of(database).containsContact(txn, contactId);
 			will(returnValue(false));
-			exactly(17).of(database).abortTransaction(txn);
+			exactly(13).of(database).abortTransaction(txn);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, eventBus,
 				shutdown);
@@ -337,20 +334,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			// Expected
 		}
 
-		try {
-			db.generateSubscriptionAck(contactId);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
-		try {
-			db.generateSubscriptionUpdate(contactId, 123);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
 		try {
 			db.getContact(contactId);
 			fail();
@@ -402,23 +385,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 			// Expected
 		}
 
-		try {
-			SubscriptionAck a = new SubscriptionAck(0);
-			db.receiveSubscriptionAck(contactId, a);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
-		try {
-			SubscriptionUpdate u = new SubscriptionUpdate(
-					Collections.<Group>emptyList(), 1);
-			db.receiveSubscriptionUpdate(contactId, u);
-			fail();
-		} catch (NoSuchContactException expected) {
-			// Expected
-		}
-
 		try {
 			db.removeContact(contactId);
 			fail();
@@ -856,58 +822,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	@Test
-	public void testGenerateSubscriptionUpdateNoUpdateDue() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency);
-			will(returnValue(null));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, eventBus,
-				shutdown);
-
-		assertNull(db.generateSubscriptionUpdate(contactId, maxLatency));
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testGenerateSubscriptionUpdate() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).getSubscriptionUpdate(txn, contactId, maxLatency);
-			will(returnValue(new SubscriptionUpdate(
-					Collections.singletonList(group), 1)));
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, eventBus,
-				shutdown);
-
-		SubscriptionUpdate u = db.generateSubscriptionUpdate(contactId,
-				maxLatency);
-		assertEquals(Collections.singletonList(group), u.getGroups());
-		assertEquals(1, u.getVersion());
-
-		context.assertIsSatisfied();
-	}
-
 	@Test
 	public void testReceiveAck() throws Exception {
 		Mockery context = new Mockery();
@@ -1103,56 +1017,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	@Test
-	public void testReceiveSubscriptionAck() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).setSubscriptionUpdateAcked(txn, contactId, 1);
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, eventBus,
-				shutdown);
-
-		SubscriptionAck a = new SubscriptionAck(1);
-		db.receiveSubscriptionAck(contactId, a);
-
-		context.assertIsSatisfied();
-	}
-
-	@Test
-	public void testReceiveSubscriptionUpdate() throws Exception {
-		Mockery context = new Mockery();
-		@SuppressWarnings("unchecked")
-		final Database<Object> database = context.mock(Database.class);
-		final ShutdownManager shutdown = context.mock(ShutdownManager.class);
-		final EventBus eventBus = context.mock(EventBus.class);
-		context.checking(new Expectations() {{
-			oneOf(database).startTransaction();
-			will(returnValue(txn));
-			oneOf(database).containsContact(txn, contactId);
-			will(returnValue(true));
-			oneOf(database).setGroups(txn, contactId,
-					Collections.singletonList(group), 1);
-			oneOf(database).commitTransaction(txn);
-		}});
-		DatabaseComponent db = createDatabaseComponent(database, eventBus,
-				shutdown);
-
-		SubscriptionUpdate u = new SubscriptionUpdate(
-				Collections.singletonList(group), 1);
-		db.receiveSubscriptionUpdate(contactId, u);
-
-		context.assertIsSatisfied();
-	}
-
 	@Test
 	public void testChangingVisibilityCallsListeners() throws Exception {
 		final ContactId contactId1 = new ContactId(123);
diff --git a/briar-tests/src/org/briarproject/sync/ConstantsTest.java b/briar-tests/src/org/briarproject/sync/ConstantsTest.java
index 4d0d498a4adcd5f3be7073ac41f9cb95af1c7504..ac3d4334f3fb7671d34afbeb6a7b1a33dbec14b6 100644
--- a/briar-tests/src/org/briarproject/sync/ConstantsTest.java
+++ b/briar-tests/src/org/briarproject/sync/ConstantsTest.java
@@ -22,16 +22,12 @@ import org.briarproject.api.messaging.MessagingConstants;
 import org.briarproject.api.messaging.PrivateMessage;
 import org.briarproject.api.messaging.PrivateMessageFactory;
 import org.briarproject.api.sync.Ack;
-import org.briarproject.api.sync.ClientId;
-import org.briarproject.api.sync.Group;
-import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.Offer;
 import org.briarproject.api.sync.PacketWriter;
 import org.briarproject.api.sync.PacketWriterFactory;
 import org.briarproject.api.sync.Request;
-import org.briarproject.api.sync.SubscriptionUpdate;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
 import org.briarproject.data.DataModule;
@@ -52,9 +48,7 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENG
 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.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
-import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
 import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
-import static org.briarproject.api.sync.SyncConstants.MAX_SUBSCRIPTIONS;
 import static org.junit.Assert.assertTrue;
 
 public class ConstantsTest extends BriarTestCase {
@@ -62,7 +56,6 @@ public class ConstantsTest extends BriarTestCase {
 	// TODO: Break this up into tests that are relevant for each package
 
 	private final CryptoComponent crypto;
-	private final GroupFactory groupFactory;
 	private final AuthorFactory authorFactory;
 	private final PrivateMessageFactory privateMessageFactory;
 	private final ForumPostFactory forumPostFactory;
@@ -75,7 +68,6 @@ public class ConstantsTest extends BriarTestCase {
 				new DataModule(), new EventModule(), new ForumModule(),
 				new IdentityModule(), new MessagingModule(), new SyncModule());
 		crypto = i.getInstance(CryptoComponent.class);
-		groupFactory = i.getInstance(GroupFactory.class);
 		authorFactory = i.getInstance(AuthorFactory.class);
 		privateMessageFactory = i.getInstance(PrivateMessageFactory.class);
 		forumPostFactory = i.getInstance(ForumPostFactory.class);
@@ -188,27 +180,6 @@ public class ConstantsTest extends BriarTestCase {
 		testMessageIdsFitIntoRequest(1000);
 	}
 
-	@Test
-	public void testGroupsFitIntoSubscriptionUpdate() throws Exception {
-		// Create the maximum number of maximum-length groups
-		Random random = new Random();
-		ClientId clientId = new ClientId(TestUtils.getRandomId());
-		Collection<Group> groups = new ArrayList<Group>();
-		for (int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
-			byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
-			random.nextBytes(descriptor);
-			groups.add(groupFactory.createGroup(clientId, descriptor));
-		}
-		// Create a maximum-length subscription update
-		SubscriptionUpdate u = new SubscriptionUpdate(groups, Long.MAX_VALUE);
-		// Serialise the update
-		ByteArrayOutputStream out = new ByteArrayOutputStream();
-		PacketWriter writer = packetWriterFactory.createPacketWriter(out);
-		writer.writeSubscriptionUpdate(u);
-		// Check the size of the serialised subscription update
-		assertTrue(out.size() <= MAX_PACKET_PAYLOAD_LENGTH);
-	}
-
 	private void testMessageIdsFitIntoAck(int length) throws Exception {
 		// Create an ack with as many message IDs as possible
 		ByteArrayOutputStream out = new ByteArrayOutputStream(length);
diff --git a/briar-tests/src/org/briarproject/sync/PacketReaderImplTest.java b/briar-tests/src/org/briarproject/sync/PacketReaderImplTest.java
index 7224f5b6e28263f03d66a84c10d317af5d26531d..5bfc0971a289f2f7ca72fad708736e4f7a40b758 100644
--- a/briar-tests/src/org/briarproject/sync/PacketReaderImplTest.java
+++ b/briar-tests/src/org/briarproject/sync/PacketReaderImplTest.java
@@ -23,7 +23,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testFormatExceptionIfAckIsTooLarge() throws Exception {
 		byte[] b = createAck(true);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readAck();
 	}
 
@@ -31,7 +31,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
 		byte[] b = createAck(false);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readAck();
 	}
 
@@ -39,7 +39,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testEmptyAck() throws Exception {
 		byte[] b = createEmptyAck();
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readAck();
 	}
 
@@ -47,7 +47,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testFormatExceptionIfOfferIsTooLarge() throws Exception {
 		byte[] b = createOffer(true);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readOffer();
 	}
 
@@ -55,7 +55,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception {
 		byte[] b = createOffer(false);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readOffer();
 	}
 
@@ -63,7 +63,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testEmptyOffer() throws Exception {
 		byte[] b = createEmptyOffer();
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readOffer();
 	}
 
@@ -71,7 +71,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testFormatExceptionIfRequestIsTooLarge() throws Exception {
 		byte[] b = createRequest(true);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readRequest();
 	}
 
@@ -79,7 +79,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception {
 		byte[] b = createRequest(false);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readRequest();
 	}
 
@@ -87,7 +87,7 @@ public class PacketReaderImplTest extends BriarTestCase {
 	public void testEmptyRequest() throws Exception {
 		byte[] b = createEmptyRequest();
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
-		PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
+		PacketReaderImpl reader = new PacketReaderImpl(null, in);
 		reader.readRequest();
 	}
 
diff --git a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
index 983946f22e634a51c405c6dee458b42d79ea9618..ef2a58c476801cc4049f28ff149b92a178c00b0e 100644
--- a/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
+++ b/briar-tests/src/org/briarproject/sync/SimplexOutgoingSessionTest.java
@@ -52,12 +52,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
 		context.checking(new Expectations() {{
 			// Add listener
 			oneOf(eventBus).addListener(session);
-			// No subscription ack to send
-			oneOf(db).generateSubscriptionAck(contactId);
-			will(returnValue(null));
-			// No subscription update to send
-			oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
-			will(returnValue(null));
 			// No acks to send
 			oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
 			will(returnValue(MAX_MESSAGES_PER_ACK));
@@ -86,12 +80,6 @@ public class SimplexOutgoingSessionTest extends BriarTestCase {
 		context.checking(new Expectations() {{
 			// Add listener
 			oneOf(eventBus).addListener(session);
-			// No subscription ack to send
-			oneOf(db).generateSubscriptionAck(contactId);
-			will(returnValue(null));
-			// No subscription update to send
-			oneOf(db).generateSubscriptionUpdate(contactId, maxLatency);
-			will(returnValue(null));
 			// One ack to send
 			oneOf(packetWriter).getMaxMessagesForAck(with(any(long.class)));
 			will(returnValue(MAX_MESSAGES_PER_ACK));