diff --git a/api/net/sf/briar/api/protocol/AuthorId.java b/api/net/sf/briar/api/protocol/AuthorId.java
index a353f27661b21db44ace32e35249f0c8aa588243..c34d32c05452cb11470bb622fb1ae26b482d2123 100644
--- a/api/net/sf/briar/api/protocol/AuthorId.java
+++ b/api/net/sf/briar/api/protocol/AuthorId.java
@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.AUTHOR_ID);
+		w.writeUserDefinedTag(Types.AUTHOR_ID);
 		w.writeBytes(id);
 	}
 
diff --git a/api/net/sf/briar/api/protocol/BatchId.java b/api/net/sf/briar/api/protocol/BatchId.java
index 95949e0872ad2170e33663278ac382f3ce01bc77..f2b2f542ffed2bf3cae881b631f247b78c41ae9e 100644
--- a/api/net/sf/briar/api/protocol/BatchId.java
+++ b/api/net/sf/briar/api/protocol/BatchId.java
@@ -16,7 +16,7 @@ public class BatchId extends UniqueId {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.BATCH_ID);
+		w.writeUserDefinedTag(Types.BATCH_ID);
 		w.writeBytes(id);
 	}
 
diff --git a/api/net/sf/briar/api/protocol/GroupId.java b/api/net/sf/briar/api/protocol/GroupId.java
index 6621777de8ecb56124e3aa9075ff4e34d9434342..aa34e728acf85402d2ca40572eee38289188754c 100644
--- a/api/net/sf/briar/api/protocol/GroupId.java
+++ b/api/net/sf/briar/api/protocol/GroupId.java
@@ -16,7 +16,7 @@ public class GroupId extends UniqueId {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.GROUP_ID);
+		w.writeUserDefinedTag(Types.GROUP_ID);
 		w.writeBytes(id);
 	}
 
diff --git a/api/net/sf/briar/api/protocol/MessageId.java b/api/net/sf/briar/api/protocol/MessageId.java
index 10ba76d7359947116c908336eb837a4c08dcbb3d..e70a44f76240f84b3255e16b5501e76d5a244488 100644
--- a/api/net/sf/briar/api/protocol/MessageId.java
+++ b/api/net/sf/briar/api/protocol/MessageId.java
@@ -17,7 +17,7 @@ public class MessageId extends UniqueId {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.MESSAGE_ID);
+		w.writeUserDefinedTag(Types.MESSAGE_ID);
 		w.writeBytes(id);
 	}
 
diff --git a/api/net/sf/briar/api/protocol/Tags.java b/api/net/sf/briar/api/protocol/Types.java
similarity index 66%
rename from api/net/sf/briar/api/protocol/Tags.java
rename to api/net/sf/briar/api/protocol/Types.java
index 77ca61c57c0aef105a4b2c7d5e6d7c16ae115ddc..54dc03d9689b7ea70bb221f66c544b3e9c719b05 100644
--- a/api/net/sf/briar/api/protocol/Tags.java
+++ b/api/net/sf/briar/api/protocol/Types.java
@@ -1,11 +1,7 @@
 package net.sf.briar.api.protocol;
 
