diff --git a/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..96a114d74f9421d85f5f5369e842077376b13086
--- /dev/null
+++ b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTest.java
@@ -0,0 +1,658 @@
+package org.briarproject;
+
+import net.jodah.concurrentunit.Waiter;
+
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogInvitationRequest;
+import org.briarproject.api.blogs.BlogInvitationResponse;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostFactory;
+import org.briarproject.api.blogs.BlogSharingManager;
+import org.briarproject.api.contact.Contact;
+import org.briarproject.api.contact.ContactId;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.KeyPair;
+import org.briarproject.api.crypto.SecretKey;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.event.BlogInvitationReceivedEvent;
+import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
+import org.briarproject.api.event.Event;
+import org.briarproject.api.event.EventListener;
+import org.briarproject.api.event.MessageStateChangedEvent;
+import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.sharing.InvitationMessage;
+import org.briarproject.api.sync.ClientId;
+import org.briarproject.api.sync.SyncSession;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.api.sync.ValidationManager.State;
+import org.briarproject.api.system.Clock;
+import org.briarproject.blogs.BlogsModule;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.properties.PropertiesModule;
+import org.briarproject.sharing.SharingModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.transport.TransportModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static org.briarproject.TestPluginsModule.MAX_LATENCY;
+import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
+import static org.briarproject.api.sync.ValidationManager.State.INVALID;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class BlogSharingIntegrationTest extends BriarTestCase {
+
+	private LifecycleManager lifecycleManager0, lifecycleManager1,
+			lifecycleManager2;
+	private SyncSessionFactory sync0, sync1, sync2;
+	private BlogManager blogManager0, blogManager1;
+	private ContactManager contactManager0, contactManager1, contactManager2;
+	private Contact contact1, contact2, contact01, contact02;
+	private ContactId contactId1, contactId2, contactId01, contactId02;
+	private IdentityManager identityManager0, identityManager1,
+			identityManager2;
+	private LocalAuthor author0, author1, author2;
+	private Blog blog0, blog1, blog2;
+	private SharerListener listener0, listener2;
+	private InviteeListener listener1;
+
+	@Inject
+	Clock clock;
+	@Inject
+	AuthorFactory authorFactory;
+	@Inject
+	BlogPostFactory blogPostFactory;
+	@Inject
+	CryptoComponent cryptoComponent;
+
+	// objects accessed from background threads need to be volatile
+	private volatile BlogSharingManager blogSharingManager0;
+	private volatile BlogSharingManager blogSharingManager1;
+	private volatile BlogSharingManager blogSharingManager2;
+	private volatile Waiter eventWaiter;
+	private volatile Waiter msgWaiter;
+
+	private final File testDir = TestUtils.getTestDirectory();
+	private final SecretKey master = TestUtils.getSecretKey();
+	private final int TIMEOUT = 15000;
+	private final String SHARER = "Sharer";
+	private final String INVITEE = "Invitee";
+	private final String CONTACT2 = "Contact2";
+
+	private static final Logger LOG =
+			Logger.getLogger(BlogSharingIntegrationTest.class.getName());
+
+	private BlogSharingIntegrationTestComponent t0, t1, t2;
+
+	@Rule
+	public ExpectedException thrown = ExpectedException.none();
+
+	@Before
+	public void setUp() {
+		BlogSharingIntegrationTestComponent component =
+				DaggerBlogSharingIntegrationTestComponent.builder().build();
+		component.inject(this);
+		injectEagerSingletons(component);
+
+		assertTrue(testDir.mkdirs());
+		File t0Dir = new File(testDir, SHARER);
+		t0 = DaggerBlogSharingIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
+		injectEagerSingletons(t0);
+		File t1Dir = new File(testDir, INVITEE);
+		t1 = DaggerBlogSharingIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
+		injectEagerSingletons(t1);
+		File t2Dir = new File(testDir, CONTACT2);
+		t2 = DaggerBlogSharingIntegrationTestComponent.builder()
+				.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
+		injectEagerSingletons(t2);
+
+		identityManager0 = t0.getIdentityManager();
+		identityManager1 = t1.getIdentityManager();
+		identityManager2 = t2.getIdentityManager();
+		contactManager0 = t0.getContactManager();
+		contactManager1 = t1.getContactManager();
+		contactManager2 = t2.getContactManager();
+		blogManager0 = t0.getBlogManager();
+		blogManager1 = t1.getBlogManager();
+		blogSharingManager0 = t0.getBlogSharingManager();
+		blogSharingManager1 = t1.getBlogSharingManager();
+		blogSharingManager2 = t2.getBlogSharingManager();
+		sync0 = t0.getSyncSessionFactory();
+		sync1 = t1.getSyncSessionFactory();
+		sync2 = t2.getSyncSessionFactory();
+
+		// initialize waiters fresh for each test
+		eventWaiter = new Waiter();
+		msgWaiter = new Waiter();
+	}
+
+	@Test
+	public void testPersonalBlogCannotBeSharedWithOwner() throws Exception {
+		startLifecycles();
+		defaultInit(true);
+
+		assertFalse(blogSharingManager0.canBeShared(blog1.getId(), contact1));
+		assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact2));
+		assertFalse(blogSharingManager1.canBeShared(blog0.getId(), contact01));
+		assertFalse(blogSharingManager2.canBeShared(blog0.getId(), contact02));
+
+		// create invitation
+		blogSharingManager0
+				.sendInvitation(blog1.getId(), contactId1, "Hi!");
+
+		// sync invitation
+		sync0To1();
+		// make sure the invitee ignored the request for their own blog
+		assertFalse(listener1.requestReceived);
+
+		stopLifecycles();
+	}
+
+	@Test
+	public void testSuccessfulSharing() throws Exception {
+		startLifecycles();
+
+		// initialize and let invitee accept all requests
+		defaultInit(true);
+
+		// send invitation
+		blogSharingManager0
+				.sendInvitation(blog2.getId(), contactId1, "Hi!");
+
+		// invitee has own blog and that of the sharer
+		assertEquals(2, blogManager1.getBlogs().size());
+
+		// sync first request message
+		sync0To1();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener1.requestReceived);
+
+		// sync response back
+		sync1To0();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener0.responseReceived);
+
+		// blog was added successfully
+		assertEquals(0, blogSharingManager0.getInvited().size());
+		assertEquals(3, blogManager1.getBlogs().size());
+
+		// invitee has one invitation message from sharer
+		List<InvitationMessage> list =
+				new ArrayList<>(blogSharingManager1
+						.getInvitationMessages(contactId01));
+		assertEquals(2, list.size());
+		// check other things are alright with the message
+		for (InvitationMessage m : list) {
+			if (m instanceof BlogInvitationRequest) {
+				BlogInvitationRequest invitation =
+						(BlogInvitationRequest) m;
+				assertFalse(invitation.isAvailable());
+				assertEquals(blog2.getAuthor().getName(),
+						invitation.getBlogAuthorName());
+				assertEquals(contactId1, invitation.getContactId());
+				assertEquals("Hi!", invitation.getMessage());
+			} else {
+				BlogInvitationResponse response =
+						(BlogInvitationResponse) m;
+				assertEquals(contactId01, response.getContactId());
+				assertTrue(response.wasAccepted());
+				assertTrue(response.isLocal());
+			}
+		}
+		// sharer has own invitation message and response
+		assertEquals(2,
+				blogSharingManager0.getInvitationMessages(contactId1)
+						.size());
+		// blog can not be shared again
+		assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1));
+		assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01));
+
+		stopLifecycles();
+	}
+
+	@Test
+	public void testDeclinedSharing() throws Exception {
+		startLifecycles();
+
+		// initialize and let invitee deny all requests
+		defaultInit(false);
+
+		// send invitation
+		blogSharingManager0
+				.sendInvitation(blog2.getId(), contactId1, null);
+
+		// sync first request message
+		sync0To1();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener1.requestReceived);
+
+		// sync response back
+		sync1To0();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener0.responseReceived);
+
+		// blog was not added
+		assertEquals(0, blogSharingManager0.getInvited().size());
+		assertEquals(2, blogManager1.getBlogs().size());
+		// blog is no longer available to invitee who declined
+		assertEquals(0, blogSharingManager1.getInvited().size());
+
+		// invitee has one invitation message from sharer and one response
+		List<InvitationMessage> list =
+				new ArrayList<>(blogSharingManager1
+						.getInvitationMessages(contactId01));
+		assertEquals(2, list.size());
+		// check things are alright with the  message
+		for (InvitationMessage m : list) {
+			if (m instanceof BlogInvitationRequest) {
+				BlogInvitationRequest invitation =
+						(BlogInvitationRequest) m;
+				assertFalse(invitation.isAvailable());
+				assertEquals(blog2.getAuthor().getName(),
+						invitation.getBlogAuthorName());
+				assertEquals(contactId1, invitation.getContactId());
+				assertEquals(null, invitation.getMessage());
+			} else {
+				BlogInvitationResponse response =
+						(BlogInvitationResponse) m;
+				assertEquals(contactId01, response.getContactId());
+				assertFalse(response.wasAccepted());
+				assertTrue(response.isLocal());
+			}
+		}
+		// sharer has own invitation message and response
+		assertEquals(2,
+				blogSharingManager0.getInvitationMessages(contactId1)
+						.size());
+		// blog can be shared again
+		assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
+
+		stopLifecycles();
+	}
+
+	@Test
+	public void testInviteeLeavesAfterFinished() throws Exception {
+		startLifecycles();
+
+		// initialize and let invitee accept all requests
+		defaultInit(true);
+
+		// send invitation
+		blogSharingManager0
+				.sendInvitation(blog2.getId(), contactId1, "Hi!");
+
+		// sync first request message
+		sync0To1();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener1.requestReceived);
+
+		// sync response back
+		sync1To0();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener0.responseReceived);
+
+		// blog was added successfully
+		assertEquals(0, blogSharingManager0.getInvited().size());
+		assertEquals(3, blogManager1.getBlogs().size());
+		assertTrue(blogManager1.getBlogs().contains(blog2));
+
+		// sharer shares blog with invitee
+		assertTrue(blogSharingManager0.getSharedWith(blog2.getId())
+				.contains(contact1));
+		// invitee gets blog shared by sharer
+		assertTrue(blogSharingManager1.getSharedBy(blog2.getId())
+				.contains(contact01));
+
+		// invitee un-subscribes from blog
+		blogManager1.removeBlog(blog2);
+
+		// send leave message to sharer
+		sync1To0();
+
+		// blog is gone
+		assertEquals(0, blogSharingManager0.getInvited().size());
+		assertEquals(2, blogManager1.getBlogs().size());
+
+		// sharer no longer shares blog with invitee
+		assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
+				.contains(contact1));
+		// invitee no longer gets blog shared by sharer
+		assertFalse(blogSharingManager1.getSharedBy(blog2.getId())
+				.contains(contact01));
+		// blog can be shared again
+		assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
+		assertTrue(blogSharingManager1.canBeShared(blog2.getId(), contact01));
+
+		stopLifecycles();
+	}
+
+	@Test
+	public void testInvitationForExistingBlog() throws Exception {
+		startLifecycles();
+
+		// initialize and let invitee accept all requests
+		defaultInit(true);
+
+		// 1 and 2 are adding each other
+		contactManager1.addContact(author2,
+				author1.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		contactManager2.addContact(author1,
+				author2.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		assertEquals(3, blogManager1.getBlogs().size());
+
+		// sharer sends invitation for 2's blog to 1
+		blogSharingManager0
+				.sendInvitation(blog2.getId(), contactId1, "Hi!");
+
+		// sync first request message
+		sync0To1();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener1.requestReceived);
+
+		// make sure blog2 is shared by 0
+		Collection<Contact> contacts =
+				blogSharingManager1.getSharedBy(blog2.getId());
+		assertEquals(1, contacts.size());
+		assertTrue(contacts.contains(contact01));
+
+		// make sure 1 knows that they have blog2 already
+		Collection<InvitationMessage> messages =
+				blogSharingManager1.getInvitationMessages(contactId01);
+		assertEquals(2, messages.size());
+		assertEquals(blog2, blogManager1.getBlog(blog2.getId()));
+
+		// sync response back
+		sync1To0();
+		eventWaiter.await(TIMEOUT, 1);
+		assertTrue(listener0.responseReceived);
+
+		// blog was not added, because it was there already
+		assertEquals(0, blogSharingManager0.getInvited().size());
+		assertEquals(3, blogManager1.getBlogs().size());
+
+		stopLifecycles();
+	}
+
+
+	@After
+	public void tearDown() throws InterruptedException {
+		TestUtils.deleteTestDirectory(testDir);
+	}
+
+	private class SharerListener implements EventListener {
+
+		volatile boolean requestReceived = false;
+		volatile boolean responseReceived = false;
+
+		public void eventOccurred(Event e) {
+			if (e instanceof MessageStateChangedEvent) {
+				MessageStateChangedEvent event = (MessageStateChangedEvent) e;
+				State s = event.getState();
+				ClientId c = event.getClientId();
+				if ((s == DELIVERED || s == INVALID) &&
+						c.equals(blogSharingManager0.getClientId()) &&
+						!event.isLocal()) {
+					LOG.info("TEST: Sharer received message in group " +
+							event.getMessage().getGroupId().hashCode());
+					msgWaiter.resume();
+				} else if (s == DELIVERED && !event.isLocal() &&
+						c.equals(blogManager0.getClientId())) {
+					LOG.info("TEST: Sharer received blog post");
+					msgWaiter.resume();
+				}
+			} else if (e instanceof BlogInvitationResponseReceivedEvent) {
+				BlogInvitationResponseReceivedEvent event =
+						(BlogInvitationResponseReceivedEvent) e;
+				eventWaiter.assertEquals(contactId1, event.getContactId());
+				responseReceived = true;
+				eventWaiter.resume();
+			}
+			// this is only needed for tests where a blog is re-shared
+			else if (e instanceof BlogInvitationReceivedEvent) {
+				BlogInvitationReceivedEvent event =
+						(BlogInvitationReceivedEvent) e;
+				eventWaiter.assertEquals(contactId1, event.getContactId());
+				requestReceived = true;
+				Blog b = event.getBlog();
+				try {
+					Contact c = contactManager0.getContact(contactId1);
+					blogSharingManager0.respondToInvitation(b, c, true);
+				} catch (DbException ex) {
+					eventWaiter.rethrow(ex);
+				} finally {
+					eventWaiter.resume();
+				}
+			}
+		}
+	}
+
+	private class InviteeListener implements EventListener {
+
+		volatile boolean requestReceived = false;
+		volatile boolean responseReceived = false;
+
+		private final boolean accept, answer;
+
+		InviteeListener(boolean accept, boolean answer) {
+			this.accept = accept;
+			this.answer = answer;
+		}
+
+		InviteeListener(boolean accept) {
+			this(accept, true);
+		}
+
+		public void eventOccurred(Event e) {
+			if (e instanceof MessageStateChangedEvent) {
+				MessageStateChangedEvent event = (MessageStateChangedEvent) e;
+				State s = event.getState();
+				ClientId c = event.getClientId();
+				if ((s == DELIVERED || s == INVALID) &&
+						c.equals(blogSharingManager0.getClientId()) &&
+						!event.isLocal()) {
+					LOG.info("TEST: Invitee received message in group " +
+							event.getMessage().getGroupId().hashCode());
+					msgWaiter.resume();
+				} else if (s == DELIVERED && !event.isLocal() &&
+						c.equals(blogManager0.getClientId())) {
+					LOG.info("TEST: Invitee received blog post");
+					msgWaiter.resume();
+				}
+			} else if (e instanceof BlogInvitationReceivedEvent) {
+				BlogInvitationReceivedEvent event =
+						(BlogInvitationReceivedEvent) e;
+				requestReceived = true;
+				if (!answer) return;
+				Blog b = event.getBlog();
+				try {
+					eventWaiter.assertEquals(1,
+							blogSharingManager1.getInvited().size());
+					Contact c =
+							contactManager1.getContact(event.getContactId());
+					blogSharingManager1.respondToInvitation(b, c, accept);
+				} catch (DbException ex) {
+					eventWaiter.rethrow(ex);
+				} finally {
+					eventWaiter.resume();
+				}
+			}
+			// this is only needed for tests where a blog is re-shared
+			else if (e instanceof BlogInvitationResponseReceivedEvent) {
+				BlogInvitationResponseReceivedEvent event =
+						(BlogInvitationResponseReceivedEvent) e;
+				eventWaiter.assertEquals(contactId01, event.getContactId());
+				responseReceived = true;
+				eventWaiter.resume();
+			}
+		}
+	}
+
+	private void startLifecycles() throws InterruptedException {
+		// Start the lifecycle manager and wait for it to finish
+		lifecycleManager0 = t0.getLifecycleManager();
+		lifecycleManager1 = t1.getLifecycleManager();
+		lifecycleManager2 = t2.getLifecycleManager();
+		lifecycleManager0.startServices();
+		lifecycleManager1.startServices();
+		lifecycleManager2.startServices();
+		lifecycleManager0.waitForStartup();
+		lifecycleManager1.waitForStartup();
+		lifecycleManager2.waitForStartup();
+	}
+
+	private void stopLifecycles() throws InterruptedException {
+		// Clean up
+		lifecycleManager0.stopServices();
+		lifecycleManager1.stopServices();
+		lifecycleManager2.stopServices();
+		lifecycleManager0.waitForShutdown();
+		lifecycleManager1.waitForShutdown();
+		lifecycleManager2.waitForShutdown();
+	}
+
+	private void defaultInit(boolean accept) throws DbException {
+		addDefaultIdentities();
+		addDefaultContacts();
+		getPersonalBlogOfSharer();
+		listenToEvents(accept);
+	}
+
+	private void addDefaultIdentities() throws DbException {
+		KeyPair keyPair = cryptoComponent.generateSignatureKeyPair();
+		author0 = authorFactory.createLocalAuthor(SHARER,
+				keyPair.getPublic().getEncoded(),
+				keyPair.getPrivate().getEncoded());
+		identityManager0.addLocalAuthor(author0);
+
+		keyPair = cryptoComponent.generateSignatureKeyPair();
+		author1 = authorFactory.createLocalAuthor(INVITEE,
+				keyPair.getPublic().getEncoded(),
+				keyPair.getPrivate().getEncoded());
+		identityManager1.addLocalAuthor(author1);
+
+		keyPair = cryptoComponent.generateSignatureKeyPair();
+		author2 = authorFactory.createLocalAuthor(CONTACT2,
+				keyPair.getPublic().getEncoded(),
+				keyPair.getPrivate().getEncoded());
+		identityManager2.addLocalAuthor(author2);
+	}
+
+	private void addDefaultContacts() throws DbException {
+		// sharer adds invitee as contact
+		contactId1 = contactManager0.addContact(author1,
+				author0.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		contact1 = contactManager0.getContact(contactId1);
+		// sharer adds second contact
+		contactId2 = contactManager0.addContact(author2,
+				author0.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		contact2 = contactManager0.getContact(contactId2);
+		// contacts add sharer back
+		contactId01 = contactManager1.addContact(author0,
+				author1.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		contact01 = contactManager1.getContact(contactId01);
+		contactId02 = contactManager2.addContact(author0,
+				author2.getId(), master, clock.currentTimeMillis(), true,
+				true
+		);
+		contact02 = contactManager2.getContact(contactId02);
+	}
+
+	private void getPersonalBlogOfSharer() throws DbException {
+		blog0 = blogManager0.getPersonalBlog(author0);
+		blog1 = blogManager0.getPersonalBlog(author1);
+		blog2 = blogManager0.getPersonalBlog(author2);
+	}
+
+	private void listenToEvents(boolean accept) {
+		listener0 = new SharerListener();
+		t0.getEventBus().addListener(listener0);
+		listener1 = new InviteeListener(accept);
+		t1.getEventBus().addListener(listener1);
+		listener2 = new SharerListener();
+		t2.getEventBus().addListener(listener2);
+	}
+
+	private void sync0To1() throws IOException, TimeoutException {
+		deliverMessage(sync0, contactId01, sync1, contactId1,
+				"Sharer to Invitee");
+	}
+
+	private void sync1To0() throws IOException, TimeoutException {
+		deliverMessage(sync1, contactId1, sync0, contactId01,
+				"Invitee to Sharer");
+	}
+
+	private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
+			SyncSessionFactory toSync, ContactId toId, String debug)
+			throws IOException, TimeoutException {
+
+		if (debug != null) LOG.info("TEST: Sending message from " + debug);
+
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		// Create an outgoing sync session
+		SyncSession sessionFrom =
+				fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
+		// Write whatever needs to be written
+		sessionFrom.run();
+		out.close();
+
+		ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+		// Create an incoming sync session
+		SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
+		// Read whatever needs to be read
+		sessionTo.run();
+		in.close();
+
+		// wait for message to actually arrive
+		msgWaiter.await(TIMEOUT, 1);
+	}
+
+	private void injectEagerSingletons(
+			BlogSharingIntegrationTestComponent component) {
+
+		component.inject(new LifecycleModule.EagerSingletons());
+		component.inject(new BlogsModule.EagerSingletons());
+		component.inject(new CryptoModule.EagerSingletons());
+		component.inject(new ContactModule.EagerSingletons());
+		component.inject(new TransportModule.EagerSingletons());
+		component.inject(new SharingModule.EagerSingletons());
+		component.inject(new SyncModule.EagerSingletons());
+		component.inject(new PropertiesModule.EagerSingletons());
+	}
+
+}
diff --git a/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f738830e5082e215803fe1c31ff700eb04dd3bd
--- /dev/null
+++ b/briar-android-tests/src/test/java/org/briarproject/BlogSharingIntegrationTestComponent.java
@@ -0,0 +1,100 @@
+package org.briarproject;
+
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogSharingManager;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.clients.MessageQueueManager;
+import org.briarproject.api.clients.PrivateGroupFactory;
+import org.briarproject.api.contact.ContactManager;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.event.EventBus;
+import org.briarproject.api.forum.ForumManager;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.lifecycle.LifecycleManager;
+import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.blogs.BlogsModule;
+import org.briarproject.clients.ClientsModule;
+import org.briarproject.contact.ContactModule;
+import org.briarproject.crypto.CryptoModule;
+import org.briarproject.data.DataModule;
+import org.briarproject.db.DatabaseModule;
+import org.briarproject.event.EventModule;
+import org.briarproject.forum.ForumModule;
+import org.briarproject.identity.IdentityModule;
+import org.briarproject.lifecycle.LifecycleModule;
+import org.briarproject.properties.PropertiesModule;
+import org.briarproject.sharing.SharingModule;
+import org.briarproject.sync.SyncModule;
+import org.briarproject.system.SystemModule;
+import org.briarproject.transport.TransportModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestDatabaseModule.class,
+		TestPluginsModule.class,
+		TestSeedProviderModule.class,
+		ClientsModule.class,
+		ContactModule.class,
+		CryptoModule.class,
+		DataModule.class,
+		DatabaseModule.class,
+		EventModule.class,
+		BlogsModule.class,
+		ForumModule.class,
+		IdentityModule.class,
+		LifecycleModule.class,
+		PropertiesModule.class,
+		SharingModule.class,
+		SyncModule.class,
+		SystemModule.class,
+		TransportModule.class
+})
+interface BlogSharingIntegrationTestComponent {
+
+	void inject(BlogSharingIntegrationTest testCase);
+
+	void inject(ContactModule.EagerSingletons init);
+
+	void inject(CryptoModule.EagerSingletons init);
+
+	void inject(BlogsModule.EagerSingletons init);
+
+	void inject(LifecycleModule.EagerSingletons init);
+
+	void inject(PropertiesModule.EagerSingletons init);
+
+	void inject(SharingModule.EagerSingletons init);
+
+	void inject(SyncModule.EagerSingletons init);
+
+	void inject(TransportModule.EagerSingletons init);
+
+	LifecycleManager getLifecycleManager();
+
+	EventBus getEventBus();
+
+	IdentityManager getIdentityManager();
+
+	ContactManager getContactManager();
+
+	BlogSharingManager getBlogSharingManager();
+
+	BlogManager getBlogManager();
+
+	SyncSessionFactory getSyncSessionFactory();
+
+	/* the following methods are only needed to manually construct messages */
+
+	DatabaseComponent getDatabaseComponent();
+
+	PrivateGroupFactory getPrivateGroupFactory();
+
+	ClientHelper getClientHelper();
+
+	MessageQueueManager getMessageQueueManager();
+
+}
diff --git a/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java
index 7e74d841b01a425caec7deac4ce9235d3c77767a..74c771488f832a608ebf66ee610a0e5fd6e1ddc2 100644
--- a/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java
+++ b/briar-android-tests/src/test/java/org/briarproject/ForumManagerTestComponent.java
@@ -7,6 +7,7 @@ import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.blogs.BlogsModule;
 import org.briarproject.clients.ClientsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
