diff --git a/briar-api/src/org/briarproject/api/clients/ClientHelper.java b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..04397bf6e5a6ebdf0ae8d0395620f1da2847b836
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
@@ -0,0 +1,62 @@
+package org.briarproject.api.clients;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.Transaction;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageId;
+
+import java.util.Map;
+
+public interface ClientHelper {
+
+	Message createMessage(GroupId g, long timestamp, BdfDictionary body)
+			throws FormatException;
+
+	Message createMessage(GroupId g, long timestamp, BdfList body)
+			throws FormatException;
+
+	BdfDictionary getMessageAsDictionary(MessageId m) throws DbException,
+			FormatException;
+
+	BdfDictionary getMessageAsDictionary(Transaction txn, MessageId m)
+			throws DbException, FormatException;
+
+	BdfList getMessageAsList(MessageId m) throws DbException, FormatException;
+
+	BdfList getMessageAsList(Transaction txn, MessageId m) throws DbException,
+			FormatException;
+
+	BdfDictionary getGroupMetadataAsDictionary(GroupId g) throws DbException,
+			FormatException;
+
+	BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
+			throws DbException, FormatException;
+
+	BdfDictionary getMessageMetadataAsDictionary(MessageId m) throws DbException,
+			FormatException;
+
+	BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
+			throws DbException, FormatException;
+
+	Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary(GroupId g)
+			throws DbException, FormatException;
+
+	Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
+			Transaction txn, GroupId g) throws DbException, FormatException;
+
+	void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
+			throws DbException, FormatException;
+
+	void mergeGroupMetadata(Transaction txn, GroupId g, BdfDictionary metadata)
+			throws DbException, FormatException;
+
+	void mergeMessageMetadata(MessageId m, BdfDictionary metadata)
+			throws DbException, FormatException;
+
+	void mergeMessageMetadata(Transaction txn, MessageId m,
+			BdfDictionary metadata) throws DbException, FormatException;
+}
diff --git a/briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java b/briar-api/src/org/briarproject/api/clients/PrivateGroupFactory.java
similarity index 65%
rename from briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java
rename to briar-api/src/org/briarproject/api/clients/PrivateGroupFactory.java
index 0de62ebc96b5d5807b44a89b5f0bb1999353d21d..3a87775724028aade1cf68ed3ffa732099c042ec 100644
--- a/briar-api/src/org/briarproject/api/sync/PrivateGroupFactory.java
+++ b/briar-api/src/org/briarproject/api/clients/PrivateGroupFactory.java
@@ -1,6 +1,8 @@
-package org.briarproject.api.sync;
+package org.briarproject.api.clients;
 
 import org.briarproject.api.contact.Contact;
