diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index ece42f725c2eb7b8be65a840088aeb8040edcc2a..ed3bc3cf4ace5e5a44150d3e8c3faed7db08f275 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.identity.AuthorId;
 import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.mailbox.MailboxInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
 import org.briarproject.bramble.api.settings.Settings;
@@ -114,6 +115,13 @@ public interface DatabaseComponent {
 	KeySetId addTransportKeys(Transaction txn, ContactId c,
 			TransportKeys k) throws DbException;
 
+	/**
+	 * Set the @{Link ContactType#CONTACT_MAILBOX} for a contact.
+	 * key set ID.
+	 */
+	void setMailboxForContact(Transaction txn, ContactId c, @Nullable ContactId mailboxId,
+			@Nullable ContactId aliasId) throws DbException;
+
 	/**
 	 * Returns true if the database contains the given contact for the given
 	 * local pseudonym.
@@ -235,6 +243,14 @@ public interface DatabaseComponent {
 	Collection<ContactId> getContacts(Transaction txn, AuthorId a)
 			throws DbException;
 
+	/**
+	 * Returns @{MailboxInfo}s for all contact mailboxes.
+	 * <p/>
+	 * Read-only.
+	 */
+	Collection<MailboxInfo> getContactMailboxes(Transaction txn)
+			throws DbException;
+
 	/**
 	 * Returns the group with the given ID.
 	 * <p/>
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bd84867fba93f2cbb31714993d2b339411a9595
--- /dev/null
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java
@@ -0,0 +1,41 @@
+package org.briarproject.bramble.api.mailbox;
+
+import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import javax.annotation.Nullable;
+
+@NotNullByDefault
+public class MailboxInfo {
+
+	private final ContactId contactId;
+	// Null if the contact has no mailbox, our own will be used if possible.
+	@Nullable
+	private final ContactId mailboxId;
+	// The alias of the contact for the mailbox. The mailbox must not know
+	// the read id.
+	@Nullable
+	private final ContactId aliasId;
+
+	public MailboxInfo(ContactId contactId,
+			ContactId mailboxId,
+			ContactId aliasId) {
+		this.contactId = contactId;
+		this.mailboxId = mailboxId;
+		this.aliasId = aliasId;
+	}
+
+	public ContactId getContactId() {
+		return contactId;
+	}
+
+	@Nullable
+	public ContactId getMailboxId() {
+		return mailboxId;
+	}
+
+	@Nullable
+	public ContactId getAliasId() {
+		return aliasId;
+	}
+}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index 3c0f476a6d1521bfd9dfc48fc2a1e0a2feafb34f..dd75128f88b03e4052a76a1a53164d3096f9aaa6 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.db.MigrationListener;
 import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.identity.AuthorId;
 import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.mailbox.MailboxInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
 import org.briarproject.bramble.api.settings.Settings;
@@ -135,6 +136,13 @@ interface Database<T> {
 	KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
 			throws DbException;
 
+	/**
+	 * Set the @{Link ContactType#CONTACT_MAILBOX} for a contact.
+	 * key set ID.
+	 */
+	void setMailboxForContact(T txn, ContactId c, @Nullable ContactId mailboxId,
+			@Nullable ContactId aliasId) throws DbException;
+
 	/**
 	 * Returns true if the database contains the given contact for the given
 	 * local pseudonym.
@@ -246,6 +254,13 @@ interface Database<T> {
 	 */
 	Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
 
+	/**
+	 * Returns @{MailboxInfo}s for all contact mailboxes.
+	 * <p/>
+	 * Read-only.
+	 */
+	Collection<MailboxInfo> getContactMailboxes(T txn) throws DbException;
+
 	/**
 	 * Returns the amount of free storage space available to the database, in
 	 * bytes. This is based on the minimum of the space available on the device
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index 76e68f696b21b459b3439dbdf1d11765c56db023..75832b9b020628c35a88ffcc930d50d0d12c3fb6 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -27,6 +27,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent;
 import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent;
 import org.briarproject.bramble.api.lifecycle.ShutdownManager;
+import org.briarproject.bramble.api.mailbox.MailboxInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
 import org.briarproject.bramble.api.settings.Settings;
@@ -249,6 +250,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		return db.addTransportKeys(txn, c, k);
 	}
 
+	@Override
+	public void setMailboxForContact(Transaction transaction, ContactId c,
+			@Nullable ContactId mailboxId, @Nullable ContactId aliasId)
+			throws DbException {
+		if (transaction.isReadOnly()) throw new IllegalArgumentException();
+		T txn = unbox(transaction);
+		if (!db.containsContact(txn, c))
+			throw new NoSuchContactException();
+		db.setMailboxForContact(txn, c, mailboxId, aliasId);
+	}
+
 	@Override
 	public boolean containsContact(Transaction transaction, AuthorId remote,
 			AuthorId local) throws DbException {
@@ -421,6 +433,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		return db.getContacts(txn, a);
 	}
 
+	@Override
+	public Collection<MailboxInfo> getContactMailboxes(Transaction transaction)
+			throws DbException {
+		T txn = unbox(transaction);
+		return db.getContactMailboxes(txn);
+	}
+
 	@Override
 	public Group getGroup(Transaction transaction, GroupId g)
 			throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index eadad3e42c66c15606a9630dc405cf636ca49f61..eb88aaf4f75cc7a27d3edf1085d5602df57f25d8 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.db.MigrationListener;
 import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.identity.AuthorId;
 import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.mailbox.MailboxInfo;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.plugin.TransportId;
 import org.briarproject.bramble.api.settings.Settings;
@@ -278,6 +279,19 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " REFERENCES contacts (contactId)"
 					+ " ON DELETE CASCADE)";
 
+	private static final String CREATE_MAILBOXES =
+			"CREATE TABLE mailboxes"
+					+ " (contactId INT NOT NULL,"
+					+ " mailboxId INT,"
+					+ " aliasId INT,"
+					+ " PRIMARY KEY (contactId),"
+					+ " FOREIGN KEY (contactId)"
+					+ " REFERENCES contacts (contactId)"
+					+ " ON DELETE CASCADE,"
+					+ " FOREIGN KEY (mailboxId)"
+					+ " REFERENCES contacts (contactId)"
+					+ " ON DELETE CASCADE)";
+
 	private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
 			"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
 					+ " ON contacts (authorId)";
@@ -439,6 +453,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
 			s.executeUpdate(insertTypeNames(CREATE_OUTGOING_KEYS));
 			s.executeUpdate(insertTypeNames(CREATE_INCOMING_KEYS));
+			s.executeUpdate(insertTypeNames(CREATE_MAILBOXES));
 			s.close();
 		} catch (SQLException e) {
 			tryToClose(s);
@@ -970,6 +985,29 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	@Override
+	public void setMailboxForContact(Connection txn, ContactId contactId,
+			ContactId mailboxId, ContactId aliasId)
+			throws DbException {
+		PreparedStatement ps = null;
+		try {
+			String sql = "INSERT INTO mailboxes (contactId, mailboxId, aliasId)"
+					+ " VALUES (?, ?, ?)";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, contactId.getInt());
+			if (mailboxId == null) ps.setNull(2, INTEGER);
+			else ps.setInt(1, mailboxId.getInt());
+			if (aliasId == null) ps.setNull(3, INTEGER);
+			else ps.setInt(3, aliasId.getInt());
+			int affected = ps.executeUpdate();
+			if (affected != 1) throw new DbStateException();
+			ps.close();
+		} catch (SQLException e) {
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	@Override
 	public boolean containsContact(Connection txn, AuthorId remote,
 			AuthorId local) throws DbException {
@@ -1369,6 +1407,34 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	@Override
+	public Collection<MailboxInfo> getContactMailboxes(Connection txn)
+			throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT contactId, mailboxId, aliasId FROM mailboxes";
+			ps = txn.prepareStatement(sql);
+			rs = ps.executeQuery();
+			List<MailboxInfo> mailboxInfos = new ArrayList<>();
+			while (rs.next()) {
+				int contactId = rs.getInt(1);
+				int mailboxid = rs.getInt(2);
+				int aliasId = rs.getInt(3);
+				mailboxInfos.add(new MailboxInfo(new ContactId(contactId),
+						mailboxid == 0 ? null : new ContactId(mailboxid),
+						aliasId == 0 ? null : new ContactId(aliasId)));
+			}
+			rs.close();
+			ps.close();
+			return mailboxInfos;
+		} catch (SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	@Override
 	public Group getGroup(Connection txn, GroupId g) throws DbException {
 		PreparedStatement ps = null;
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java
index ca86b1d0675f0ea0477bacca5d75aa8c2e7cd889..ba9e56071afa81e3b495d7aae8a763226f2c02c7 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java
@@ -41,6 +41,17 @@ class Migration39_40 implements Migration<Connection> {
 			// Set type NOT NULL
 			s.execute("ALTER TABLE contacts"
 					+ " ALTER COLUMN type INT NOT NULL");
+			s.execute("CREATE TABLE mailboxes"
+					+ " (contactId INT NOT NULL,"
+					+ " mailboxId INT,"
+					+ " aliasId INT,"
+					+ " PRIMARY KEY (contactId),"
+					+ " FOREIGN KEY (contactId)"
+					+ " REFERENCES contacts (contactId)"
+					+ " ON DELETE CASCADE,"
+					+ " FOREIGN KEY (mailboxId)"
+					+ " REFERENCES contacts (contactId)"
+					+ " ON DELETE CASCADE)");
 		} catch (SQLException e) {
 			tryToClose(s);
 			throw new DbException(e);
diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
index 811f3a351008aaca919437fc2519d3d47087ba57..56709159dca46cdb4cf6f914e22357475d6355ba 100644
--- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java
@@ -173,6 +173,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
 					.addContact(txn, author, localAuthor.getId(), secretKey,
 							timestamp, true, verified, true);
 			transportPropertyManager.addRemoteProperties(txn, contactId, props);
+			db.setMailboxForContact(txn, contactId, null, contactId);
 			contact = db.getContact(txn, contactId);
 			db.commitTransaction(txn);
 		} finally {