@@ -38,6 +39,7 @@ import dagger.Component;
 		DatabaseModule.class,
 		EventModule.class,
 		ForumModule.class,
+		BlogsModule.class,
 		IdentityModule.class,
 		LifecycleModule.class,
 		PropertiesModule.class,
@@ -46,7 +48,7 @@ import dagger.Component;
 		SystemModule.class,
 		TransportModule.class
 })
-public interface ForumManagerTestComponent {
+interface ForumManagerTestComponent {
 
 	void inject(ForumManagerTest testCase);
 
diff --git a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java
index 8e3ecdf9f6b5ce55cf19f5ef51a135f65bda6eea..6f3da3c1c0c05abbb5cad6132a0665b35c8154d7 100644
--- a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java
+++ b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTestComponent.java
@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.sync.SyncSessionFactory;
+import org.briarproject.blogs.BlogsModule;
 import org.briarproject.clients.ClientsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
@@ -42,6 +43,7 @@ import dagger.Component;
 		DatabaseModule.class,
 		EventModule.class,
 		ForumModule.class,
+		BlogsModule.class,
 		IdentityModule.class,
 		LifecycleModule.class,
 		PropertiesModule.class,
@@ -50,7 +52,7 @@ import dagger.Component;
 		SystemModule.class,
 		TransportModule.class
 })