+import org.briarproject.api.sync.ClientId;
+import org.briarproject.api.sync.Group;
 
 public interface PrivateGroupFactory {
 
diff --git a/briar-api/src/org/briarproject/api/data/BdfReader.java b/briar-api/src/org/briarproject/api/data/BdfReader.java
index 813dc86c942fbeed7e347da84e0bef9b01e6dddb..ad0efccf23a43594c2ed306f65afb511dc35a9ec 100644
--- a/briar-api/src/org/briarproject/api/data/BdfReader.java
+++ b/briar-api/src/org/briarproject/api/data/BdfReader.java
@@ -15,13 +15,13 @@ public interface BdfReader {
 	boolean readBoolean() throws IOException;
 	void skipBoolean() throws IOException;
 
-	boolean hasInteger() throws IOException;
-	long readInteger() throws IOException;
-	void skipInteger() throws IOException;
+	boolean hasLong() throws IOException;
+	long readLong() throws IOException;
+	void skipLong() throws IOException;
 
-	boolean hasFloat() throws IOException;
-	double readFloat() throws IOException;
-	void skipFloat() throws IOException;
+	boolean hasDouble() throws IOException;
+	double readDouble() throws IOException;
+	void skipDouble() throws IOException;
 
 	boolean hasString() throws IOException;
 	String readString(int maxLength) throws IOException;
@@ -32,12 +32,14 @@ public interface BdfReader {
 	void skipRaw() throws IOException;
 
 	boolean hasList() throws IOException;
+	BdfList readList() throws IOException;
 	void readListStart() throws IOException;
 	boolean hasListEnd() throws IOException;
 	void readListEnd() throws IOException;
 	void skipList() throws IOException;
 
 	boolean hasDictionary() throws IOException;
+	BdfDictionary readDictionary() throws IOException;
 	void readDictionaryStart() throws IOException;
 	boolean hasDictionaryEnd() throws IOException;
 	void readDictionaryEnd() throws IOException;
diff --git a/briar-api/src/org/briarproject/api/data/BdfWriter.java b/briar-api/src/org/briarproject/api/data/BdfWriter.java
index b0f34323112ce70babaf290a906da44ab3e14b7a..df0eb676df5c2d0ffaefed8277562312cae4225d 100644
--- a/briar-api/src/org/briarproject/api/data/BdfWriter.java
+++ b/briar-api/src/org/briarproject/api/data/BdfWriter.java
@@ -11,8 +11,8 @@ public interface BdfWriter {
 
 	void writeNull() throws IOException;
 	void writeBoolean(boolean b) throws IOException;
-	void writeInteger(long l) throws IOException;
-	void writeFloat(double d) throws IOException;
+	void writeLong(long l) throws IOException;
+	void writeDouble(double d) throws IOException;
 	void writeString(String s) throws IOException;
 	void writeRaw(byte[] b) throws IOException;
 
diff --git a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..361b631898810d65e2bc15951dc27bedb43e8733
--- /dev/null
+++ b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
@@ -0,0 +1,258 @@
+package org.briarproject.clients;
+
+import com.google.inject.Inject;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.data.BdfReader;
+import org.briarproject.api.data.BdfReaderFactory;
+import org.briarproject.api.data.BdfWriter;
+import org.briarproject.api.data.BdfWriterFactory;
+import org.briarproject.api.data.MetadataEncoder;
+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.Transaction;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageFactory;
+import org.briarproject.api.sync.MessageId;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+class ClientHelperImpl implements ClientHelper {
+
+	private final DatabaseComponent db;
+	private final MessageFactory messageFactory;
+	private final BdfReaderFactory bdfReaderFactory;
+	private final BdfWriterFactory bdfWriterFactory;
+	private final MetadataParser metadataParser;
+	private final MetadataEncoder metadataEncoder;
+
+	@Inject
+	ClientHelperImpl(DatabaseComponent db, MessageFactory messageFactory,
+			BdfReaderFactory bdfReaderFactory,
+			BdfWriterFactory bdfWriterFactory, MetadataParser metadataParser,
+			MetadataEncoder metadataEncoder) {
+		this.db = db;
+		this.messageFactory = messageFactory;
+		this.bdfReaderFactory = bdfReaderFactory;
+		this.bdfWriterFactory = bdfWriterFactory;
+		this.metadataParser = metadataParser;
+		this.metadataEncoder = metadataEncoder;
+	}
+
+	@Override
+	public Message createMessage(GroupId g, long timestamp, BdfDictionary body)
+			throws FormatException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		BdfWriter writer = bdfWriterFactory.createWriter(out);
+		try {
+			writer.writeDictionary(body);
+		} catch (FormatException e) {
+			throw e;
+		} catch (IOException e) {
+			// Shouldn't happen with ByteArrayOutputStream
+			throw new RuntimeException(e);
+		}
+		byte[] raw = out.toByteArray();
+		return messageFactory.createMessage(g, timestamp, raw);
+	}
+
+	@Override
+	public Message createMessage(GroupId g, long timestamp, BdfList body)
+			throws FormatException {
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		BdfWriter writer = bdfWriterFactory.createWriter(out);
+		try {
+			writer.writeList(body);
+		} catch (FormatException e) {
+			throw e;
+		} catch (IOException e) {
+			// Shouldn't happen with ByteArrayOutputStream
+			throw new RuntimeException(e);
+		}
+		byte[] raw = out.toByteArray();
+		return messageFactory.createMessage(g, timestamp, raw);
+	}
+
+	@Override
+	public BdfDictionary getMessageAsDictionary(MessageId m) throws DbException,
+			FormatException {
+		BdfDictionary dictionary;
+		Transaction txn = db.startTransaction();
+		try {
+			dictionary = getMessageAsDictionary(txn, m);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return dictionary;
+	}
+
+	@Override
+	public BdfDictionary getMessageAsDictionary(Transaction txn, MessageId m)
+			throws DbException, FormatException {
+		byte[] raw = db.getRawMessage(txn, m);
+		if (raw == null) return null;
+		ByteArrayInputStream in = new ByteArrayInputStream(raw);
+		BdfReader reader = bdfReaderFactory.createReader(in);
+		BdfDictionary dictionary;
+		try {
+			dictionary = reader.readDictionary();
+			if (!reader.eof()) throw new FormatException();
+		} catch (FormatException e) {
+			throw e;
+		} catch (IOException e) {
+			// Shouldn't happen with ByteArrayInputStream
+			throw new RuntimeException(e);
+		}
+		return dictionary;
+	}
+
+	@Override
+	public BdfList getMessageAsList(MessageId m) throws DbException,
+			FormatException {
+		BdfList list;
+		Transaction txn = db.startTransaction();
+		try {
+			list = getMessageAsList(txn, m);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return list;
+	}
+
+	@Override
+	public BdfList getMessageAsList(Transaction txn, MessageId m)
+			throws DbException, FormatException {
+		byte[] raw = db.getRawMessage(txn, m);
+		if (raw == null) return null;
+		ByteArrayInputStream in = new ByteArrayInputStream(raw);
+		BdfReader reader = bdfReaderFactory.createReader(in);
+		BdfList list;
+		try {
+			list = reader.readList();
+			if (!reader.eof()) throw new FormatException();
+		} catch (FormatException e) {
+			throw e;
+		} catch (IOException e) {
+			// Shouldn't happen with ByteArrayInputStream
+			throw new RuntimeException(e);
+		}
+		return list;
+	}
+
+	@Override
+	public BdfDictionary getGroupMetadataAsDictionary(GroupId g)
+			throws DbException, FormatException {
+		BdfDictionary dictionary;
+		Transaction txn = db.startTransaction();
+		try {
+			dictionary = getGroupMetadataAsDictionary(txn, g);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return dictionary;
+	}
+
+	@Override
+	public BdfDictionary getGroupMetadataAsDictionary(Transaction txn,
+			GroupId g) throws DbException, FormatException {
+		Metadata metadata = db.getGroupMetadata(txn, g);
+		return metadataParser.parse(metadata);
+	}
+
+	@Override
+	public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
+			throws DbException, FormatException {
+		BdfDictionary dictionary;
+		Transaction txn = db.startTransaction();
+		try {
+			dictionary = getMessageMetadataAsDictionary(txn, m);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return dictionary;
+	}
+
+	@Override
+	public BdfDictionary getMessageMetadataAsDictionary(Transaction txn,
+			MessageId m) throws DbException, FormatException {
+		Metadata metadata = db.getMessageMetadata(txn, m);
+		return metadataParser.parse(metadata);
+	}
+
+	@Override
+	public Map<MessageId, BdfDictionary> getMessageMetatataAsDictionary(
+			GroupId g) throws DbException, FormatException {
+		Map<MessageId, BdfDictionary> map;
+		Transaction txn = db.startTransaction();
+		try {
+			map = getMessageMetadataAsDictionary(txn, g);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return map;
+	}
+
+	@Override
+	public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
+			Transaction txn, GroupId g) throws DbException, FormatException {
+		Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g);
+		Map<MessageId, BdfDictionary> parsed =
+				new HashMap<MessageId, BdfDictionary>(raw.size());
+		for (Entry<MessageId, Metadata> e : raw.entrySet())
+			parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
+		return Collections.unmodifiableMap(parsed);
+	}
+
+	@Override
+	public void mergeGroupMetadata(GroupId g, BdfDictionary metadata)
+			throws DbException, FormatException {
+		Transaction txn = db.startTransaction();
+		try {
+			mergeGroupMetadata(txn, g, metadata);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
+
+	@Override
+	public void mergeGroupMetadata(Transaction txn, GroupId g,
+			BdfDictionary metadata) throws DbException, FormatException {
+		db.mergeGroupMetadata(txn, g, metadataEncoder.encode(metadata));
+	}
+
+	@Override
+	public void mergeMessageMetadata(MessageId m, BdfDictionary metadata)
+			throws DbException, FormatException {
+		Transaction txn = db.startTransaction();
+		try {
+			mergeMessageMetadata(txn, m, metadata);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
+
+	@Override
+	public void mergeMessageMetadata(Transaction txn, MessageId m,
+			BdfDictionary metadata) throws DbException, FormatException {
+		db.mergeMessageMetadata(txn, m, metadataEncoder.encode(metadata));
+	}
+}
diff --git a/briar-core/src/org/briarproject/clients/ClientsModule.java b/briar-core/src/org/briarproject/clients/ClientsModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..4f3a1d4a9fd616b575af179bbc2be331f89c7af6
--- /dev/null
+++ b/briar-core/src/org/briarproject/clients/ClientsModule.java
@@ -0,0 +1,15 @@
+package org.briarproject.clients;
+
+import com.google.inject.AbstractModule;
+
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.clients.PrivateGroupFactory;
+
+public class ClientsModule extends AbstractModule {
+
+	@Override
+	protected void configure() {
+		bind(ClientHelper.class).to(ClientHelperImpl.class);
+		bind(PrivateGroupFactory.class).to(PrivateGroupFactoryImpl.class);
+	}
+}
diff --git a/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java b/briar-core/src/org/briarproject/clients/PrivateGroupFactoryImpl.java
similarity index 94%
rename from briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
rename to briar-core/src/org/briarproject/clients/PrivateGroupFactoryImpl.java
index 424f8b328160f3bc0348370350cb960306eb68a2..c1842e6ba6230dca5ede95fd579c8c72d7b274de 100644
--- a/briar-core/src/org/briarproject/sync/PrivateGroupFactoryImpl.java
+++ b/briar-core/src/org/briarproject/clients/PrivateGroupFactoryImpl.java
@@ -1,8 +1,9 @@
-package org.briarproject.sync;
+package org.briarproject.clients;
 
 import com.google.inject.Inject;
 
 import org.briarproject.api.Bytes;
+import org.briarproject.api.clients.PrivateGroupFactory;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.data.BdfWriter;
 import org.briarproject.api.data.BdfWriterFactory;
@@ -10,7 +11,6 @@ import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupFactory;
-import org.briarproject.api.sync.PrivateGroupFactory;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
diff --git a/briar-core/src/org/briarproject/data/BdfReaderImpl.java b/briar-core/src/org/briarproject/data/BdfReaderImpl.java
index 71917b3bd912bb5fb3ef47578c2a265b32da132f..9c02a26dd0cd6155a20002bf853d473d6b0b8287 100644
--- a/briar-core/src/org/briarproject/data/BdfReaderImpl.java
+++ b/briar-core/src/org/briarproject/data/BdfReaderImpl.java
@@ -1,11 +1,14 @@
 package org.briarproject.data;
 
 import org.briarproject.api.FormatException;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.BdfReader;
 
 import java.io.IOException;
 import java.io.InputStream;
 
+import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
 import static org.briarproject.data.Types.DICTIONARY;
 import static org.briarproject.data.Types.END;
 import static org.briarproject.data.Types.FALSE;
@@ -74,11 +77,26 @@ class BdfReaderImpl implements BdfReader {
 		}
 	}
 
+	private Object readObject() throws IOException {
+		if (hasNull()) {
+			readNull();
+			return NULL_VALUE;
+		}
+		if (hasBoolean()) return readBoolean();
+		if (hasLong()) return readLong();
+		if (hasDouble()) return readDouble();
+		if (hasString()) return readString(Integer.MAX_VALUE);
+		if (hasRaw()) return readRaw(Integer.MAX_VALUE);
+		if (hasList()) return readList();
+		if (hasDictionary()) return readDictionary();
+		throw new FormatException();
+	}
+
 	private void skipObject() throws IOException {
 		if (hasNull()) skipNull();
 		else if (hasBoolean()) skipBoolean();
-		else if (hasInteger()) skipInteger();
-		else if (hasFloat()) skipFloat();
+		else if (hasLong()) skipLong();
+		else if (hasDouble()) skipDouble();
 		else if (hasString()) skipString();
 		else if (hasRaw()) skipRaw();
 		else if (hasList()) skipList();
@@ -129,15 +147,15 @@ class BdfReaderImpl implements BdfReader {
 		hasLookahead = false;
 	}
 
-	public boolean hasInteger() throws IOException {
+	public boolean hasLong() throws IOException {
 		if (!hasLookahead) readLookahead();
 		if (eof) return false;
 		return next == INT_8 || next == INT_16 || next == INT_32 ||
 				next == INT_64;
 	}
 
-	public long readInteger() throws IOException {
-		if (!hasInteger()) throw new FormatException();
+	public long readLong() throws IOException {
+		if (!hasLong()) throw new FormatException();
 		hasLookahead = false;
 		if (next == INT_8) return readInt8();
 		if (next == INT_16) return readInt16();
@@ -169,8 +187,8 @@ class BdfReaderImpl implements BdfReader {
 		return value;
 	}
 
-	public void skipInteger() throws IOException {
-		if (!hasInteger()) throw new FormatException();
+	public void skipLong() throws IOException {
+		if (!hasLong()) throw new FormatException();
 		if (next == INT_8) skip(1);
 		else if (next == INT_16) skip(2);
 		else if (next == INT_32) skip(4);
@@ -178,14 +196,14 @@ class BdfReaderImpl implements BdfReader {
 		hasLookahead = false;
 	}
 
-	public boolean hasFloat() throws IOException {
+	public boolean hasDouble() throws IOException {
 		if (!hasLookahead) readLookahead();
 		if (eof) return false;
 		return next == FLOAT_64;
 	}
 
-	public double readFloat() throws IOException {
-		if (!hasFloat()) throw new FormatException();
+	public double readDouble() throws IOException {
+		if (!hasDouble()) throw new FormatException();
 		hasLookahead = false;
 		readIntoBuffer(8);
 		long value = 0;
@@ -193,8 +211,8 @@ class BdfReaderImpl implements BdfReader {
 		return Double.longBitsToDouble(value);
 	}
 
-	public void skipFloat() throws IOException {
-		if (!hasFloat()) throw new FormatException();
+	public void skipDouble() throws IOException {
+		if (!hasDouble()) throw new FormatException();
 		skip(8);
 		hasLookahead = false;
 	}
@@ -268,6 +286,15 @@ class BdfReaderImpl implements BdfReader {
 		return next == LIST;
 	}
 
+	public BdfList readList() throws IOException {
+		if (!hasList()) throw new FormatException();
+		BdfList list = new BdfList();
+		readListStart();
+		while (!hasListEnd()) list.add(readObject());
+		readListEnd();
+		return list;
+	}
+
 	public void readListStart() throws IOException {
 		if (!hasList()) throw new FormatException();
 		hasLookahead = false;
@@ -305,6 +332,16 @@ class BdfReaderImpl implements BdfReader {
 		return next == DICTIONARY;
 	}
 
+	public BdfDictionary readDictionary() throws IOException {
+		if (!hasDictionary()) throw new FormatException();
+		BdfDictionary dictionary = new BdfDictionary();
+		readDictionaryStart();
+		while (!hasDictionaryEnd())
+			dictionary.put(readString(Integer.MAX_VALUE), readObject());
+		readDictionaryEnd();
+		return dictionary;
+	}
+
 	public void readDictionaryStart() throws IOException {
 		if (!hasDictionary()) throw new FormatException();
 		hasLookahead = false;
diff --git a/briar-core/src/org/briarproject/data/BdfWriterImpl.java b/briar-core/src/org/briarproject/data/BdfWriterImpl.java
index 07137184d51c0667436b31aeb87b28c234e71313..2b497dd02f21c802a9498167631acd4769dee65b 100644
--- a/briar-core/src/org/briarproject/data/BdfWriterImpl.java
+++ b/briar-core/src/org/briarproject/data/BdfWriterImpl.java
@@ -11,6 +11,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
+import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
 import static org.briarproject.data.Types.DICTIONARY;
 import static org.briarproject.data.Types.END;
 import static org.briarproject.data.Types.FALSE;
@@ -55,7 +56,7 @@ class BdfWriterImpl implements BdfWriter {
 		else out.write(FALSE);
 	}
 
-	public void writeInteger(long i) throws IOException {
+	public void writeLong(long i) throws IOException {
 		if (i >= Byte.MIN_VALUE && i <= Byte.MAX_VALUE) {
 			out.write(INT_8);
 			out.write((byte) i);
@@ -94,7 +95,7 @@ class BdfWriterImpl implements BdfWriter {
 		out.write((byte) ((i << 56) >> 56));
 	}
 
-	public void writeFloat(double d) throws IOException {
+	public void writeDouble(double d) throws IOException {
 		out.write(FLOAT_64);
 		writeInt64(Double.doubleToRawLongBits(d));
 	}
@@ -135,14 +136,14 @@ class BdfWriterImpl implements BdfWriter {
 	}
 
 	private void writeObject(Object o) throws IOException {
-		if (o == null) writeNull();
+		if (o == null || o == NULL_VALUE) writeNull();
 		else if (o instanceof Boolean) writeBoolean((Boolean) o);
-		else if (o instanceof Byte) writeInteger((Byte) o);
-		else if (o instanceof Short) writeInteger((Short) o);
-		else if (o instanceof Integer) writeInteger((Integer) o);
-		else if (o instanceof Long) writeInteger((Long) o);
-		else if (o instanceof Float) writeFloat((Float) o);
-		else if (o instanceof Double) writeFloat((Double) o);
+		else if (o instanceof Byte) writeLong((Byte) o);
+		else if (o instanceof Short) writeLong((Short) o);
+		else if (o instanceof Integer) writeLong((Integer) o);
+		else if (o instanceof Long) writeLong((Long) o);
+		else if (o instanceof Float) writeDouble((Float) o);
+		else if (o instanceof Double) writeDouble((Double) o);
 		else if (o instanceof String) writeString((String) o);
 		else if (o instanceof byte[]) writeRaw((byte[]) o);
 		else if (o instanceof Bytes) writeRaw(((Bytes) o).getBytes());
diff --git a/briar-core/src/org/briarproject/forum/ForumListValidator.java b/briar-core/src/org/briarproject/forum/ForumListValidator.java
index 98d905c2989f081368cb4ae104460d17f73ebe6d..0a8e04e3226147a03757544112926878a2be37e1 100644
--- a/briar-core/src/org/briarproject/forum/ForumListValidator.java
+++ b/briar-core/src/org/briarproject/forum/ForumListValidator.java
@@ -41,7 +41,7 @@ class ForumListValidator implements MessageValidator {
 					MESSAGE_HEADER_LENGTH, raw.length - MESSAGE_HEADER_LENGTH);
 			BdfReader r = bdfReaderFactory.createReader(in);
 			r.readListStart();
-			long version = r.readInteger();
+			long version = r.readLong();
 			if (version < 0) throw new FormatException();
 			r.readListStart();
 			while (!r.hasListEnd()) {
diff --git a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
index 515310aa8fed400569f11b816e972101d5a89a76..c598dc1c3162d45483f79fb76aa66bcd5abcfa57 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
@@ -77,7 +77,7 @@ class ForumPostFactoryImpl implements ForumPostFactory {
 		BdfWriter w = bdfWriterFactory.createWriter(out);
 		w.writeListStart();
 		w.writeRaw(groupId.getBytes());
-		w.writeInteger(timestamp);
+		w.writeLong(timestamp);
 		if (parent == null) w.writeNull();
 		else w.writeRaw(parent.getBytes());
 		writeAuthor(w, author);
diff --git a/briar-core/src/org/briarproject/forum/ForumPostValidator.java b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
index a21f546252e5c3151e6bada39fe02cf2506ccf9f..3fbcf2f08fcbc0820f1493b18ad5210ecf522fbd 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostValidator.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
@@ -117,7 +117,7 @@ class ForumPostValidator implements MessageValidator {
 				BdfWriter w = bdfWriterFactory.createWriter(out);
 				w.writeListStart();
 				w.writeRaw(m.getGroupId().getBytes());
-				w.writeInteger(m.getTimestamp());
+				w.writeLong(m.getTimestamp());
 				if (parent == null) w.writeNull();
 				else w.writeRaw(parent.getBytes());
 				writeAuthor(w, author);
diff --git a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java
index 1224a97e1e4b403e233ac73696c853f8e653c783..c80de0a21a450b42f34fb3bd74036f02756aadb5 100644
--- a/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumSharingManagerImpl.java
@@ -3,6 +3,7 @@ package org.briarproject.forum;
 import com.google.inject.Inject;
 
 import org.briarproject.api.FormatException;
+import org.briarproject.api.clients.PrivateGroupFactory;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
@@ -28,7 +29,6 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.MessageId;
-import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.api.sync.ValidationManager.ValidationHook;
 import org.briarproject.api.system.Clock;
 import org.briarproject.util.StringUtils;
@@ -345,7 +345,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
 		BdfReader r = bdfReaderFactory.createReader(in);
 		try {
 			r.readListStart();
-			r.skipInteger(); // Version
+			r.skipLong(); // Version
 			r.readListStart();
 			while (!r.hasListEnd()) {
 				r.readListStart();
@@ -387,7 +387,7 @@ class ForumSharingManagerImpl implements ForumSharingManager, AddContactHook,
 		BdfWriter w = bdfWriterFactory.createWriter(out);
 		try {
 			w.writeListStart();
-			w.writeInteger(version);
+			w.writeLong(version);
 			w.writeListStart();
 			for (Forum f : forums) {
 				w.writeListStart();
diff --git a/briar-core/src/org/briarproject/invitation/Connector.java b/briar-core/src/org/briarproject/invitation/Connector.java
index 8b26b16295b672eb22f5b4788a3c7dfaa057e671..6310c4f170ad3031faeecc00c80697f2b95932dc 100644
--- a/briar-core/src/org/briarproject/invitation/Connector.java
+++ b/briar-core/src/org/briarproject/invitation/Connector.java
@@ -196,13 +196,13 @@ abstract class Connector extends Thread {
 
 	protected void sendTimestamp(BdfWriter w, long timestamp)
 			throws IOException {
-		w.writeInteger(timestamp);
+		w.writeLong(timestamp);
 		w.flush();
 		if (LOG.isLoggable(INFO)) LOG.info(pluginName + " sent timestamp");
 	}
 
 	protected long receiveTimestamp(BdfReader r) throws IOException {
-		long timestamp = r.readInteger();
+		long timestamp = r.readLong();
 		if (timestamp < 0) throw new FormatException();
 		if (LOG.isLoggable(INFO)) LOG.info(pluginName + " received timestamp");
 		return timestamp;
diff --git a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
index 2d4bf9c95969efc7c4a8d68a73b51e03fb1b02ae..64a0a6c4346f50f3a75187b6da37c644dfdbf995 100644
--- a/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
+++ b/briar-core/src/org/briarproject/messaging/MessagingManagerImpl.java
@@ -3,6 +3,7 @@ package org.briarproject.messaging;
 import com.google.inject.Inject;
 
 import org.briarproject.api.FormatException;
+import org.briarproject.api.clients.PrivateGroupFactory;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
@@ -24,7 +25,6 @@ import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.MessageStatus;
-import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.util.StringUtils;
 
 import java.io.ByteArrayInputStream;
diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
index a254551595f6fa387a6e326a173ada60c3be97bc..38400d1913e5db1a6579189285d01518b102d0ae 100644
--- a/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
+++ b/briar-core/src/org/briarproject/properties/TransportPropertyManagerImpl.java
@@ -5,6 +5,7 @@ import com.google.inject.Inject;
 import org.briarproject.api.DeviceId;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.TransportId;
+import org.briarproject.api.clients.PrivateGroupFactory;
 import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager.AddContactHook;
@@ -30,7 +31,6 @@ import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.Message;
 import org.briarproject.api.sync.MessageFactory;
 import org.briarproject.api.sync.MessageId;
-import org.briarproject.api.sync.PrivateGroupFactory;
 import org.briarproject.api.system.Clock;
 import org.briarproject.util.StringUtils;
 
@@ -288,7 +288,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 			w.writeListStart();
 			w.writeRaw(dev.getBytes());
 			w.writeString(t.getString());
-			w.writeInteger(version);
+			w.writeLong(version);
 			w.writeDictionary(p);
 			w.writeListEnd();
 		} catch (IOException e) {
@@ -342,7 +342,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
 			r.readListStart();
 			r.skipRaw(); // Device ID
 			r.skipString(); // Transport ID
-			r.skipInteger(); // Version
+			r.skipLong(); // Version
 			r.readDictionaryStart();
 			while (!r.hasDictionaryEnd()) {
 				String key = r.readString(MAX_PROPERTY_LENGTH);
diff --git a/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java b/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java
index a3b7c17832ac5997d3ece03b33418e5ebee9d3c7..06973ae7d8986e7dacd4c50d65b14f66fe7110ff 100644
--- a/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java
+++ b/briar-core/src/org/briarproject/properties/TransportPropertyValidator.java
@@ -57,7 +57,7 @@ class TransportPropertyValidator implements MessageValidator {
 			if (deviceId.length != UniqueId.LENGTH) throw new FormatException();
 			String transportId = r.readString(MAX_TRANSPORT_ID_LENGTH);
 			if (transportId.length() == 0) throw new FormatException();
-			long version = r.readInteger();
+			long version = r.readLong();
 			if (version < 0) throw new FormatException();
 			r.readDictionaryStart();
 			for (int i = 0; !r.hasDictionaryEnd(); i++) {
diff --git a/briar-core/src/org/briarproject/sync/SyncModule.java b/briar-core/src/org/briarproject/sync/SyncModule.java
index 83c8f86ec503824edf796153b339aff133e0aed0..64ed5d50538bda1d054c77ba1d897ac74aff78b4 100644
--- a/briar-core/src/org/briarproject/sync/SyncModule.java
+++ b/briar-core/src/org/briarproject/sync/SyncModule.java
@@ -9,7 +9,6 @@ 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.SyncSessionFactory;
 import org.briarproject.api.sync.ValidationManager;
 
@@ -23,7 +22,6 @@ public class SyncModule extends AbstractModule {
 		bind(MessageFactory.class).to(MessageFactoryImpl.class);
 		bind(PacketReaderFactory.class).to(PacketReaderFactoryImpl.class);
 		bind(PacketWriterFactory.class).to(PacketWriterFactoryImpl.class);
-		bind(PrivateGroupFactory.class).to(PrivateGroupFactoryImpl.class);
 		bind(SyncSessionFactory.class).to(
 				SyncSessionFactoryImpl.class).in(Singleton.class);
 	}
diff --git a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..9ea0e901f9207e72466c7e5076adc4ded431c5f9
--- /dev/null
+++ b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
@@ -0,0 +1,14 @@
+package org.briarproject.clients;
+
+import org.briarproject.BriarTestCase;
+import org.junit.Test;
+
+import static org.junit.Assert.fail;
+
+public class ClientHelperImplTest extends BriarTestCase {
+
+	@Test
+	public void testUnitTestsExist() {
+		fail(); // FIXME: Write tests
+	}
+}
diff --git a/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java b/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java
index a3309ddac02562881778ae6c2b46af6d4075fa8d..8bf3830ab13ebdcbf3ce510a1560b64177c7847e 100644
--- a/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java
+++ b/briar-tests/src/org/briarproject/data/BdfReaderImplTest.java
@@ -3,11 +3,14 @@ package org.briarproject.data;
 import org.briarproject.BriarTestCase;
 import org.briarproject.TestUtils;
 import org.briarproject.api.FormatException;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfList;
 import org.briarproject.util.StringUtils;
 import org.junit.Test;
 
 import java.io.ByteArrayInputStream;
 
+import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -54,100 +57,102 @@ public class BdfReaderImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testReadInt8() throws Exception {
+	public void testReadLong8() throws Exception {
 		setContents("21" + "00" + "21" + "FF"
 				+ "21" + "7F" + "21" + "80");
-		assertEquals(0, r.readInteger());
-		assertEquals(-1, r.readInteger());
-		assertEquals(Byte.MAX_VALUE, r.readInteger());
-		assertEquals(Byte.MIN_VALUE, r.readInteger());
+		assertEquals(0, r.readLong());
+		assertEquals(-1, r.readLong());
+		assertEquals(Byte.MAX_VALUE, r.readLong());
+		assertEquals(Byte.MIN_VALUE, r.readLong());
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testSkipInt8() throws Exception {
+	public void testSkipLong8() throws Exception {
 		setContents("21" + "00");
-		r.skipInteger();
+		r.skipLong();
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testReadInt16() throws Exception {
+	public void testReadLong16() throws Exception {
 		setContents("22" + "0080" + "22" + "FF7F"
 				+ "22" + "7FFF" + "22" + "8000");
-		assertEquals(Byte.MAX_VALUE + 1, r.readInteger());
-		assertEquals(Byte.MIN_VALUE - 1, r.readInteger());
-		assertEquals(Short.MAX_VALUE, r.readInteger());
-		assertEquals(Short.MIN_VALUE, r.readInteger());
+		assertEquals(Byte.MAX_VALUE + 1, r.readLong());
+		assertEquals(Byte.MIN_VALUE - 1, r.readLong());
+		assertEquals(Short.MAX_VALUE, r.readLong());
+		assertEquals(Short.MIN_VALUE, r.readLong());
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testSkipInt16() throws Exception {
+	public void testSkipLong16() throws Exception {
 		setContents("22" + "0080");
-		r.skipInteger();
+		r.skipLong();
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testReadInt32() throws Exception {
+	public void testReadLong32() throws Exception {
 		setContents("24" + "00008000" + "24" + "FFFF7FFF"
 				+ "24" + "7FFFFFFF" + "24" + "80000000");
-		assertEquals(Short.MAX_VALUE + 1, r.readInteger());
-		assertEquals(Short.MIN_VALUE - 1, r.readInteger());
-		assertEquals(Integer.MAX_VALUE, r.readInteger());
-		assertEquals(Integer.MIN_VALUE, r.readInteger());
+		assertEquals(Short.MAX_VALUE + 1, r.readLong());
+		assertEquals(Short.MIN_VALUE - 1, r.readLong());
+		assertEquals(Integer.MAX_VALUE, r.readLong());
+		assertEquals(Integer.MIN_VALUE, r.readLong());
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testSkipInt32() throws Exception {
+	public void testSkipLong32() throws Exception {
 		setContents("24" + "00008000");
-		r.skipInteger();
+		r.skipLong();
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testReadInt64() throws Exception {
+	public void testReadLong64() throws Exception {
 		setContents("28" + "0000000080000000" + "28" + "FFFFFFFF7FFFFFFF"
 				+ "28" + "7FFFFFFFFFFFFFFF" + "28" + "8000000000000000");
-		assertEquals(Integer.MAX_VALUE + 1L, r.readInteger());
-		assertEquals(Integer.MIN_VALUE - 1L, r.readInteger());
-		assertEquals(Long.MAX_VALUE, r.readInteger());
-		assertEquals(Long.MIN_VALUE, r.readInteger());
+		assertEquals(Integer.MAX_VALUE + 1L, r.readLong());
+		assertEquals(Integer.MIN_VALUE - 1L, r.readLong());
+		assertEquals(Long.MAX_VALUE, r.readLong());
+		assertEquals(Long.MIN_VALUE, r.readLong());
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testSkipInt64() throws Exception {
+	public void testSkipLong() throws Exception {
 		setContents("28" + "0000000080000000");
-		r.skipInteger();
+		r.skipLong();
 		assertTrue(r.eof());
 	}
 
 	@Test
-	public void testReadFloat() throws Exception {
+	public void testReadDouble() throws Exception {
 		// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
 		// http://steve.hollasch.net/cgindex/coding/ieeefloat.html
 		setContents("38" + "0000000000000000" + "38" + "3FF0000000000000"
 				+ "38" + "4000000000000000" + "38" + "BFF0000000000000"
 				+ "38" + "8000000000000000" + "38" + "FFF0000000000000"
 				+ "38" + "7FF0000000000000" + "38" + "7FF8000000000000");
-		assertEquals(0, Double.compare(0.0, r.readFloat()));
-		assertEquals(0, Double.compare(1.0, r.readFloat()));
-		assertEquals(0, Double.compare(2.0, r.readFloat()));
-		assertEquals(0, Double.compare(-1.0, r.readFloat()));
-		assertEquals(0, Double.compare(-0.0, r.readFloat()));
-		assertEquals(0, Double.compare(Double.NEGATIVE_INFINITY, r.readFloat()));
-		assertEquals(0, Double.compare(Double.POSITIVE_INFINITY, r.readFloat()));
-		assertTrue(Double.isNaN(r.readFloat()));
+		assertEquals(0, Double.compare(0.0, r.readDouble()));
+		assertEquals(0, Double.compare(1.0, r.readDouble()));
+		assertEquals(0, Double.compare(2.0, r.readDouble()));
+		assertEquals(0, Double.compare(-1.0, r.readDouble()));
+		assertEquals(0, Double.compare(-0.0, r.readDouble()));
+		assertEquals(0, Double.compare(Double.NEGATIVE_INFINITY,
+				r.readDouble()));
+		assertEquals(0, Double.compare(Double.POSITIVE_INFINITY,
+				r.readDouble()));
+		assertTrue(Double.isNaN(r.readDouble()));
 		assertTrue(r.eof());
 	}
 
 	@Test
 	public void testSkipFloat() throws Exception {
 		setContents("38" + "0000000000000000");
-		r.skipFloat();
+		r.skipDouble();
 		assertTrue(r.eof());
 	}
 
@@ -377,17 +382,31 @@ public class BdfReaderImplTest extends BriarTestCase {
 
 	@Test
 	public void testReadList() throws Exception {
-		// A list containing 1, "foo", and 128
+		// A list containing 1, "foo", and null
 		setContents("60" + "21" + "01" +
 				"41" + "03" + "666F6F" +
-				"22" + "0080" + "80");
+				"00" + "80");
+		BdfList list = r.readList();
+		assertEquals(3, list.size());
+		assertEquals(1L, list.get(0));
+		assertEquals("foo", list.get(1));
+		assertEquals(NULL_VALUE, list.get(2));
+	}
+
+	@Test
+	public void testReadListManually() throws Exception {
+		// A list containing 1, "foo", and null
+		setContents("60" + "21" + "01" +
+				"41" + "03" + "666F6F" +
+				"00" + "80");
 		r.readListStart();
 		assertFalse(r.hasListEnd());
-		assertEquals(1, r.readInteger());
+		assertEquals(1, r.readLong());
 		assertFalse(r.hasListEnd());
 		assertEquals("foo", r.readString(1000));
 		assertFalse(r.hasListEnd());
-		assertEquals(128, r.readInteger());
+		assertTrue(r.hasNull());
+		r.readNull();
 		assertTrue(r.hasListEnd());
 		r.readListEnd();
 		assertTrue(r.eof());
@@ -405,6 +424,19 @@ public class BdfReaderImplTest extends BriarTestCase {
 
 	@Test
 	public void testReadDictionary() throws Exception {
+		// A dictionary containing "foo" -> 123 and "bar" -> null
+		setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
+				"41" + "03" + "626172" + "00" + "80");
+		BdfDictionary dictionary = r.readDictionary();
+		assertEquals(2, dictionary.size());
+		assertTrue(dictionary.containsKey("foo"));
+		assertEquals(123L, dictionary.get("foo"));
+		assertTrue(dictionary.containsKey("bar"));
+		assertEquals(NULL_VALUE, dictionary.get("bar"));
+	}
+
+	@Test
+	public void testReadDictionaryManually() throws Exception {
 		// A dictionary containing "foo" -> 123 and "bar" -> null
 		setContents("70" + "41" + "03" + "666F6F" + "21" + "7B" +
 				"41" + "03" + "626172" + "00" + "80");
@@ -412,7 +444,7 @@ public class BdfReaderImplTest extends BriarTestCase {
 		assertFalse(r.hasDictionaryEnd());
 		assertEquals("foo", r.readString(1000));
 		assertFalse(r.hasDictionaryEnd());
-		assertEquals(123, r.readInteger());
+		assertEquals(123, r.readLong());
 		assertFalse(r.hasDictionaryEnd());
 		assertEquals("bar", r.readString(1000));
 		assertFalse(r.hasDictionaryEnd());
diff --git a/briar-tests/src/org/briarproject/data/BdfWriterImplTest.java b/briar-tests/src/org/briarproject/data/BdfWriterImplTest.java
index 8da0f0e2c6e8205570f4e3993a28c049bafa545c..8ddd0c2097139e63d66c4765c8d52a02a3eb928a 100644
--- a/briar-tests/src/org/briarproject/data/BdfWriterImplTest.java
+++ b/briar-tests/src/org/briarproject/data/BdfWriterImplTest.java
@@ -13,6 +13,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 
+import static org.briarproject.api.data.BdfDictionary.NULL_VALUE;
 import static org.junit.Assert.assertArrayEquals;
 
 public class BdfWriterImplTest extends BriarTestCase {
@@ -41,17 +42,17 @@ public class BdfWriterImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testWriteInteger() throws IOException {
-		w.writeInteger(0);
-		w.writeInteger(-1);
-		w.writeInteger(Byte.MAX_VALUE);
-		w.writeInteger(Byte.MIN_VALUE);
-		w.writeInteger(Short.MAX_VALUE);
-		w.writeInteger(Short.MIN_VALUE);
-		w.writeInteger(Integer.MAX_VALUE);
-		w.writeInteger(Integer.MIN_VALUE);
-		w.writeInteger(Long.MAX_VALUE);
-		w.writeInteger(Long.MIN_VALUE);
+	public void testWriteLong() throws IOException {
+		w.writeLong(0);
+		w.writeLong(-1);
+		w.writeLong(Byte.MAX_VALUE);
+		w.writeLong(Byte.MIN_VALUE);
+		w.writeLong(Short.MAX_VALUE);
+		w.writeLong(Short.MIN_VALUE);
+		w.writeLong(Integer.MAX_VALUE);
+		w.writeLong(Integer.MIN_VALUE);
+		w.writeLong(Long.MAX_VALUE);
+		w.writeLong(Long.MIN_VALUE);
 		// INTEGER_8 tag, 0, INTEGER_8 tag, -1, etc
 		checkContents("21" + "00" + "21" + "FF" +
 				"21" + "7F" + "21" + "80" +
@@ -61,17 +62,17 @@ public class BdfWriterImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testWriteFloat() throws IOException {
+	public void testWriteDouble() throws IOException {
 		// http://babbage.cs.qc.edu/IEEE-754/Decimal.html
 		// 1 bit for sign, 11 for exponent, 52 for significand
-		w.writeFloat(0.0); // 0 0 0 -> 0x0000000000000000
-		w.writeFloat(1.0); // 0 1023 1 -> 0x3FF0000000000000
-		w.writeFloat(2.0); // 0 1024 1 -> 0x4000000000000000
-		w.writeFloat(-1.0); // 1 1023 1 -> 0xBFF0000000000000
-		w.writeFloat(-0.0); // 1 0 0 -> 0x8000000000000000
-		w.writeFloat(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000...
-		w.writeFloat(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000...
-		w.writeFloat(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000
+		w.writeDouble(0.0); // 0 0 0 -> 0x0000000000000000
+		w.writeDouble(1.0); // 0 1023 1 -> 0x3FF0000000000000
+		w.writeDouble(2.0); // 0 1024 1 -> 0x4000000000000000
+		w.writeDouble(-1.0); // 1 1023 1 -> 0xBFF0000000000000
+		w.writeDouble(-0.0); // 1 0 0 -> 0x8000000000000000
+		w.writeDouble(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000...
+		w.writeDouble(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000...
+		w.writeDouble(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000
 		checkContents("38" + "0000000000000000" + "38" + "3FF0000000000000"
 				+ "38" + "4000000000000000" + "38" + "BFF0000000000000"
 				+ "38" + "8000000000000000" + "38" + "FFF0000000000000"
@@ -122,7 +123,7 @@ public class BdfWriterImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testWriteBytes8() throws IOException {
+	public void testWriteRaw8() throws IOException {
 		byte[] longest = new byte[Byte.MAX_VALUE];
 		String longHex = StringUtils.toHexString(longest);
 		w.writeRaw(new byte[] {1, 2, 3});
@@ -132,7 +133,7 @@ public class BdfWriterImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testWriteBytes16() throws IOException {
+	public void testWriteRaw16() throws IOException {
 		byte[] shortest = new byte[Byte.MAX_VALUE + 1];
 		String shortHex = StringUtils.toHexString(shortest);
 		byte[] longest = new byte[Short.MAX_VALUE];
@@ -144,7 +145,7 @@ public class BdfWriterImplTest extends BriarTestCase {
 	}
 
 	@Test
-	public void testWriteBytes32() throws IOException {
+	public void testWriteRaw32() throws IOException {
 		byte[] shortest = new byte[Short.MAX_VALUE + 1];
 		String shortHex = StringUtils.toHexString(shortest);
 		w.writeRaw(shortest);
@@ -166,10 +167,11 @@ public class BdfWriterImplTest extends BriarTestCase {
 		List<Object> l = new ArrayList<Object>();
 		l.add(1);
 		l.add(null);
+		l.add(NULL_VALUE);
 		l.add(2);
 		w.writeList(l);
-		// LIST tag, 1 as integer, NULL tag, 2 as integer, END tag
-		checkContents("60" + "21" + "01" + "00" + "21" + "02" + "80");
+		// LIST tag, 1 as integer, NULL tag, NULL tag, 2 as integer, END tag
+		checkContents("60" + "21" + "01" + "00" + "00" + "21" + "02" + "80");
 	}
 
 	@Test
@@ -188,9 +190,9 @@ public class BdfWriterImplTest extends BriarTestCase {
 	@Test
 	public void testWriteDelimitedList() throws IOException {
 		w.writeListStart();
-		w.writeInteger(1);
+		w.writeLong(1);
 		w.writeString("foo");
-		w.writeInteger(128);
+		w.writeLong(128);
 		w.writeListEnd();
 		// LIST tag, 1 as integer, "foo" as string, 128 as integer, END tag
 		checkContents("60" + "21" + "01" +
@@ -202,7 +204,7 @@ public class BdfWriterImplTest extends BriarTestCase {
 	public void testWriteDelimitedDictionary() throws IOException {
 		w.writeDictionaryStart();
 		w.writeString("foo");
-		w.writeInteger(123);
+		w.writeLong(123);
 		w.writeString("bar");
 		w.writeNull();
 		w.writeDictionaryEnd();
diff --git a/briar-tests/src/org/briarproject/sync/ConstantsTest.java b/briar-tests/src/org/briarproject/sync/ConstantsTest.java
index f3af582b878ad5b272c6c6bd01be4f218441f827..bac9995415b5ed08f8bd12077c45340b20632a1d 100644
--- a/briar-tests/src/org/briarproject/sync/ConstantsTest.java
+++ b/briar-tests/src/org/briarproject/sync/ConstantsTest.java
@@ -23,6 +23,7 @@ import org.briarproject.api.messaging.PrivateMessage;
 import org.briarproject.api.messaging.PrivateMessageFactory;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
+import org.briarproject.clients.ClientsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
 import org.briarproject.data.DataModule;
@@ -56,10 +57,10 @@ public class ConstantsTest extends BriarTestCase {
 	public ConstantsTest() throws Exception {
 		Injector i = Guice.createInjector(new TestDatabaseModule(),
 				new TestLifecycleModule(), new TestSystemModule(),
-				new ContactModule(), new CryptoModule(), new DatabaseModule(),
-				new DataModule(), new EventModule(), new ForumModule(),
-				new IdentityModule(), new MessagingModule(), new SyncModule(),
-				new TransportModule());
+				new ClientsModule(), new ContactModule(), new CryptoModule(),
+				new DatabaseModule(), new DataModule(), new EventModule(),
+				new ForumModule(), new IdentityModule(), new MessagingModule(),
+				new SyncModule(), new TransportModule());
 		crypto = i.getInstance(CryptoComponent.class);
 		authorFactory = i.getInstance(AuthorFactory.class);
 		privateMessageFactory = i.getInstance(PrivateMessageFactory.class);
diff --git a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
index a548539d44a7f3cf595f94bd7c218b27851b2cc3..0c95d8b3b694811bc56f6acb211ae25b80174305 100644
--- a/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
+++ b/briar-tests/src/org/briarproject/sync/SimplexMessagingIntegrationTest.java
@@ -36,6 +36,7 @@ import org.briarproject.api.transport.KeyManager;
 import org.briarproject.api.transport.StreamContext;
 import org.briarproject.api.transport.StreamReaderFactory;
 import org.briarproject.api.transport.StreamWriterFactory;
+import org.briarproject.clients.ClientsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
 import org.briarproject.data.DataModule;
@@ -86,10 +87,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
 
 	private Injector createInjector(File dir) {
 		return Guice.createInjector(new TestDatabaseModule(dir),
-				new TestSystemModule(), new ContactModule(), new CryptoModule(),
-				new DatabaseModule(), new DataModule(), new EventModule(),
-				new IdentityModule(), new LifecycleModule(),
-				new MessagingModule(), new SyncModule(), new TransportModule());
+				new TestSystemModule(), new ClientsModule(),
+				new ContactModule(), new CryptoModule(), new DatabaseModule(),
+				new DataModule(), new EventModule(), new IdentityModule(),
+				new LifecycleModule(), new MessagingModule(), new SyncModule(),
+				new TransportModule());
 	}
 
 	@Test