-/**
- * User-defined tags for encoding and decoding protocol objects. An object
- * should have a user-defined tag if it appears in a list or a map, or if
- * objects of different types may be encountered in a given protocol state.
- */
-public interface Tags {
+/** User-defined type identifiers for encoding and decoding protocol objects. */
+public interface Types {
 
 	static final int ACK = 0;
 	static final int AUTHOR = 1;
diff --git a/api/net/sf/briar/api/serial/Reader.java b/api/net/sf/briar/api/serial/Reader.java
index c00acfe934d1276a982d7187668287258759dffc..1b41585f0dc382fcdeb5325617caaff84ecc56ef 100644
--- a/api/net/sf/briar/api/serial/Reader.java
+++ b/api/net/sf/briar/api/serial/Reader.java
@@ -18,8 +18,8 @@ public interface Reader {
 	void addConsumer(Consumer c);
 	void removeConsumer(Consumer c);
 
-	void addObjectReader(int tag, ObjectReader<?> o);
-	void removeObjectReader(int tag);
+	void addObjectReader(int id, ObjectReader<?> o);
+	void removeObjectReader(int id);
 
 	boolean hasBoolean() throws IOException;
 	boolean readBoolean() throws IOException;
@@ -69,7 +69,7 @@ public interface Reader {
 	boolean hasNull() throws IOException;
 	void readNull() throws IOException;
 
-	boolean hasUserDefined(int tag) throws IOException;
-	<T> T readUserDefined(int tag, Class<T> t) throws IOException;
-	void readUserDefinedTag(int tag) throws IOException;
+	boolean hasUserDefined(int id) throws IOException;
+	<T> T readUserDefined(int id, Class<T> t) throws IOException;
+	void readUserDefinedId(int id) throws IOException;
 }
diff --git a/components/net/sf/briar/protocol/AckReader.java b/components/net/sf/briar/protocol/AckReader.java
index f6474609cab0493f7e956543c3a215e370dd51cb..5f78511ae7fdfd78b1cb97ad8d5e5ed9908900c2 100644
--- a/components/net/sf/briar/protocol/AckReader.java
+++ b/components/net/sf/briar/protocol/AckReader.java
@@ -7,7 +7,7 @@ import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -28,11 +28,11 @@ class AckReader implements ObjectReader<Ack> {
 			new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH);
 		// Read and digest the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.ACK);
-		r.addObjectReader(Tags.BATCH_ID, batchIdReader);
+		r.readUserDefinedId(Types.ACK);
+		r.addObjectReader(Types.BATCH_ID, batchIdReader);
 		Collection<BatchId> batches = r.readList(BatchId.class);
 		if(batches.size() > Ack.MAX_IDS_PER_ACK) throw new FormatException();
-		r.removeObjectReader(Tags.BATCH_ID);
+		r.removeObjectReader(Types.BATCH_ID);
 		r.removeConsumer(counting);
 		// Build and return the ack
 		return ackFactory.createAck(batches);
diff --git a/components/net/sf/briar/protocol/AuthorImpl.java b/components/net/sf/briar/protocol/AuthorImpl.java
index 2bbc960d2a37639756ccb7660a1a0c00b309a5d0..9596dffbba6c5bba539b986da6c8600bd8ee173b 100644
--- a/components/net/sf/briar/protocol/AuthorImpl.java
+++ b/components/net/sf/briar/protocol/AuthorImpl.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Writer;
 
 class AuthorImpl implements Author {
@@ -32,7 +32,7 @@ class AuthorImpl implements Author {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.AUTHOR);
+		w.writeUserDefinedTag(Types.AUTHOR);
 		w.writeString(name);
 		w.writeBytes(publicKey);
 	}
diff --git a/components/net/sf/briar/protocol/AuthorReader.java b/components/net/sf/briar/protocol/AuthorReader.java
index 4ce4cc8e405dd2bbd24516ce81f4fd5d83fc197b..06d509cafaed22598b0d58326eadece18ce20422 100644
--- a/components/net/sf/briar/protocol/AuthorReader.java
+++ b/components/net/sf/briar/protocol/AuthorReader.java
@@ -7,7 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
 import net.sf.briar.api.protocol.AuthorId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
@@ -27,7 +27,7 @@ class AuthorReader implements ObjectReader<Author> {
 		messageDigest.reset();
 		// Read and digest the data
 		r.addConsumer(digesting);
-		r.readUserDefinedTag(Tags.AUTHOR);
+		r.readUserDefinedId(Types.AUTHOR);
 		String name = r.readString(Author.MAX_NAME_LENGTH);
 		byte[] publicKey = r.readBytes(Author.MAX_PUBLIC_KEY_LENGTH);
 		r.removeConsumer(digesting);
diff --git a/components/net/sf/briar/protocol/BatchIdReader.java b/components/net/sf/briar/protocol/BatchIdReader.java
index 21807eb475c6a12ce34af6619aa60f7093a39e0d..f288f04ab2e00999eecde23616a6c6eb1d848336 100644
--- a/components/net/sf/briar/protocol/BatchIdReader.java
+++ b/components/net/sf/briar/protocol/BatchIdReader.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -12,7 +12,7 @@ import net.sf.briar.api.serial.Reader;
 class BatchIdReader implements ObjectReader<BatchId> {
 
 	public BatchId readObject(Reader r) throws IOException {
-		r.readUserDefinedTag(Tags.BATCH_ID);
+		r.readUserDefinedId(Types.BATCH_ID);
 		byte[] b = r.readBytes(UniqueId.LENGTH);
 		if(b.length != UniqueId.LENGTH) throw new FormatException();
 		return new BatchId(b);
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
index 5f3bce3283c72ca85c0cd36245af3f2cc697e536..e77c711f582867235c662da4f01cba0927d56b26 100644
--- a/components/net/sf/briar/protocol/BatchReader.java
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -9,7 +9,7 @@ import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -36,10 +36,10 @@ class BatchReader implements ObjectReader<Batch> {
 		// Read and digest the data
 		r.addConsumer(counting);
 		r.addConsumer(digesting);
-		r.readUserDefinedTag(Tags.BATCH);
-		r.addObjectReader(Tags.MESSAGE, messageReader);
+		r.readUserDefinedId(Types.BATCH);
+		r.addObjectReader(Types.MESSAGE, messageReader);
 		List<Message> messages = r.readList(Message.class);
-		r.removeObjectReader(Tags.MESSAGE);
+		r.removeObjectReader(Types.MESSAGE);
 		r.removeConsumer(digesting);
 		r.removeConsumer(counting);
 		// Build and return the batch
diff --git a/components/net/sf/briar/protocol/GroupIdReader.java b/components/net/sf/briar/protocol/GroupIdReader.java
index bf9fd81b1eb1f43e0aff54d0b6c6e6ec9c608298..b121fd8cbc950d0e1c69eefa33768de09eac4d2f 100644
--- a/components/net/sf/briar/protocol/GroupIdReader.java
+++ b/components/net/sf/briar/protocol/GroupIdReader.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.GroupId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -12,7 +12,7 @@ import net.sf.briar.api.serial.Reader;
 class GroupIdReader implements ObjectReader<GroupId> {
 
 	public GroupId readObject(Reader r) throws IOException {
-		r.readUserDefinedTag(Tags.GROUP_ID);
+		r.readUserDefinedId(Types.GROUP_ID);
 		byte[] b = r.readBytes(UniqueId.LENGTH);
 		if(b.length != UniqueId.LENGTH) throw new FormatException();
 		return new GroupId(b);
diff --git a/components/net/sf/briar/protocol/GroupImpl.java b/components/net/sf/briar/protocol/GroupImpl.java
index 34fe24e6d12960868d442993457c62280b58b916..ad265d51d5637cdae0bdbc87c2d29eb4bb4b7436 100644
--- a/components/net/sf/briar/protocol/GroupImpl.java
+++ b/components/net/sf/briar/protocol/GroupImpl.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Writer;
 
 class GroupImpl implements Group {
@@ -32,7 +32,7 @@ class GroupImpl implements Group {
 	}
 
 	public void writeTo(Writer w) throws IOException {
-		w.writeUserDefinedTag(Tags.GROUP);
+		w.writeUserDefinedTag(Types.GROUP);
 		w.writeString(name);
 		if(publicKey == null) w.writeNull();
 		else w.writeBytes(publicKey);
diff --git a/components/net/sf/briar/protocol/GroupReader.java b/components/net/sf/briar/protocol/GroupReader.java
index d855850dd437ee345ba6012e9e3711647b5d9eaf..9b371c19da12da6c009d9abd4db4051c5ea1bb3e 100644
--- a/components/net/sf/briar/protocol/GroupReader.java
+++ b/components/net/sf/briar/protocol/GroupReader.java
@@ -7,7 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
@@ -27,7 +27,7 @@ class GroupReader implements ObjectReader<Group> {
 		messageDigest.reset();
 		// Read and digest the data
 		r.addConsumer(digesting);
-		r.readUserDefinedTag(Tags.GROUP);
+		r.readUserDefinedId(Types.GROUP);
 		String name = r.readString(Group.MAX_NAME_LENGTH);
 		byte[] publicKey = null;
 		if(r.hasNull()) r.readNull();
diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java
index 2907689ea29f833cf3b4a360f02fca8da725be5d..af8f7b902bd1da5e51705ec2bc8ee1762b988d36 100644
--- a/components/net/sf/briar/protocol/MessageEncoderImpl.java
+++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java
@@ -14,7 +14,7 @@ import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
 import net.sf.briar.api.protocol.MessageId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
@@ -65,7 +65,7 @@ class MessageEncoderImpl implements MessageEncoder {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
 		// Write the message
-		w.writeUserDefinedTag(Tags.MESSAGE);
+		w.writeUserDefinedTag(Types.MESSAGE);
 		parent.writeTo(w);
 		group.writeTo(w);
 		if(author == null) w.writeNull();
diff --git a/components/net/sf/briar/protocol/MessageIdReader.java b/components/net/sf/briar/protocol/MessageIdReader.java
index 32e4e905ace77b21041917ea35d1db9a37985731..a47ef6a2db0a6cd6fc5ddba1a1befb737c6657c7 100644
--- a/components/net/sf/briar/protocol/MessageIdReader.java
+++ b/components/net/sf/briar/protocol/MessageIdReader.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.MessageId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -12,7 +12,7 @@ import net.sf.briar.api.serial.Reader;
 class MessageIdReader implements ObjectReader<MessageId> {
 
 	public MessageId readObject(Reader r) throws IOException {
-		r.readUserDefinedTag(Tags.MESSAGE_ID);
+		r.readUserDefinedId(Types.MESSAGE_ID);
 		byte[] b = r.readBytes(UniqueId.LENGTH);
 		if(b.length != UniqueId.LENGTH) throw new FormatException();
 		return new MessageId(b);
diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java
index d7fdcab4a89f81eef4246e2cb6957f6ef46c888f..669fa38051ec5f5e702696e6c62c31fc250f3a13 100644
--- a/components/net/sf/briar/protocol/MessageReader.java
+++ b/components/net/sf/briar/protocol/MessageReader.java
@@ -15,7 +15,7 @@ import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
@@ -47,21 +47,21 @@ class MessageReader implements ObjectReader<Message> {
 		r.addConsumer(copying);
 		r.addConsumer(counting);
 		// Read the initial tag
-		r.readUserDefinedTag(Tags.MESSAGE);
+		r.readUserDefinedId(Types.MESSAGE);
 		// Read the parent's message ID
-		r.addObjectReader(Tags.MESSAGE_ID, messageIdReader);
-		MessageId parent = r.readUserDefined(Tags.MESSAGE_ID, MessageId.class);
-		r.removeObjectReader(Tags.MESSAGE_ID);
+		r.addObjectReader(Types.MESSAGE_ID, messageIdReader);
+		MessageId parent = r.readUserDefined(Types.MESSAGE_ID, MessageId.class);
+		r.removeObjectReader(Types.MESSAGE_ID);
 		// Read the group
-		r.addObjectReader(Tags.GROUP, groupReader);
-		Group group = r.readUserDefined(Tags.GROUP, Group.class);
-		r.removeObjectReader(Tags.GROUP);
+		r.addObjectReader(Types.GROUP, groupReader);
+		Group group = r.readUserDefined(Types.GROUP, Group.class);
+		r.removeObjectReader(Types.GROUP);
 		// Read the author, if there is one
-		r.addObjectReader(Tags.AUTHOR, authorReader);
+		r.addObjectReader(Types.AUTHOR, authorReader);
 		Author author = null;
 		if(r.hasNull()) r.readNull();
-		else author = r.readUserDefined(Tags.AUTHOR, Author.class);
-		r.removeObjectReader(Tags.AUTHOR);
+		else author = r.readUserDefined(Types.AUTHOR, Author.class);
+		r.removeObjectReader(Types.AUTHOR);
 		// Read the timestamp
 		long timestamp = r.readInt64();
 		if(timestamp < 0L) throw new FormatException();
diff --git a/components/net/sf/briar/protocol/OfferReader.java b/components/net/sf/briar/protocol/OfferReader.java
index 970b96453c9a2ab153baa9cabe7ffc3e0569c0c4..04a15536cf44ab3340d6b7900a535b676e68181c 100644
--- a/components/net/sf/briar/protocol/OfferReader.java
+++ b/components/net/sf/briar/protocol/OfferReader.java
@@ -7,7 +7,7 @@ import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.Offer;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -29,12 +29,12 @@ class OfferReader implements ObjectReader<Offer> {
 			new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.OFFER);
-		r.addObjectReader(Tags.MESSAGE_ID, messageIdReader);
+		r.readUserDefinedId(Types.OFFER);
+		r.addObjectReader(Types.MESSAGE_ID, messageIdReader);
 		Collection<MessageId> messages = r.readList(MessageId.class);
 		if(messages.size() > Offer.MAX_IDS_PER_OFFER)
 			throw new FormatException();
-		r.removeObjectReader(Tags.MESSAGE_ID);
+		r.removeObjectReader(Types.MESSAGE_ID);
 		r.removeConsumer(counting);
 		// Build and return the offer
 		return offerFactory.createOffer(messages);
diff --git a/components/net/sf/briar/protocol/ProtocolReaderImpl.java b/components/net/sf/briar/protocol/ProtocolReaderImpl.java
index d79d8a4e40cbf2980507a78fdecd29ebc9cf41f5..e2c39da0c491a7df600e83994022f2c025217b09 100644
--- a/components/net/sf/briar/protocol/ProtocolReaderImpl.java
+++ b/components/net/sf/briar/protocol/ProtocolReaderImpl.java
@@ -9,7 +9,7 @@ import net.sf.briar.api.protocol.Offer;
 import net.sf.briar.api.protocol.ProtocolReader;
 import net.sf.briar.api.protocol.Request;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.TransportUpdate;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -26,60 +26,60 @@ class ProtocolReaderImpl implements ProtocolReader {
 			ObjectReader<SubscriptionUpdate> subscriptionReader,
 			ObjectReader<TransportUpdate> transportReader) {
 		reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.ACK, ackReader);
-		reader.addObjectReader(Tags.BATCH, batchReader);
-		reader.addObjectReader(Tags.OFFER, offerReader);
-		reader.addObjectReader(Tags.REQUEST, requestReader);
-		reader.addObjectReader(Tags.SUBSCRIPTION_UPDATE, subscriptionReader);
-		reader.addObjectReader(Tags.TRANSPORT_UPDATE, transportReader);
+		reader.addObjectReader(Types.ACK, ackReader);
+		reader.addObjectReader(Types.BATCH, batchReader);
+		reader.addObjectReader(Types.OFFER, offerReader);
+		reader.addObjectReader(Types.REQUEST, requestReader);
+		reader.addObjectReader(Types.SUBSCRIPTION_UPDATE, subscriptionReader);
+		reader.addObjectReader(Types.TRANSPORT_UPDATE, transportReader);
 	}
 
 	public boolean hasAck() throws IOException {
-		return reader.hasUserDefined(Tags.ACK);
+		return reader.hasUserDefined(Types.ACK);
 	}
 
 	public Ack readAck() throws IOException {
-		return reader.readUserDefined(Tags.ACK, Ack.class);
+		return reader.readUserDefined(Types.ACK, Ack.class);
 	}
 
 	public boolean hasBatch() throws IOException {
-		return reader.hasUserDefined(Tags.BATCH);
+		return reader.hasUserDefined(Types.BATCH);
 	}
 
 	public Batch readBatch() throws IOException {
-		return reader.readUserDefined(Tags.BATCH, Batch.class);
+		return reader.readUserDefined(Types.BATCH, Batch.class);
 	}
 
 	public boolean hasOffer() throws IOException {
-		return reader.hasUserDefined(Tags.OFFER);
+		return reader.hasUserDefined(Types.OFFER);
 	}
 
 	public Offer readOffer() throws IOException {
-		return reader.readUserDefined(Tags.OFFER, Offer.class);
+		return reader.readUserDefined(Types.OFFER, Offer.class);
 	}
 
 	public boolean hasRequest() throws IOException {
-		return reader.hasUserDefined(Tags.REQUEST);
+		return reader.hasUserDefined(Types.REQUEST);
 	}
 
 	public Request readRequest() throws IOException {
-		return reader.readUserDefined(Tags.REQUEST, Request.class);
+		return reader.readUserDefined(Types.REQUEST, Request.class);
 	}
 
 	public boolean hasSubscriptionUpdate() throws IOException {
-		return reader.hasUserDefined(Tags.SUBSCRIPTION_UPDATE);
+		return reader.hasUserDefined(Types.SUBSCRIPTION_UPDATE);
 	}
 
 	public SubscriptionUpdate readSubscriptionUpdate() throws IOException {
-		return reader.readUserDefined(Tags.SUBSCRIPTION_UPDATE,
+		return reader.readUserDefined(Types.SUBSCRIPTION_UPDATE,
 				SubscriptionUpdate.class);
 	}
 
 	public boolean hasTransportUpdate() throws IOException {
-		return reader.hasUserDefined(Tags.TRANSPORT_UPDATE);
+		return reader.hasUserDefined(Types.TRANSPORT_UPDATE);
 	}
 
 	public TransportUpdate readTransportUpdate() throws IOException {
-		return reader.readUserDefined(Tags.TRANSPORT_UPDATE, TransportUpdate.class);
+		return reader.readUserDefined(Types.TRANSPORT_UPDATE, TransportUpdate.class);
 	}
 }
diff --git a/components/net/sf/briar/protocol/RequestReader.java b/components/net/sf/briar/protocol/RequestReader.java
index 88ebec391b77f1a4c8f523632e26e96c1872e5e1..b2edd24cb9c6cad49d5a6ec538e7c47aa274b39a 100644
--- a/components/net/sf/briar/protocol/RequestReader.java
+++ b/components/net/sf/briar/protocol/RequestReader.java
@@ -5,7 +5,7 @@ import java.util.BitSet;
 
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Request;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -24,7 +24,7 @@ class RequestReader implements ObjectReader<Request> {
 			new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.REQUEST);
+		r.readUserDefinedId(Types.REQUEST);
 		byte[] bitmap = r.readBytes(ProtocolConstants.MAX_PACKET_LENGTH);
 		r.removeConsumer(counting);
 		// Convert the bitmap into a BitSet
diff --git a/components/net/sf/briar/protocol/SubscriptionReader.java b/components/net/sf/briar/protocol/SubscriptionReader.java
index 7c13d6ffa6a0abfba5618b33b638d844a72b45b7..40aa96e6a82e265d4157895035e2dc0254ffe86b 100644
--- a/components/net/sf/briar/protocol/SubscriptionReader.java
+++ b/components/net/sf/briar/protocol/SubscriptionReader.java
@@ -6,7 +6,7 @@ import java.util.Map;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
@@ -28,10 +28,10 @@ class SubscriptionReader implements ObjectReader<SubscriptionUpdate> {
 			new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
-		r.addObjectReader(Tags.GROUP, groupReader);
+		r.readUserDefinedId(Types.SUBSCRIPTION_UPDATE);
+		r.addObjectReader(Types.GROUP, groupReader);
 		Map<Group, Long> subs = r.readMap(Group.class, Long.class);
-		r.removeObjectReader(Tags.GROUP);
+		r.removeObjectReader(Types.GROUP);
 		long timestamp = r.readInt64();
 		r.removeConsumer(counting);
 		// Build and return the subscription update
diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java
index 345acff2b036abfb0fdd2d7d74efd62263587c23..9cb20e79be0bb42361b27404154816ef3bfb85be 100644
--- a/components/net/sf/briar/protocol/TransportReader.java
+++ b/components/net/sf/briar/protocol/TransportReader.java
@@ -7,7 +7,7 @@ import java.util.TreeMap;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.TransportUpdate;
 import net.sf.briar.api.serial.Consumer;
 import net.sf.briar.api.serial.ObjectReader;
@@ -29,12 +29,12 @@ class TransportReader implements ObjectReader<TransportUpdate> {
 			new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH);
 		// Read the data
 		r.addConsumer(counting);
-		r.readUserDefinedTag(Tags.TRANSPORT_UPDATE);
-		r.addObjectReader(Tags.TRANSPORT_PROPERTIES, propertiesReader);
+		r.readUserDefinedId(Types.TRANSPORT_UPDATE);
+		r.addObjectReader(Types.TRANSPORT_PROPERTIES, propertiesReader);
 		r.setMaxStringLength(ProtocolConstants.MAX_PACKET_LENGTH);
 		List<TransportProperties> l = r.readList(TransportProperties.class);
 		r.resetMaxStringLength();
-		r.removeObjectReader(Tags.TRANSPORT_PROPERTIES);
+		r.removeObjectReader(Types.TRANSPORT_PROPERTIES);
 		if(l.size() > TransportUpdate.MAX_PLUGINS_PER_UPDATE)
 			throw new FormatException();
 		Map<String, Map<String, String>> transports =
@@ -64,7 +64,7 @@ class TransportReader implements ObjectReader<TransportUpdate> {
 	implements ObjectReader<TransportProperties> {
 
 		public TransportProperties readObject(Reader r) throws IOException {
-			r.readUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
+			r.readUserDefinedId(Types.TRANSPORT_PROPERTIES);
 			String name = r.readString(TransportUpdate.MAX_NAME_LENGTH);
 			r.setMaxStringLength(TransportUpdate.MAX_KEY_OR_VALUE_LENGTH);
 			Map<String, String> properties =
diff --git a/components/net/sf/briar/protocol/writers/AckWriterImpl.java b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
index 0fea45d9dac7eee8d82f0f61172a55c456a14093..886455753d280e209b9c578035c2f7ef3eb831c6 100644
--- a/components/net/sf/briar/protocol/writers/AckWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/AckWriterImpl.java
@@ -5,7 +5,7 @@ import java.io.OutputStream;
 
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.BatchId;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -25,7 +25,7 @@ class AckWriterImpl implements AckWriter {
 
 	public boolean writeBatchId(BatchId b) throws IOException {
 		if(!started) {
-			w.writeUserDefinedTag(Tags.ACK);
+			w.writeUserDefinedTag(Types.ACK);
 			w.writeListStart();
 			started = true;
 		}
@@ -37,7 +37,7 @@ class AckWriterImpl implements AckWriter {
 
 	public void finish() throws IOException {
 		if(!started) {
-			w.writeUserDefinedTag(Tags.ACK);
+			w.writeUserDefinedTag(Types.ACK);
 			w.writeListStart();
 			started = true;
 		}
diff --git a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
index 5a9a6a2f74d5e46d3c898e0ffb28954523a80e86..36e84d81986f217f9ffc30549e030b06a417e24a 100644
--- a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
@@ -7,7 +7,7 @@ import java.security.MessageDigest;
 
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -36,7 +36,7 @@ class BatchWriterImpl implements BatchWriter {
 	public boolean writeMessage(byte[] message) throws IOException {
 		if(!started) {
 			messageDigest.reset();
-			w.writeUserDefinedTag(Tags.BATCH);
+			w.writeUserDefinedTag(Types.BATCH);
 			w.writeListStart();
 			started = true;
 		}
@@ -52,7 +52,7 @@ class BatchWriterImpl implements BatchWriter {
 	public BatchId finish() throws IOException {
 		if(!started) {
 			messageDigest.reset();
-			w.writeUserDefinedTag(Tags.BATCH);
+			w.writeUserDefinedTag(Types.BATCH);
 			w.writeListStart();
 			started = true;
 		}
diff --git a/components/net/sf/briar/protocol/writers/OfferWriterImpl.java b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java
index f606172ea009c723e6c61c5f7885e2a63d6811dc..c9f0670da4f1359bc24c1a42495c30b84be2e5c6 100644
--- a/components/net/sf/briar/protocol/writers/OfferWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java
@@ -5,7 +5,7 @@ import java.io.OutputStream;
 
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.Offer;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.OfferWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -25,7 +25,7 @@ class OfferWriterImpl implements OfferWriter {
 
 	public boolean writeMessageId(MessageId m) throws IOException {
 		if(!started) {
-			w.writeUserDefinedTag(Tags.OFFER);
+			w.writeUserDefinedTag(Types.OFFER);
 			w.writeListStart();
 			started = true;
 		}
@@ -37,7 +37,7 @@ class OfferWriterImpl implements OfferWriter {
 
 	public void finish() throws IOException {
 		if(!started) {
-			w.writeUserDefinedTag(Tags.OFFER);
+			w.writeUserDefinedTag(Types.OFFER);
 			w.writeListStart();
 			started = true;
 		}
diff --git a/components/net/sf/briar/protocol/writers/RequestWriterImpl.java b/components/net/sf/briar/protocol/writers/RequestWriterImpl.java
index 6e6ce137ad48018cd1f1a45313132c8b5021ea16..a3ceb5e50c70f50adc644fad23f4690ff987e7fe 100644
--- a/components/net/sf/briar/protocol/writers/RequestWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/RequestWriterImpl.java
@@ -4,7 +4,7 @@ import java.io.IOException;
 import java.io.OutputStream;
 import java.util.BitSet;
 
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.RequestWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -21,7 +21,7 @@ class RequestWriterImpl implements RequestWriter {
 
 	public void writeRequest(BitSet b, int length)
 	throws IOException {
-		w.writeUserDefinedTag(Tags.REQUEST);
+		w.writeUserDefinedTag(Types.REQUEST);
 		// If the number of bits isn't a multiple of 8, round up to a byte
 		int bytes = length % 8 == 0 ? length / 8 : length / 8 + 1;
 		byte[] bitmap = new byte[bytes];
diff --git a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
index ad778ba303eda69d7e078498df240f16013032bb..97b2e8aa0992c8bbfd604c69fb50a8413cda7652 100644
--- a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java
@@ -5,7 +5,7 @@ import java.io.OutputStream;
 import java.util.Map;
 
 import net.sf.briar.api.protocol.Group;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.SubscriptionWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -22,7 +22,7 @@ class SubscriptionWriterImpl implements SubscriptionWriter {
 
 	public void writeSubscriptions(Map<Group, Long> subs, long timestamp)
 	throws IOException {
-		w.writeUserDefinedTag(Tags.SUBSCRIPTION_UPDATE);
+		w.writeUserDefinedTag(Types.SUBSCRIPTION_UPDATE);
 		w.writeMap(subs);
 		w.writeInt64(timestamp);
 		out.flush();
diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
index 227a45eec9b453ba1ac6e25b7536baf4bb434db8..1d346fdabb059177eda39c0e4a9c5432f2b4e7a0 100644
--- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java
@@ -5,7 +5,7 @@ import java.io.OutputStream;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.TransportWriter;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
@@ -22,10 +22,10 @@ class TransportWriterImpl implements TransportWriter {
 
 	public void writeTransports(Map<String, Map<String, String>> transports,
 			long timestamp) throws IOException {
-		w.writeUserDefinedTag(Tags.TRANSPORT_UPDATE);
+		w.writeUserDefinedTag(Types.TRANSPORT_UPDATE);
 		w.writeListStart();
 		for(Entry<String, Map<String, String>> e : transports.entrySet()) {
-			w.writeUserDefinedTag(Tags.TRANSPORT_PROPERTIES);
+			w.writeUserDefinedTag(Types.TRANSPORT_PROPERTIES);
 			w.writeString(e.getKey());
 			w.writeMap(e.getValue());
 		}
diff --git a/components/net/sf/briar/serial/ReaderImpl.java b/components/net/sf/briar/serial/ReaderImpl.java
index 32f56df70706458f69d81851b5ac8edc8d30c901..0e9cc495d894e99128595c7a7203ab86294c57d0 100644
--- a/components/net/sf/briar/serial/ReaderImpl.java
+++ b/components/net/sf/briar/serial/ReaderImpl.java
@@ -110,21 +110,21 @@ class ReaderImpl implements Reader {
 		else throw new IllegalArgumentException();
 	}
 
-	public void addObjectReader(int tag, ObjectReader<?> o) {
-		if(tag < 0 || tag > 255) throw new IllegalArgumentException();
-		if(objectReaders.length < tag + 1) {
-			ObjectReader<?>[] newObjectReaders = new ObjectReader<?>[tag + 1];
+	public void addObjectReader(int id, ObjectReader<?> o) {
+		if(id < 0 || id > 255) throw new IllegalArgumentException();
+		if(objectReaders.length < id + 1) {
+			ObjectReader<?>[] newObjectReaders = new ObjectReader<?>[id + 1];
 			System.arraycopy(objectReaders, 0, newObjectReaders, 0,
 					objectReaders.length);
 			objectReaders = newObjectReaders;
 		}
-		objectReaders[tag] = o;	
+		objectReaders[id] = o;	
 	}
 
-	public void removeObjectReader(int tag) {
-		if(tag < 0 || tag > objectReaders.length)
+	public void removeObjectReader(int id) {
+		if(id < 0 || id > objectReaders.length)
 			throw new IllegalArgumentException();
-		objectReaders[tag] = null;
+		objectReaders[id] = null;
 	}
 
 	public boolean hasBoolean() throws IOException {
@@ -551,21 +551,21 @@ class ReaderImpl implements Reader {
 		consumeLookahead();
 	}
 
-	public boolean hasUserDefined(int tag) throws IOException {
-		if(tag < 0 || tag > 255) throw new IllegalArgumentException();
+	public boolean hasUserDefined(int id) throws IOException {
+		if(id < 0 || id > 255) throw new IllegalArgumentException();
 		if(!hasLookahead) readLookahead(true);
 		if(eof) return false;
 		if(next == Tag.USER)
-			return tag == (0xFF & nextNext);
+			return id == (0xFF & nextNext);
 		else if((next & Tag.SHORT_USER_MASK) == Tag.SHORT_USER)
-			return tag == (0xFF & next ^ Tag.SHORT_USER);
+			return id == (0xFF & next ^ Tag.SHORT_USER);
 		else return false;
 	}
 
-	public <T> T readUserDefined(int tag, Class<T> t) throws IOException {
-		if(!hasUserDefined(tag)) throw new FormatException();
-		if(tag >= objectReaders.length) throw new FormatException();
-		ObjectReader<?> o = objectReaders[tag];
+	public <T> T readUserDefined(int id, Class<T> t) throws IOException {
+		if(!hasUserDefined(id)) throw new FormatException();
+		if(id >= objectReaders.length) throw new FormatException();
+		ObjectReader<?> o = objectReaders[id];
 		if(o == null) throw new FormatException();
 		try {
 			return t.cast(o.readObject(this));
@@ -574,8 +574,8 @@ class ReaderImpl implements Reader {
 		}
 	}
 
-	public void readUserDefinedTag(int tag) throws IOException {
-		if(!hasUserDefined(tag)) throw new FormatException();
+	public void readUserDefinedId(int id) throws IOException {
+		if(!hasUserDefined(id)) throw new FormatException();
 		consumeLookahead();
 	}
 }
diff --git a/test/net/sf/briar/protocol/AckReaderTest.java b/test/net/sf/briar/protocol/AckReaderTest.java
index 42dffb2d0afce319d9299b99bad30433b7ef6728..32ffedd83a2a9a1afda262e17e6b5335d41c9c87 100644
--- a/test/net/sf/briar/protocol/AckReaderTest.java
+++ b/test/net/sf/briar/protocol/AckReaderTest.java
@@ -11,7 +11,7 @@ import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.Ack;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
@@ -48,10 +48,10 @@ public class AckReaderTest extends TestCase {
 		byte[] b = createAck(true);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.ACK, ackReader);
+		reader.addObjectReader(Types.ACK, ackReader);
 
 		try {
-			reader.readUserDefined(Tags.ACK, Ack.class);
+			reader.readUserDefined(Types.ACK, Ack.class);
 			fail();
 		} catch(FormatException expected) {}
 		context.assertIsSatisfied();
@@ -71,9 +71,9 @@ public class AckReaderTest extends TestCase {
 		byte[] b = createAck(false);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.ACK, ackReader);
+		reader.addObjectReader(Types.ACK, ackReader);
 
-		assertEquals(ack, reader.readUserDefined(Tags.ACK, Ack.class));
+		assertEquals(ack, reader.readUserDefined(Types.ACK, Ack.class));
 		context.assertIsSatisfied();
 	}
 
@@ -91,27 +91,27 @@ public class AckReaderTest extends TestCase {
 		byte[] b = createEmptyAck();
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.ACK, ackReader);
+		reader.addObjectReader(Types.ACK, ackReader);
 
-		assertEquals(ack, reader.readUserDefined(Tags.ACK, Ack.class));
+		assertEquals(ack, reader.readUserDefined(Types.ACK, Ack.class));
 		context.assertIsSatisfied();
 	}
 
 	private byte[] createAck(boolean tooBig) throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.ACK);
+		w.writeUserDefinedTag(Types.ACK);
 		w.writeListStart();
 		byte[] b = new byte[UniqueId.LENGTH];
 		Random random = new Random();
 		while(out.size() + BatchId.LENGTH + 3
 				< ProtocolConstants.MAX_PACKET_LENGTH) {
-			w.writeUserDefinedTag(Tags.BATCH_ID);
+			w.writeUserDefinedTag(Types.BATCH_ID);
 			random.nextBytes(b);
 			w.writeBytes(b);
 		}
 		if(tooBig) {
-			w.writeUserDefinedTag(Tags.BATCH_ID);
+			w.writeUserDefinedTag(Types.BATCH_ID);
 			random.nextBytes(b);
 			w.writeBytes(b);
 		}
@@ -123,7 +123,7 @@ public class AckReaderTest extends TestCase {
 	private byte[] createEmptyAck() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.ACK);
+		w.writeUserDefinedTag(Types.ACK);
 		w.writeListStart();
 		w.writeListEnd();
 		return out.toByteArray();
diff --git a/test/net/sf/briar/protocol/BatchReaderTest.java b/test/net/sf/briar/protocol/BatchReaderTest.java
index 8306b683eb9138dacfd6ac976e6766400d610099..f969446421ecd77ac90ca26c5ec0837405df83f7 100644
--- a/test/net/sf/briar/protocol/BatchReaderTest.java
+++ b/test/net/sf/briar/protocol/BatchReaderTest.java
@@ -13,7 +13,7 @@ import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.ProtocolConstants;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
@@ -58,10 +58,10 @@ public class BatchReaderTest extends TestCase {
 		byte[] b = createBatch(ProtocolConstants.MAX_PACKET_LENGTH + 1);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.BATCH, batchReader);
+		reader.addObjectReader(Types.BATCH, batchReader);
 
 		try {
-			reader.readUserDefined(Tags.BATCH, Batch.class);
+			reader.readUserDefined(Types.BATCH, Batch.class);
 			fail();
 		} catch(FormatException expected) {}
 		context.assertIsSatisfied();
@@ -83,9 +83,9 @@ public class BatchReaderTest extends TestCase {
 		byte[] b = createBatch(ProtocolConstants.MAX_PACKET_LENGTH);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.BATCH, batchReader);
+		reader.addObjectReader(Types.BATCH, batchReader);
 
-		assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
+		assertEquals(batch, reader.readUserDefined(Types.BATCH, Batch.class));
 		context.assertIsSatisfied();
 	}
 
@@ -112,9 +112,9 @@ public class BatchReaderTest extends TestCase {
 
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.BATCH, batchReader);
+		reader.addObjectReader(Types.BATCH, batchReader);
 
-		assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
+		assertEquals(batch, reader.readUserDefined(Types.BATCH, Batch.class));
 		context.assertIsSatisfied();
 	}
 
@@ -134,19 +134,19 @@ public class BatchReaderTest extends TestCase {
 		byte[] b = createEmptyBatch();
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.BATCH, batchReader);
+		reader.addObjectReader(Types.BATCH, batchReader);
 
-		assertEquals(batch, reader.readUserDefined(Tags.BATCH, Batch.class));
+		assertEquals(batch, reader.readUserDefined(Types.BATCH, Batch.class));
 		context.assertIsSatisfied();
 	}
 
 	private byte[] createBatch(int size) throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream(size);
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.BATCH);
+		w.writeUserDefinedTag(Types.BATCH);
 		w.writeListStart();
 		// We're using a fake message reader, so it's OK to use a fake message
-		w.writeUserDefinedTag(Tags.MESSAGE);
+		w.writeUserDefinedTag(Types.MESSAGE);
 		w.writeBytes(new byte[size - 10]);
 		w.writeListEnd();
 		byte[] b = out.toByteArray();
@@ -157,7 +157,7 @@ public class BatchReaderTest extends TestCase {
 	private byte[] createEmptyBatch() throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.BATCH);
+		w.writeUserDefinedTag(Types.BATCH);
 		w.writeListStart();
 		w.writeListEnd();
 		return out.toByteArray();
@@ -166,7 +166,7 @@ public class BatchReaderTest extends TestCase {
 	private class TestMessageReader implements ObjectReader<Message> {
 
 		public Message readObject(Reader r) throws IOException {
-			r.readUserDefinedTag(Tags.MESSAGE);
+			r.readUserDefinedId(Types.MESSAGE);
 			r.readBytes();
 			return message;
 		}
diff --git a/test/net/sf/briar/protocol/RequestReaderTest.java b/test/net/sf/briar/protocol/RequestReaderTest.java
index d93ec834a668a8246462879be445b0a72777d60c..1f4a97fba2af07b742f719c855a891c569f3f65b 100644
--- a/test/net/sf/briar/protocol/RequestReaderTest.java
+++ b/test/net/sf/briar/protocol/RequestReaderTest.java
@@ -8,7 +8,7 @@ import junit.framework.TestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Request;
-import net.sf.briar.api.protocol.Tags;
+import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Reader;
 import net.sf.briar.api.serial.ReaderFactory;
 import net.sf.briar.api.serial.Writer;
@@ -44,10 +44,10 @@ public class RequestReaderTest extends TestCase {
 		byte[] b = createRequest(true);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.REQUEST, requestReader);
+		reader.addObjectReader(Types.REQUEST, requestReader);
 
 		try {
-			reader.readUserDefined(Tags.REQUEST, Request.class);
+			reader.readUserDefined(Types.REQUEST, Request.class);
 			fail();
 		} catch(FormatException expected) {}
 		context.assertIsSatisfied();
@@ -67,9 +67,9 @@ public class RequestReaderTest extends TestCase {
 		byte[] b = createRequest(false);
 		ByteArrayInputStream in = new ByteArrayInputStream(b);
 		Reader reader = readerFactory.createReader(in);
-		reader.addObjectReader(Tags.REQUEST, requestReader);
+		reader.addObjectReader(Types.REQUEST, requestReader);
 
-		assertEquals(request, reader.readUserDefined(Tags.REQUEST,
+		assertEquals(request, reader.readUserDefined(Types.REQUEST,
 				Request.class));
 		context.assertIsSatisfied();
 	}
@@ -98,8 +98,8 @@ public class RequestReaderTest extends TestCase {
 			Reader reader = readerFactory.createReader(in);
 			RequestReader requestReader =
 				new RequestReader(new RequestFactoryImpl());
-			reader.addObjectReader(Tags.REQUEST, requestReader);
-			Request r = reader.readUserDefined(Tags.REQUEST, Request.class);
+			reader.addObjectReader(Types.REQUEST, requestReader);
+			Request r = reader.readUserDefined(Types.REQUEST, Request.class);
 			BitSet decoded = r.getBitmap();
 			// Check that the decoded BitSet matches the original - we can't
 			// use equals() because of padding, but the first i bits should
@@ -115,7 +115,7 @@ public class RequestReaderTest extends TestCase {
 	private byte[] createRequest(boolean tooBig) throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.REQUEST);
+		w.writeUserDefinedTag(Types.REQUEST);
 		// Allow one byte for the REQUEST tag, one byte for the BYTES tag,
 		// and five bytes for the length as an int32
 		int size = ProtocolConstants.MAX_PACKET_LENGTH - 7;
@@ -128,7 +128,7 @@ public class RequestReaderTest extends TestCase {
 	private byte[] createRequest(byte[] bitmap) throws Exception {
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.REQUEST);
+		w.writeUserDefinedTag(Types.REQUEST);
 		w.writeBytes(bitmap);
 		return out.toByteArray();
 	}
diff --git a/test/net/sf/briar/serial/ReaderImplTest.java b/test/net/sf/briar/serial/ReaderImplTest.java
index aa5c0bfb74baa5ad9ba5894d364f0517d1cb1584..982f0f397a036739b750f4197a139138c277f54f 100644
--- a/test/net/sf/briar/serial/ReaderImplTest.java
+++ b/test/net/sf/briar/serial/ReaderImplTest.java
@@ -375,13 +375,13 @@ public class ReaderImplTest extends TestCase {
 		// Add object readers for two user-defined types
 		r.addObjectReader(0, new ObjectReader<Foo>() {
 			public Foo readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(0);
+				r.readUserDefinedId(0);
 				return new Foo(r.readString());
 			}
 		});
 		r.addObjectReader(255, new ObjectReader<Bar>() {
 			public Bar readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(255);
+				r.readUserDefinedId(255);
 				return new Bar(r.readString());
 			}
 		});
@@ -398,13 +398,13 @@ public class ReaderImplTest extends TestCase {
 		// Add object readers for two user-defined types
 		r.addObjectReader(0, new ObjectReader<Foo>() {
 			public Foo readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(0);
+				r.readUserDefinedId(0);
 				return new Foo(r.readString());
 			}
 		});
 		r.addObjectReader(255, new ObjectReader<Bar>() {
 			public Bar readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(255);
+				r.readUserDefinedId(255);
 				return new Bar(r.readString());
 			}
 		});
@@ -451,7 +451,7 @@ public class ReaderImplTest extends TestCase {
 		// Add an object reader for tag 0, class Foo
 		r.addObjectReader(0, new ObjectReader<Foo>() {
 			public Foo readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(0);
+				r.readUserDefinedId(0);
 				return new Foo(r.readString());
 			}
 		});
@@ -469,7 +469,7 @@ public class ReaderImplTest extends TestCase {
 		// Add an object reader for a user-defined type
 		r.addObjectReader(0, new ObjectReader<Foo>() {
 			public Foo readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(0);
+				r.readUserDefinedId(0);
 				return new Foo(r.readString());
 			}
 		});
@@ -485,13 +485,13 @@ public class ReaderImplTest extends TestCase {
 		// Add object readers for two user-defined types
 		r.addObjectReader(0, new ObjectReader<Foo>() {
 			public Foo readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(0);
+				r.readUserDefinedId(0);
 				return new Foo(r.readString());
 			}
 		});
 		r.addObjectReader(1, new ObjectReader<Bar>() {
 			public Bar readObject(Reader r) throws IOException {
-				r.readUserDefinedTag(1);
+				r.readUserDefinedId(1);
 				return new Bar(r.readString());
 			}
 		});