-public interface ForumSharingIntegrationTestComponent {
+interface ForumSharingIntegrationTestComponent {
 
 	void inject(ForumSharingIntegrationTest testCase);
 
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
index 7d6188feda24f7c3fdaf06b28460c0878c467676..f39a90d629fc6a244854cb2f1737b72a9c435161 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogManager.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
@@ -36,7 +36,7 @@ public interface BlogManager {
 	Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
 
 	/** Returns only the personal blog of the given author. */
-	Blog getPersonalBlog(Author author) throws DbException;
+	Blog getPersonalBlog(Author author);
 
 	/** Returns all blogs to which the user subscribes. */
 	Collection<Blog> getBlogs() throws DbException;
diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
index e89f49d8ac0ea279093234cdcfeba0e4a3113fc4..3c2b10953c4ff7e31ab886913f3c54c5a6a66bba 100644
--- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java
+++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
@@ -20,6 +20,12 @@ public interface IdentityManager {
 	/** Returns the local pseudonym with the given ID. */
 	LocalAuthor getLocalAuthor(AuthorId a) throws DbException;
 
+	/** Returns the main local identity. */
+	LocalAuthor getLocalAuthor() throws DbException;
+
+	/** Returns the main local identity within the given Transaction. */
+	LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
+
 	/** Returns all local pseudonyms. */
 	Collection<LocalAuthor> getLocalAuthors() throws DbException;
 
diff --git a/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java
index 9a3e0583aa1f04952920139ea626f43d44e72de3..68fbe1258e9f09e16730c9578208c8773badd3b2 100644
--- a/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java
@@ -69,7 +69,7 @@ class BlogFactoryImpl implements BlogFactory {
 		Author a =
 				authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
 		// TODO change permanent depending on how this will be used
-		boolean permanent = false;
+		boolean permanent = true;
 		return new Blog(g, blog.getString(0), description, a, permanent);
 	}
 
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
index a35bc0bc7fbad33d9b2928941fec1e91668ab16a..5a20d8934f07417394e330b206cdbf8d1c4eb613 100644
--- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -282,7 +282,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 	}
 
 	@Override
-	public Blog getPersonalBlog(Author author) throws DbException {
+	public Blog getPersonalBlog(Author author) {
 		return blogFactory.createPersonalBlog(author);
 	}
 
diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
index bd14c334076b222f0218b5783118e3a5cd915c11..235448b461f32b32212b9e6d9b2ffb976b911b28 100644
--- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
+++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
@@ -10,9 +10,7 @@ import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.inject.Inject;
@@ -69,12 +67,22 @@ class IdentityManagerImpl implements IdentityManager {
 		return author;
 	}
 
+	@Override
+	public LocalAuthor getLocalAuthor() throws DbException {
+		return getLocalAuthors().iterator().next();
+	}
+
+	@Override
+	public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
+		return getLocalAuthors(txn).iterator().next();
+	}
+
 	@Override
 	public Collection<LocalAuthor> getLocalAuthors() throws DbException {
 		Collection<LocalAuthor> authors;
 		Transaction txn = db.startTransaction(true);
 		try {
-			authors = db.getLocalAuthors(txn);
+			authors = getLocalAuthors(txn);
 			txn.setComplete();
 		} finally {
 			db.endTransaction(txn);
@@ -82,6 +90,12 @@ class IdentityManagerImpl implements IdentityManager {
 		return authors;
 	}
 
+	private Collection<LocalAuthor> getLocalAuthors(Transaction txn)
+			throws DbException {
+
+		return db.getLocalAuthors(txn);
+	}
+
 	@Override
 	public void removeLocalAuthor(AuthorId a) throws DbException {
 		Transaction txn = db.startTransaction(false);
diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
index 19f9eef34dfc269ded88f7e2afc40ebf2e3f59f1..7c1ce43bbb0301f8d34f86ece817362d4e66906b 100644
--- a/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/BlogSharingManagerImpl.java
@@ -26,6 +26,8 @@ import org.briarproject.api.event.BlogInvitationReceivedEvent;
 import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.sharing.InvitationMessage;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.GroupId;
@@ -50,6 +52,8 @@ class BlogSharingManagerImpl extends
 			"bee438b5de0b3a685badc4e49d76e72d"
 					+ "21e01c4b569a775112756bdae267a028"));
 
+	@Inject
+	IdentityManager identityManager;
 	private final BlogManager blogManager;
 
 	private final SFactory sFactory;
@@ -84,10 +88,19 @@ class BlogSharingManagerImpl extends
 	}
 
 	@Override
-	public boolean canBeShared(GroupId g, Contact c) throws DbException {
-		Blog b = blogManager.getPersonalBlog(c.getAuthor());
+	protected boolean canBeShared(Transaction txn, GroupId g, Contact c)
+			throws DbException {
+
+		// check if g is our personal blog
+		LocalAuthor author = identityManager.getLocalAuthor(txn);
+		Blog b = blogManager.getPersonalBlog(author);
 		if (b.getId().equals(g)) return false;
-		return super.canBeShared(g, c);
+
+		// check if g is c's personal blog
+		b = blogManager.getPersonalBlog(c.getAuthor());
+		if (b.getId().equals(g)) return false;
+
+		return super.canBeShared(txn, g, c);
 	}
 
 	@Override
diff --git a/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java b/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java
index 26b89bb52c512c118e02ea5075226e42b5829eda..1a9f0ada3897cc22be668afd7c47c44cf7ea6f34 100644
--- a/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java
+++ b/briar-core/src/org/briarproject/sharing/BlogSharingValidator.java
@@ -58,7 +58,7 @@ class BlogSharingValidator extends BdfMessageValidator {
 			checkLength(name, 1, MAX_BLOG_TITLE_LENGTH);
 
 			String desc = body.getString(3);
-			checkLength(desc, 1, MAX_BLOG_DESC_LENGTH);
+			checkLength(desc, 0, MAX_BLOG_DESC_LENGTH);
 
 			BdfList author = body.getList(4);
 			checkSize(author, 2);
diff --git a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
index 5f6fd4d4111bc697df381907d57aea7ccebdbf5b..24b2b5223d50dc0e83c3904f8d806fddc2988617 100644
--- a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
+++ b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java
@@ -500,7 +500,7 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
 		return canBeShared;
 	}
 
-	private boolean canBeShared(Transaction txn, GroupId g, Contact c)
+	protected boolean canBeShared(Transaction txn, GroupId g, Contact c)
 			throws DbException {
 
 		try {