diff --git a/api/net/sf/briar/api/protocol/Tags.java b/api/net/sf/briar/api/protocol/Tags.java
index c3e2b8193e3b5d8e7cabd19ef3d7e293049862bc..2bf887b6f2514273b4654ae50e66e09294c17347 100644
--- a/api/net/sf/briar/api/protocol/Tags.java
+++ b/api/net/sf/briar/api/protocol/Tags.java
@@ -1,18 +1,17 @@
 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 {
 
-	static final int AUTHOR_ID = 1;
-	static final int BATCH = 2;
-	static final int BATCH_ID = 3;
-	static final int GROUP_ID = 4;
-	static final int HEADER = 5;
-	static final int MESSAGE = 6;
-	static final int MESSAGE_BODY = 7;
-	static final int MESSAGE_ID = 8;
-	static final int NICKNAME = 9;
-	static final int PUBLIC_KEY = 10;
-	static final int SIGNATURE = 12;
-	static final int TIMESTAMP = 13;
-	static final int TRANSPORTS = 14;
+	static final int AUTHOR_ID = 0;
+	static final int BATCH = 1;
+	static final int BATCH_ID = 2;
+	static final int GROUP_ID = 3;
+	static final int HEADER = 4;
+	static final int MESSAGE = 5;
+	static final int MESSAGE_ID = 6;
 }
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
index 336312888b16fbd527d72c71c6fa56b85e64d5f3..bfb67d352f5f4efdef14c8fdf4e1f16adb1ba5f1 100644
--- a/components/net/sf/briar/protocol/BatchReader.java
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -50,7 +50,6 @@ public class BatchReader implements ObjectReader<Batch> {
 		reader.removeObjectReader(Tags.MESSAGE);
 		reader.removeConsumer(signing);
 		// Read and verify the signature
-		reader.readUserDefinedTag(Tags.SIGNATURE);
 		byte[] sig = reader.readRaw();
 		reader.removeConsumer(digesting);
 		reader.removeConsumer(counting);
diff --git a/components/net/sf/briar/protocol/BundleWriterImpl.java b/components/net/sf/briar/protocol/BundleWriterImpl.java
index dc5aab7c366077e4b657c2a7e086f4109f59e5cd..e6ff92b7fe2e2136e250186d2b0314d53e8f0a82 100644
--- a/components/net/sf/briar/protocol/BundleWriterImpl.java
+++ b/components/net/sf/briar/protocol/BundleWriterImpl.java
@@ -60,15 +60,12 @@ class BundleWriterImpl implements BundleWriter {
 		// Subs
 		writer.writeList(subs);
 		// Transports
-		writer.writeUserDefinedTag(Tags.TRANSPORTS);
 		writer.writeMap(transports);
 		// Timestamp
-		writer.writeUserDefinedTag(Tags.TIMESTAMP);
 		writer.writeInt64(System.currentTimeMillis());
 		out.setSigning(false);
 		// Create and write the signature
 		byte[] sig = signature.sign();
-		writer.writeUserDefinedTag(Tags.SIGNATURE);
 		writer.writeRaw(sig);
 		// Expect a (possibly empty) list of batches
 		state = State.FIRST_BATCH;
@@ -99,7 +96,6 @@ class BundleWriterImpl implements BundleWriter {
 		out.setSigning(false);
 		// Create and write the signature
 		byte[] sig = signature.sign();
-		writer.writeUserDefinedTag(Tags.SIGNATURE);
 		writer.writeRaw(sig);
 		out.setDigesting(false);
 		// Calculate and return the ID
diff --git a/components/net/sf/briar/protocol/HeaderReader.java b/components/net/sf/briar/protocol/HeaderReader.java
index 24f5f02d8d0d340cc18142185c13027ffabd0004..aeaf93e2b94af72b41bab005ea9adbde735b5cb5 100644
--- a/components/net/sf/briar/protocol/HeaderReader.java
+++ b/components/net/sf/briar/protocol/HeaderReader.java
@@ -47,16 +47,13 @@ class HeaderReader implements ObjectReader<Header> {
 		Collection<GroupId> subs = reader.readList(GroupId.class);
 		reader.removeObjectReader(Tags.GROUP_ID);
 		// Transports
-		reader.readUserDefinedTag(Tags.TRANSPORTS);
 		Map<String, String> transports =
 			reader.readMap(String.class, String.class);
 		// Timestamp
-		reader.readUserDefinedTag(Tags.TIMESTAMP);
 		long timestamp = reader.readInt64();
 		if(timestamp < 0L) throw new FormatException();
 		reader.removeConsumer(signing);
 		// Read and verify the signature
-		reader.readUserDefinedTag(Tags.SIGNATURE);
 		byte[] sig = reader.readRaw();
 		reader.removeConsumer(counting);
 		if(!signature.verify(sig)) throw new SignatureException();
diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java
index c3b1748764f864fb5b5f0ba2fd45661694f9b859..339ece3b7631e5697046c7a880e723f09f98f596 100644
--- a/components/net/sf/briar/protocol/MessageEncoderImpl.java
+++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java
@@ -12,7 +12,6 @@ import net.sf.briar.api.protocol.GroupId;
 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.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
@@ -38,13 +37,9 @@ class MessageEncoderImpl implements MessageEncoder {
 		// Write the message
 		parent.writeTo(w);
 		group.writeTo(w);
-		w.writeUserDefinedTag(Tags.TIMESTAMP);
 		w.writeInt64(timestamp);
-		w.writeUserDefinedTag(Tags.NICKNAME);
 		w.writeString(nick);
-		w.writeUserDefinedTag(Tags.PUBLIC_KEY);
 		w.writeRaw(keyPair.getPublic().getEncoded());
-		w.writeUserDefinedTag(Tags.MESSAGE_BODY);
 		w.writeRaw(body);
 		// Sign the message
 		byte[] signable = out.toByteArray();
@@ -53,7 +48,6 @@ class MessageEncoderImpl implements MessageEncoder {
 		byte[] sig = signature.sign();
 		signable = null;
 		// Write the signature
-		w.writeUserDefinedTag(Tags.SIGNATURE);
 		w.writeRaw(sig);
 		byte[] raw = out.toByteArray();
 		w.close();
@@ -64,9 +58,7 @@ class MessageEncoderImpl implements MessageEncoder {
 		// The author ID is the hash of the author's nick and public key
 		out.reset();
 		w = writerFactory.createWriter(out);
-		w.writeUserDefinedTag(Tags.NICKNAME);
 		w.writeString(nick);
-		w.writeUserDefinedTag(Tags.PUBLIC_KEY);
 		w.writeRaw(keyPair.getPublic().getEncoded());
 		w.close();
 		messageDigest.reset();
diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java
index 74022dfed5e297d26f72ccf3c2a44b48579a3d2e..ba4bad5394f94d07181dce7f37910806b6398c98 100644
--- a/components/net/sf/briar/protocol/MessageReader.java
+++ b/components/net/sf/briar/protocol/MessageReader.java
@@ -48,26 +48,21 @@ class MessageReader implements ObjectReader<Message> {
 		if(b.length != UniqueId.LENGTH) throw new FormatException();
 		GroupId group = new GroupId(b);
 		// Read the timestamp
-		reader.readUserDefinedTag(Tags.TIMESTAMP);
 		long timestamp = reader.readInt64();
 		if(timestamp < 0L) throw new FormatException();
 		// Hash the author's nick and public key to get the author ID
 		DigestingConsumer digesting = new DigestingConsumer(messageDigest);
 		messageDigest.reset();
 		reader.addConsumer(digesting);
-		reader.readUserDefinedTag(Tags.NICKNAME);
 		reader.readString();
-		reader.readUserDefinedTag(Tags.PUBLIC_KEY);
 		byte[] encodedKey = reader.readRaw();
 		reader.removeConsumer(digesting);
 		AuthorId author = new AuthorId(messageDigest.digest());
 		// Skip the message body
-		reader.readUserDefinedTag(Tags.MESSAGE_BODY);
 		reader.readRaw();
 		// Record the length of the signed data
 		int messageLength = (int) counting.getCount();
 		// Read the signature
-		reader.readUserDefinedTag(Tags.SIGNATURE);
 		byte[] sig = reader.readRaw();
 		reader.removeConsumer(counting);
 		reader.removeConsumer(copying);