diff --git a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
index 943d02de98598359dedbf1767411a6334b8ed504..8fa1501816cb8771763d765995f0ec283fb14421 100644
--- a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
+++ b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
@@ -10,17 +10,22 @@ import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.SecretKey;
 import org.briarproject.api.crypto.Signature;
+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.db.ContactExistsException;
+import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.Transaction;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.plugins.ConnectionManager;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
+import org.briarproject.api.properties.TransportProperties;
+import org.briarproject.api.properties.TransportPropertyManager;
 import org.briarproject.api.system.Clock;
 import org.briarproject.api.transport.StreamReaderFactory;
 import org.briarproject.api.transport.StreamWriterFactory;
@@ -29,13 +34,19 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
 import java.util.logging.Logger;
 
 import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.TransportId.MAX_TRANSPORT_ID_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
+import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
 
 public class ContactExchangeTaskImpl extends Thread
 		implements ContactExchangeTask {
@@ -43,35 +54,40 @@ public class ContactExchangeTaskImpl extends Thread
 	private static final Logger LOG =
 			Logger.getLogger(ContactExchangeTaskImpl.class.getName());
 
+	private final DatabaseComponent db;
 	private final AuthorFactory authorFactory;
 	private final BdfReaderFactory bdfReaderFactory;
 	private final BdfWriterFactory bdfWriterFactory;
 	private final Clock clock;
 	private final ConnectionManager connectionManager;
 	private final ContactManager contactManager;
+	private final TransportPropertyManager transportPropertyManager;
 	private final CryptoComponent crypto;
 	private final StreamReaderFactory streamReaderFactory;
 	private final StreamWriterFactory streamWriterFactory;
 
-	private ContactExchangeListener listener;
-	private LocalAuthor localAuthor;
-	private DuplexTransportConnection conn;
-	private TransportId transportId;
-	private SecretKey masterSecret;
-	private boolean alice;
+	private volatile ContactExchangeListener listener;
+	private volatile LocalAuthor localAuthor;
+	private volatile DuplexTransportConnection conn;
+	private volatile TransportId transportId;
+	private volatile SecretKey masterSecret;
+	private volatile boolean alice;
 
-	public ContactExchangeTaskImpl(AuthorFactory authorFactory,
-			BdfReaderFactory bdfReaderFactory,
+	public ContactExchangeTaskImpl(DatabaseComponent db,
+			AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
 			BdfWriterFactory bdfWriterFactory, Clock clock,
 			ConnectionManager connectionManager, ContactManager contactManager,
+			TransportPropertyManager transportPropertyManager,
 			CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
 			StreamWriterFactory streamWriterFactory) {
+		this.db = db;
 		this.authorFactory = authorFactory;
 		this.bdfReaderFactory = bdfReaderFactory;
 		this.bdfWriterFactory = bdfWriterFactory;
 		this.clock = clock;
 		this.connectionManager = connectionManager;
 		this.contactManager = contactManager;
+		this.transportPropertyManager = transportPropertyManager;
 		this.crypto = crypto;
 		this.streamReaderFactory = streamReaderFactory;
 		this.streamWriterFactory = streamWriterFactory;
@@ -93,24 +109,12 @@ public class ContactExchangeTaskImpl extends Thread
 
 	@Override
 	public void run() {
-		// Derive the header keys for the transport streams
-		SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
-		SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
-		BdfReader r;
-		BdfWriter w;
+		// Get the transport connection's input and output streams
+		InputStream in;
+		OutputStream out;
 		try {
-			// Create the readers
-			InputStream streamReader =
-					streamReaderFactory.createInvitationStreamReader(
-							conn.getReader().getInputStream(),
-							alice ? bobHeaderKey : aliceHeaderKey);
-			r = bdfReaderFactory.createReader(streamReader);
-			// Create the writers
-			OutputStream streamWriter =
-					streamWriterFactory.createInvitationStreamWriter(
-							conn.getWriter().getOutputStream(),
-							alice ? aliceHeaderKey : bobHeaderKey);
-			w = bdfWriterFactory.createWriter(streamWriter);
+			in = conn.getReader().getInputStream();
+			out = conn.getWriter().getOutputStream();
 		} catch (IOException e) {
 			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
 			listener.contactExchangeFailed();
@@ -118,6 +122,32 @@ public class ContactExchangeTaskImpl extends Thread
 			return;
 		}
 
+		// Get the local transport properties
+		Map<TransportId, TransportProperties> localProperties, remoteProperties;
+		try {
+			localProperties = transportPropertyManager.getLocalProperties();
+		} catch (DbException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			listener.contactExchangeFailed();
+			tryToClose(conn, true);
+			return;
+		}
+
+		// Derive the header keys for the transport streams
+		SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
+		SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
+
+		// Create the readers
+		InputStream streamReader =
+				streamReaderFactory.createInvitationStreamReader(in,
+						alice ? bobHeaderKey : aliceHeaderKey);
+		BdfReader r = bdfReaderFactory.createReader(streamReader);
+		// Create the writers
+		OutputStream streamWriter =
+				streamWriterFactory.createInvitationStreamWriter(out,
+						alice ? aliceHeaderKey : bobHeaderKey);
+		BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
+
 		// Derive the nonces to be signed
 		byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
 		byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
@@ -130,13 +160,19 @@ public class ContactExchangeTaskImpl extends Thread
 			if (alice) {
 				sendPseudonym(w, aliceNonce);
 				sendTimestamp(w, localTimestamp);
+				sendTransportProperties(w, localProperties);
+				w.flush();
 				remoteAuthor = receivePseudonym(r, bobNonce);
 				remoteTimestamp = receiveTimestamp(r);
+				remoteProperties = receiveTransportProperties(r);
 			} else {
 				remoteAuthor = receivePseudonym(r, aliceNonce);
 				remoteTimestamp = receiveTimestamp(r);
+				remoteProperties = receiveTransportProperties(r);
 				sendPseudonym(w, bobNonce);
 				sendTimestamp(w, localTimestamp);
+				sendTransportProperties(w, localProperties);
+				w.flush();
 			}
 			// Close the outgoing stream and expect EOF on the incoming stream
 			w.close();
@@ -159,7 +195,7 @@ public class ContactExchangeTaskImpl extends Thread
 		try {
 			// Add the contact
 			ContactId contactId = addContact(remoteAuthor, masterSecret,
-					timestamp, alice);
+					timestamp, alice, remoteProperties);
 			// Reuse the connection as a transport connection
 			connectionManager.manageOutgoingConnection(contactId, transportId,
 					conn);
@@ -187,19 +223,22 @@ public class ContactExchangeTaskImpl extends Thread
 		signature.update(nonce);
 		byte[] sig = signature.sign();
 		// Write the name, public key and signature
+		w.writeListStart();
 		w.writeString(localAuthor.getName());
 		w.writeRaw(localAuthor.getPublicKey());
 		w.writeRaw(sig);
-		w.flush();
+		w.writeListEnd();
 		LOG.info("Sent pseudonym");
 	}
 
 	private Author receivePseudonym(BdfReader r, byte[] nonce)
 			throws GeneralSecurityException, IOException {
 		// Read the name, public key and signature
+		r.readListStart();
 		String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
 		byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
 		byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
+		r.readListEnd();
 		LOG.info("Received pseudonym");
 		// Verify the signature
 		Signature signature = crypto.getSignature();
@@ -217,7 +256,6 @@ public class ContactExchangeTaskImpl extends Thread
 	private void sendTimestamp(BdfWriter w, long timestamp)
 			throws IOException {
 		w.writeLong(timestamp);
-		w.flush();
 		LOG.info("Sent timestamp");
 	}
 
@@ -228,11 +266,56 @@ public class ContactExchangeTaskImpl extends Thread
 		return timestamp;
 	}
 
+	private void sendTransportProperties(BdfWriter w,
+			Map<TransportId, TransportProperties> local) throws IOException {
+		w.writeListStart();
+		for (Entry<TransportId, TransportProperties> e : local.entrySet())
+			w.writeList(BdfList.of(e.getKey().getString(), e.getValue()));
+		w.writeListEnd();
+	}
+
+	private Map<TransportId, TransportProperties> receiveTransportProperties(
+			BdfReader r) throws IOException {
+		Map<TransportId, TransportProperties> remote =
+				new HashMap<TransportId, TransportProperties>();
+		r.readListStart();
+		while (!r.hasListEnd()) {
+			r.readListStart();
+			String id = r.readString(MAX_TRANSPORT_ID_LENGTH);
+			if (id.isEmpty()) throw new FormatException();
+			TransportProperties p = new TransportProperties();
+			r.readDictionaryStart();
+			while (!r.hasDictionaryEnd()) {
+				if (p.size() == MAX_PROPERTIES_PER_TRANSPORT)
+					throw new FormatException();
+				String key = r.readString(MAX_PROPERTY_LENGTH);
+				String value = r.readString(MAX_PROPERTY_LENGTH);
+				p.put(key, value);
+			}
+			r.readDictionaryEnd();
+			r.readListEnd();
+			remote.put(new TransportId(id), p);
+		}
+		r.readListEnd();
+		return remote;
+	}
+
 	private ContactId addContact(Author remoteAuthor, SecretKey master,
-			long timestamp, boolean alice) throws DbException {
-		// Add the contact to the database
-		return contactManager.addContact(remoteAuthor, localAuthor.getId(),
-				master, timestamp, alice, true);
+			long timestamp, boolean alice,
+			Map<TransportId, TransportProperties> remoteProperties)
+			throws DbException {
+		ContactId contactId;
+		Transaction txn = db.startTransaction(false);
+		try {
+			contactId = contactManager.addContact(txn, remoteAuthor,
+					localAuthor.getId(), master, timestamp, alice, true);
+			transportPropertyManager.addRemoteProperties(txn, contactId,
+					remoteProperties);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return contactId;
 	}
 
 	private void tryToClose(DuplexTransportConnection conn,
diff --git a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
index 23ec838802d8ad75c512e970720a185b566e254c..5c5c81d1cc85d8c54ddf30b604f1e40a8a0188f6 100644
--- a/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
+++ b/briar-core/src/org/briarproject/contact/ContactManagerImpl.java
@@ -126,18 +126,18 @@ class ContactManagerImpl implements ContactManager, RemoveIdentityHook {
 	}
 
 	@Override
-	public boolean contactExists(Transaction txn, AuthorId remoteAuthorID,
+	public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
 			AuthorId localAuthorId) throws DbException {
-		return db.containsContact(txn, remoteAuthorID, localAuthorId);
+		return db.containsContact(txn, remoteAuthorId, localAuthorId);
 	}
 
 	@Override
-	public boolean contactExists(AuthorId remoteAuthorID,
+	public boolean contactExists(AuthorId remoteAuthorId,
 			AuthorId localAuthorId) throws DbException {
 		boolean exists = false;
 		Transaction txn = db.startTransaction(true);
 		try {
-			exists = contactExists(txn, remoteAuthorID, localAuthorId);
+			exists = contactExists(txn, remoteAuthorId, localAuthorId);
 			txn.setComplete();
 		} finally {
 			db.endTransaction(txn);
diff --git a/briar-core/src/org/briarproject/contact/ContactModule.java b/briar-core/src/org/briarproject/contact/ContactModule.java
index fb5c290ce2d3ad0915f57a8e44a7c6d71d4b0aec..2cdf6079eddc4bba0cb54911ce1461d3c3522375 100644
--- a/briar-core/src/org/briarproject/contact/ContactModule.java
+++ b/briar-core/src/org/briarproject/contact/ContactModule.java
@@ -5,10 +5,11 @@ import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.data.BdfReaderFactory;
 import org.briarproject.api.data.BdfWriterFactory;
+import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.identity.AuthorFactory;
 import org.briarproject.api.identity.IdentityManager;
-import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.plugins.ConnectionManager;
+import org.briarproject.api.properties.TransportPropertyManager;
 import org.briarproject.api.system.Clock;
 import org.briarproject.api.transport.StreamReaderFactory;
 import org.briarproject.api.transport.StreamWriterFactory;
@@ -28,22 +29,23 @@ public class ContactModule {
 
 	@Provides
 	@Singleton
-	ContactManager getContactManager(LifecycleManager lifecycleManager,
-			IdentityManager identityManager,
+	ContactManager getContactManager(IdentityManager identityManager,
 			ContactManagerImpl contactManager) {
 		identityManager.registerRemoveIdentityHook(contactManager);
 		return contactManager;
 	}
 
 	@Provides
-	ContactExchangeTask provideContactExchangeTask(
+	ContactExchangeTask provideContactExchangeTask(DatabaseComponent db,
 			AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
 			BdfWriterFactory bdfWriterFactory, Clock clock,
 			ConnectionManager connectionManager, ContactManager contactManager,
+			TransportPropertyManager transportPropertyManager,
 			CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
 			StreamWriterFactory streamWriterFactory) {
-		return new ContactExchangeTaskImpl(authorFactory, bdfReaderFactory,
+		return new ContactExchangeTaskImpl(db, authorFactory, bdfReaderFactory,
 				bdfWriterFactory, clock, connectionManager, contactManager,
-				crypto, streamReaderFactory, streamWriterFactory);
+				transportPropertyManager, crypto, streamReaderFactory,
+				streamWriterFactory);
 	}
 }