diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java
index 7e89a7a7bf17c8ae7cb4eb78576c54a4ece1f720..b7c208ff29feb9bafe1a67d47643ee28077f8d18 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/Author.java
@@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable;
 @NotNullByDefault
 public class Author {
 
-	public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES}
+	public enum Status {
+		NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
+	}
 
 	private final AuthorId id;
 	private final String name;
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 54238266b28389ad1146758590d6c441607c4046..7ae01fe47a056d4d1b7cf361ab3596032e52b25a 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
@@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
 @NotNullByDefault
 abstract class JdbcDatabase implements Database<Connection> {
 
-	private static final int SCHEMA_VERSION = 29;
-	private static final int MIN_SCHEMA_VERSION = 29;
+	private static final int SCHEMA_VERSION = 30;
+	private static final int MIN_SCHEMA_VERSION = 30;
 
 	private static final String CREATE_SETTINGS =
 			"CREATE TABLE settings"
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..e142a24790e2348a92c759798a507ac81c70920e
--- /dev/null
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestSocksModule.java
@@ -0,0 +1,16 @@
+package org.briarproject.bramble.test;
+
+import javax.net.SocketFactory;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class TestSocksModule {
+
+	@Provides
+	SocketFactory provideSocketFactory() {
+		return SocketFactory.getDefault();
+	}
+
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
index d8561ad4d0401e0256ae61c0be6f1c068db7511e..546f9ac18005508ef1e79de8cd539c6ccf05fe92 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostItem.java
@@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
 		return body;
 	}
 
+	public boolean isRssFeed() {
+		return header.isRssFeed();
+	}
+
 	public boolean isRead() {
 		return read;
 	}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
index ba1d11e6832e2f319f4f7880260775ae0c9e83ed..76232fc5755fc52474c25f4203a2a2e7a1cbf196 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java
@@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 		author.setAuthor(a);
 		author.setAuthorStatus(post.getAuthorStatus());
 		author.setDate(post.getTimestamp());
-		author.setPersona(AuthorView.NORMAL);
+		author.setPersona(
+				item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
 		// TODO make author clickable more often #624
 		if (item.getHeader().getType() == POST) {
 			author.setBlogLink(post.getGroupId());
@@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
 		reblogger.setVisibility(VISIBLE);
 		reblogger.setPersona(AuthorView.REBLOGGER);
 
-		author.setPersona(AuthorView.COMMENTER);
+		author.setPersona(item.getHeader().getRootPost().isRssFeed() ?
+				AuthorView.RSS_FEED_REBLOGGED :
+				AuthorView.COMMENTER);
 
 		// comments
 		for (BlogCommentHeader c : item.getComments()) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java
index 82a4320918fa1e80ce1bc2c80742b71ec4322b96..c7a64659a68367487f6f630bce75e24b34e28f8b 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/FeedFragment.java
@@ -179,7 +179,6 @@ public class FeedFragment extends BaseFragment implements
 			case R.id.action_rss_feeds_import:
 				Intent i2 =
 						new Intent(getActivity(), RssFeedImportActivity.class);
-				i2.putExtra(GROUP_ID, personalBlog.getId().getBytes());
 				startActivity(i2);
 				return true;
 			case R.id.action_rss_feeds_manage:
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java
index 3052e3694f22d42ac3001d3c3e7208b93b0fc9b8..e4ed107b6a9686509956a6e2bc717f417763dcd0 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedAdapter.java
@@ -6,7 +6,7 @@ import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.widget.ImageView;
+import android.widget.ImageButton;
 import android.widget.TextView;
 
 import org.briarproject.briar.R;
@@ -39,12 +39,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
 		if (item == null) return;
 
 		// Feed Title
-		if (item.getTitle() != null) {
-			ui.title.setText(item.getTitle());
-			ui.title.setVisibility(VISIBLE);
-		} else {
-			ui.title.setVisibility(GONE);
-		}
+		ui.title.setText(item.getTitle());
 
 		// Delete Button
 		ui.delete.setOnClickListener(new OnClickListener() {
@@ -75,6 +70,14 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
 		} else {
 			ui.description.setVisibility(GONE);
 		}
+
+		// Open feed's blog when clicked
+		ui.layout.setOnClickListener(new OnClickListener() {
+			@Override
+			public void onClick(View v) {
+				listener.onFeedClick(item);
+			}
+		});
 	}
 
 	@Override
@@ -99,8 +102,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
 	}
 
 	static class FeedViewHolder extends RecyclerView.ViewHolder {
+		private final View layout;
 		private final TextView title;
-		private final ImageView delete;
+		private final ImageButton delete;
 		private final TextView imported;
 		private final TextView updated;
 		private final TextView author;
@@ -110,8 +114,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
 		private FeedViewHolder(View v) {
 			super(v);
 
+			layout = v;
 			title = (TextView) v.findViewById(R.id.titleView);
-			delete = (ImageView) v.findViewById(R.id.deleteButton);
+			delete = (ImageButton) v.findViewById(R.id.deleteButton);
 			imported = (TextView) v.findViewById(R.id.importedView);
 			updated = (TextView) v.findViewById(R.id.updatedView);
 			author = (TextView) v.findViewById(R.id.authorView);
@@ -121,6 +126,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
 	}
 
 	interface RssFeedListener {
+		void onFeedClick(Feed feed);
 		void onDeleteClick(Feed feed);
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
index 7e2085818265acdf4dd09b2d556e726828438f3b..1d09ad2c428def1e37980d472cc8c587330bfd36 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedImportActivity.java
@@ -1,7 +1,6 @@
 package org.briarproject.briar.android.blog;
 
 import android.content.DialogInterface;
-import android.content.Intent;
 import android.os.Bundle;
 import android.support.v7.app.AlertDialog;
 import android.text.Editable;
@@ -15,7 +14,6 @@ import android.widget.ProgressBar;
 
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.lifecycle.IoExecutor;
-import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.activity.ActivityComponent;
 import org.briarproject.briar.android.activity.BriarActivity;
@@ -44,9 +42,6 @@ public class RssFeedImportActivity extends BriarActivity {
 	@IoExecutor
 	Executor ioExecutor;
 
-	// Fields that are accessed from background threads must be volatile
-	private volatile GroupId groupId = null;
-
 	@Inject
 	@SuppressWarnings("WeakerAccess")
 	volatile FeedManager feedManager;
@@ -55,12 +50,6 @@ public class RssFeedImportActivity extends BriarActivity {
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 
-		// GroupId from Intent
-		Intent i = getIntent();
-		byte[] b = i.getByteArrayExtra(GROUP_ID);
-		if (b == null) throw new IllegalStateException("No Group in intent.");
-		groupId = new GroupId(b);
-
 		setContentView(R.layout.activity_rss_feed_import);
 
 		urlInput = (EditText) findViewById(R.id.urlInput);
@@ -128,7 +117,7 @@ public class RssFeedImportActivity extends BriarActivity {
 			@Override
 			public void run() {
 				try {
-					feedManager.addFeed(url, groupId);
+					feedManager.addFeed(url);
 					feedImported();
 				} catch (DbException | IOException e) {
 					if (LOG.isLoggable(WARNING))
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
index 10e37d6f27d204d2756fd7438a854f7c4e033f7a..8525f1f653f5e1547612504624567664b1cea465 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/RssFeedManageActivity.java
@@ -1,15 +1,16 @@
 package org.briarproject.briar.android.blog;
 
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.design.widget.Snackbar;
+import android.support.v7.app.AlertDialog;
 import android.support.v7.widget.LinearLayoutManager;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 
 import org.briarproject.bramble.api.db.DbException;
-import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.briar.R;
 import org.briarproject.briar.android.activity.ActivityComponent;
 import org.briarproject.briar.android.activity.BriarActivity;
@@ -23,6 +24,7 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.support.design.widget.Snackbar.LENGTH_LONG;
 import static java.util.logging.Level.WARNING;
 
@@ -34,7 +36,6 @@ public class RssFeedManageActivity extends BriarActivity
 
 	private BriarRecyclerView list;
 	private RssFeedAdapter adapter;
-	private GroupId groupId;
 
 	@Inject
 	@SuppressWarnings("WeakerAccess")
@@ -44,12 +45,6 @@ public class RssFeedManageActivity extends BriarActivity
 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 
-		// GroupId from Intent
-		Intent i = getIntent();
-		byte[] b = i.getByteArrayExtra(GROUP_ID);
-		if (b == null) throw new IllegalStateException("No Group in intent.");
-		groupId = new GroupId(b);
-
 		setContentView(R.layout.activity_rss_feed_manage);
 
 		adapter = new RssFeedAdapter(this, this);
@@ -87,7 +82,6 @@ public class RssFeedManageActivity extends BriarActivity
 				return true;
 			case R.id.action_rss_feeds_import:
 				Intent i = new Intent(this, RssFeedImportActivity.class);
-				i.putExtra(GROUP_ID, groupId.getBytes());
 				startActivity(i);
 				return true;
 			default:
@@ -100,21 +94,32 @@ public class RssFeedManageActivity extends BriarActivity
 		component.inject(this);
 	}
 
+	@Override
+	public void onFeedClick(Feed feed) {
+		Intent i = new Intent(this, BlogActivity.class);
+		i.putExtra(GROUP_ID, feed.getBlogId().getBytes());
+		i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
+		startActivity(i);
+	}
+
 	@Override
 	public void onDeleteClick(final Feed feed) {
-		runOnDbThread(new Runnable() {
-			@Override
-			public void run() {
-				try {
-					feedManager.removeFeed(feed.getUrl());
-					onFeedDeleted(feed);
-				} catch (DbException e) {
-					if (LOG.isLoggable(WARNING))
-						LOG.log(WARNING, e.toString(), e);
-					onDeleteError();
-				}
-			}
-		});
+		DialogInterface.OnClickListener okListener =
+				new DialogInterface.OnClickListener() {
+					@Override
+					public void onClick(DialogInterface dialog, int which) {
+						deleteFeed(feed);
+					}
+				};
+		AlertDialog.Builder builder = new AlertDialog.Builder(this,
+				R.style.BriarDialogTheme);
+		builder.setTitle(getString(R.string.blogs_rss_remove_feed));
+		builder.setMessage(
+				getString(R.string.blogs_rss_remove_feed_dialog_message));
+		builder.setPositiveButton(R.string.cancel, null);
+		builder.setNegativeButton(R.string.blogs_rss_remove_feed_ok,
+				okListener);
+		builder.show();
 	}
 
 	private void loadFeeds() {
@@ -149,6 +154,22 @@ public class RssFeedManageActivity extends BriarActivity
 		});
 	}
 
+	private void deleteFeed(final Feed feed) {
+		runOnDbThread(new Runnable() {
+			@Override
+			public void run() {
+				try {
+					feedManager.removeFeed(feed);
+					onFeedDeleted(feed);
+				} catch (DbException e) {
+					if (LOG.isLoggable(WARNING))
+						LOG.log(WARNING, e.toString(), e);
+					onDeleteError();
+				}
+			}
+		});
+	}
+
 	private void onLoadError() {
 		runOnUiThreadUnlessDestroyed(new Runnable() {
 			@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
index 82442185e6b1ddb1ff08a6a5f9f2da19dc30078c..f03a88ecd1d17277bae5c70994584a9abef77683 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/view/AuthorView.java
@@ -30,6 +30,7 @@ import static android.content.Context.LAYOUT_INFLATER_SERVICE;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.graphics.Typeface.BOLD;
 import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static org.briarproject.bramble.api.identity.Author.Status.NONE;
 import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
 import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
 
@@ -40,6 +41,8 @@ public class AuthorView extends RelativeLayout {
 	public static final int REBLOGGER = 1;
 	public static final int COMMENTER = 2;
 	public static final int LIST = 3;
+	public static final int RSS_FEED = 4;
+	public static final int RSS_FEED_REBLOGGED = 5;
 
 	private final CircleImageView avatar;
 	private final ImageView avatarIcon;
@@ -83,7 +86,13 @@ public class AuthorView extends RelativeLayout {
 	}
 
 	public void setAuthorStatus(Status status) {
-		trustIndicator.setTrustLevel(status);
+		if (status != NONE) {
+			trustIndicator.setTrustLevel(status);
+			trustIndicator.setVisibility(VISIBLE);
+		} else {
+			trustIndicator.setVisibility(GONE);
+		}
+
 		if (status == OURSELVES) {
 			authorName.setTypeface(authorNameTypeface, BOLD);
 		} else {
@@ -124,10 +133,17 @@ public class AuthorView extends RelativeLayout {
 		setOnClickListener(null);
 	}
 
+	/**
+	 * Styles this view for a different persona.
+	 *
+	 * Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar
+	 *            and override the one set by
+	 *            {@link AuthorView#setAuthor(Author)}.
+	 */
 	public void setPersona(int persona) {
 		switch (persona) {
 			case NORMAL:
-				avatarIcon.setVisibility(VISIBLE);
+				avatarIcon.setVisibility(INVISIBLE);
 				date.setVisibility(VISIBLE);
 				setAvatarSize(R.dimen.blogs_avatar_normal_size);
 				setTextSize(authorName, R.dimen.text_size_small);
@@ -158,6 +174,24 @@ public class AuthorView extends RelativeLayout {
 				setCenterVertical(authorName, true);
 				setCenterVertical(trustIndicator, true);
 				break;
+			case RSS_FEED:
+				avatarIcon.setVisibility(INVISIBLE);
+				date.setVisibility(VISIBLE);
+				avatar.setImageResource(R.drawable.ic_rss_feed);
+				setAvatarSize(R.dimen.blogs_avatar_normal_size);
+				setTextSize(authorName, R.dimen.text_size_small);
+				setCenterVertical(authorName, false);
+				setCenterVertical(trustIndicator, false);
+				break;
+			case RSS_FEED_REBLOGGED:
+				avatarIcon.setVisibility(INVISIBLE);
+				date.setVisibility(VISIBLE);
+				avatar.setImageResource(R.drawable.ic_rss_feed);
+				setAvatarSize(R.dimen.blogs_avatar_comment_size);
+				setTextSize(authorName, R.dimen.text_size_tiny);
+				setCenterVertical(authorName, false);
+				setCenterVertical(trustIndicator, false);
+				break;
 		}
 	}
 
diff --git a/briar-android/src/main/res/drawable/ic_rss_feed.xml b/briar-android/src/main/res/drawable/ic_rss_feed.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6d441fa064cd0a237076a0142d60f145059e0623
--- /dev/null
+++ b/briar-android/src/main/res/drawable/ic_rss_feed.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="30dp"
+        android:height="30dp"
+        android:viewportHeight="30"
+        android:viewportWidth="30">
+
+	<path
+		android:fillColor="#ffa500"
+		android:pathData="M0,8.88178e-16 L30,8.88178e-16 L30,30 L0,30 L0,8.88178e-16 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678
+C11.9661,22.7434,10.6078,24.1017,8.9322,24.1017
+C7.25663,24.1017,5.8983,22.7434,5.8983,21.0678
+C5.8983,19.3922,7.25663,18.0339,8.9322,18.0339 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M5.8983,15 A9.1016949,9.1016949,0,0,1,15,24.1017 L18.0339,24.1017
+A12.135593,12.135593,0,0,0,5.8983,11.9661 Z"/>
+	<path
+		android:fillColor="#ffffff"
+		android:pathData="M5.8983,8.9322 A15.169492,15.169492,0,0,1,21.0678,24.1017 L24.1017,24.1017
+A18.20339,18.20339,0,0,0,5.8983,5.8983 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/src/main/res/layout/list_item_rss_feed.xml b/briar-android/src/main/res/layout/list_item_rss_feed.xml
index 76e0978a7719b623ca4b55d248eea69d87da8b14..183dfc3b0c4faea28ba30dc4e0ca7203c0052db9 100644
--- a/briar-android/src/main/res/layout/list_item_rss_feed.xml
+++ b/briar-android/src/main/res/layout/list_item_rss_feed.xml
@@ -19,7 +19,7 @@
 		android:textSize="@dimen/text_size_medium"
 		tools:text="This is a name of a RSS Feed"/>
 
-	<ImageView
+	<ImageButton
 		android:id="@+id/deleteButton"
 		android:layout_width="wrap_content"
 		android:layout_height="wrap_content"
diff --git a/briar-android/src/main/res/values/attrs.xml b/briar-android/src/main/res/values/attrs.xml
index 6d6207cd0b0843ee11b2a15f0c0ba2378a8ee350..8655d2126fc21b9c7a3fcc732e7c4ea30b7c0486 100644
--- a/briar-android/src/main/res/values/attrs.xml
+++ b/briar-android/src/main/res/values/attrs.xml
@@ -12,6 +12,8 @@
 			<enum name="reblogger" value="1"/>
 			<enum name="commenter" value="2"/>
 			<enum name="list" value="3"/>
+			<enum name="rss_feed" value="4"/>
+			<enum name="rss_feed_reblogged" value="5"/>
 		</attr>
 	</declare-styleable>
 
diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml
index 99e86b60e60929346467ce67fc34962e99de9a73..3c8a44883027d77467b7189afeea387410c2bb32 100644
--- a/briar-android/src/main/res/values/strings.xml
+++ b/briar-android/src/main/res/values/strings.xml
@@ -304,6 +304,9 @@
 	<string name="blogs_rss_feeds_manage_imported">Imported:</string>
 	<string name="blogs_rss_feeds_manage_author">Author:</string>
 	<string name="blogs_rss_feeds_manage_updated">Last Updated:</string>
+	<string name="blogs_rss_remove_feed">Remove Feed</string>
+	<string name="blogs_rss_remove_feed_dialog_message">Are you sure you want to remove this feed and all its posts?\nAny posts you have shared will not be removed from other people\'s devices.</string>
+	<string name="blogs_rss_remove_feed_ok">Remove Feed</string>
 	<string name="blogs_rss_feeds_manage_delete_error">The feed could not be deleted!</string>
 	<string name="blogs_rss_feeds_manage_empty_state">You haven\'t imported any RSS feeds.\n\nWhy don\'t you click the plus in the top right screen corner to add your first?</string>
 	<string name="blogs_rss_feeds_manage_error">There was a problem loading your feeds. Please try again later.</string>
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java
index b7e1d240fea96c14eed901a1fff18c2c9424dac9..a0e4fff021f700be77017977701959a79d2eca8a 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/Blog.java
@@ -13,16 +13,22 @@ import javax.annotation.concurrent.Immutable;
 public class Blog extends BaseGroup implements Shareable {
 
 	private final Author author;
+	private final boolean rssFeed;
 
-	public Blog(Group group, Author author) {
+	public Blog(Group group, Author author, boolean rssFeed) {
 		super(group);
 		this.author = author;
+		this.rssFeed = rssFeed;
 	}
 
 	public Author getAuthor() {
 		return author;
 	}
 
+	public boolean isRssFeed() {
+		return rssFeed;
+	}
+
 	@Override
 	public boolean equals(Object o) {
 		return o instanceof Blog && super.equals(o);
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
index 70fded28135d661b93bcb7500bb42082cefe6b15..f7914c9d55a2c45329522ea1795010c64887b5a3 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogCommentHeader.java
@@ -26,7 +26,7 @@ public class BlogCommentHeader extends BlogPostHeader {
 			Status authorStatus, boolean read) {
 
 		super(type, groupId, id, parent.getId(), timestamp,
-				timeReceived, author, authorStatus, read);
+				timeReceived, author, authorStatus, false, read);
 
 		if (type != COMMENT && type != WRAPPED_COMMENT)
 			throw new IllegalArgumentException("Incompatible Message Type");
@@ -43,4 +43,11 @@ public class BlogCommentHeader extends BlogPostHeader {
 	public BlogPostHeader getParent() {
 		return parent;
 	}
+
+	public BlogPostHeader getRootPost() {
+		if (parent instanceof BlogCommentHeader)
+			return ((BlogCommentHeader) parent).getRootPost();
+		return parent;
+	}
+
 }
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
index 99c4514b2639e51005df4459ad00d9ae28c11d86..4c8ff25d101c675ef0d92165a8b5ebf49708a954 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogConstants.java
@@ -28,6 +28,7 @@ public interface BlogConstants {
 	String KEY_AUTHOR_NAME = "name";
 	String KEY_PUBLIC_KEY = "publicKey";
 	String KEY_AUTHOR = "author";
+	String KEY_RSS_FEED = "rssFeed";
 	String KEY_READ = "read";
 	String KEY_COMMENT = "comment";
 	String KEY_ORIGINAL_MSG_ID = "originalMessageId";
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java
index 4b0a89cf377b3f8c036cd206e1f15a8c78e9be4b..a9d057501da211b41d383b1cad7bf47f110e748f 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogFactory.java
@@ -13,6 +13,11 @@ public interface BlogFactory {
 	 */
 	Blog createBlog(Author author);
 
+	/**
+	 * Creates a RSS feed blog for a given author.
+	 */
+	Blog createFeedBlog(Author author);
+
 	/**
 	 * Parses a blog with the given Group
 	 */
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java
index 20ffb991cac62a97aa50182af88299e2b5ff08fd..041d474a4eca490b8444c47a9feac2c652792800 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogManager.java
@@ -41,6 +41,11 @@ public interface BlogManager {
 	 */
 	void removeBlog(Blog b) throws DbException;
 
+	/**
+	 * Removes and deletes a blog with the given {@link Transaction}.
+	 */
+	void removeBlog(Transaction txn, Blog b) throws DbException;
+
 	/**
 	 * Stores a local blog post.
 	 */
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
index cf01dfc57117deb72e48df2ba304dd16f90d9162..57a0ef58a93968c690335bb53736f76678eb2518 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/blog/BlogPostHeader.java
@@ -17,21 +17,23 @@ public class BlogPostHeader extends PostHeader {
 	private final MessageType type;
 	private final GroupId groupId;
 	private final long timeReceived;
+	private final boolean rssFeed;
 
 	public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
 			@Nullable MessageId parentId, long timestamp, long timeReceived,
-			Author author, Status authorStatus, boolean read) {
+			Author author, Status authorStatus, boolean rssFeed, boolean read) {
 		super(id, parentId, timestamp, author, authorStatus, read);
 		this.type = type;
 		this.groupId = groupId;
 		this.timeReceived = timeReceived;
+		this.rssFeed = rssFeed;
 	}
 
 	public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
 			long timestamp, long timeReceived, Author author,
-			Status authorStatus, boolean read) {
+			Status authorStatus, boolean rssFeed, boolean read) {
 		this(type, groupId, id, null, timestamp, timeReceived, author,
-				authorStatus, read);
+				authorStatus, rssFeed, read);
 	}
 
 	public MessageType getType() {
@@ -45,4 +47,9 @@ public class BlogPostHeader extends PostHeader {
 	public long getTimeReceived() {
 		return timeReceived;
 	}
+
+	public boolean isRssFeed() {
+		return rssFeed;
+	}
+
 }
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java
index b36a8a70b113a395aa58f213e3214b840c194a7c..f2fcaa38723832f478dfccc0f57f28698876f909 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/Feed.java
@@ -1,40 +1,31 @@
 package org.briarproject.briar.api.feed;
 
-import org.briarproject.bramble.api.FormatException;
-import org.briarproject.bramble.api.data.BdfDictionary;
-import org.briarproject.bramble.api.data.BdfEntry;
+import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.GroupId;
+import org.briarproject.briar.api.blog.Blog;
 
 import javax.annotation.Nullable;
 import javax.annotation.concurrent.Immutable;
 
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_GROUP_ID;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED;
-import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL;
-
 @Immutable
 @NotNullByDefault
 public class Feed {
 
 	private final String url;
-	private final GroupId blogId;
+	private final Blog blog;
+	private final LocalAuthor localAuthor;
 	@Nullable
-	private final String title, description, author;
+	private final String description, author;
 	private final long added, updated, lastEntryTime;
 
-	public Feed(String url, GroupId blogId, @Nullable String title,
-			@Nullable String description, @Nullable String author,
-			long added, long updated, long lastEntryTime) {
+	public Feed(String url, Blog blog, LocalAuthor localAuthor,
+			@Nullable String description, @Nullable String author, long added,
+			long updated, long lastEntryTime) {
 
 		this.url = url;
-		this.blogId = blogId;
-		this.title = title;
+		this.blog = blog;
+		this.localAuthor = localAuthor;
 		this.description = description;
 		this.author = author;
 		this.added = added;
@@ -42,13 +33,13 @@ public class Feed {
 		this.lastEntryTime = lastEntryTime;
 	}
 
-	public Feed(String url, GroupId blogId, @Nullable String title,
+	public Feed(String url, Blog blog, LocalAuthor localAuthor,
 			@Nullable String description, @Nullable String author, long added) {
-		this(url, blogId, title, description, author, added, 0L, 0L);
+		this(url, blog, localAuthor, description, author, added, 0L, 0L);
 	}
 
-	public Feed(String url, GroupId blogId, long added) {
-		this(url, blogId, null, null, null, added, 0L, 0L);
+	public Feed(String url, Blog blog, LocalAuthor localAuthor, long added) {
+		this(url, blog, localAuthor, null, null, added, 0L, 0L);
 	}
 
 	public String getUrl() {
@@ -56,39 +47,19 @@ public class Feed {
 	}
 
 	public GroupId getBlogId() {
-		return blogId;
+		return blog.getId();
 	}
 
-	public BdfDictionary toBdfDictionary() {
-		BdfDictionary d = BdfDictionary.of(
-				new BdfEntry(KEY_FEED_URL, url),
-				new BdfEntry(KEY_BLOG_GROUP_ID, blogId.getBytes()),
-				new BdfEntry(KEY_FEED_ADDED, added),
-				new BdfEntry(KEY_FEED_UPDATED, updated),
-				new BdfEntry(KEY_FEED_LAST_ENTRY, lastEntryTime)
-		);
-		if (title != null) d.put(KEY_FEED_TITLE, title);
-		if (description != null) d.put(KEY_FEED_DESC, description);
-		if (author != null) d.put(KEY_FEED_AUTHOR, author);
-		return d;
+	public Blog getBlog() {
+		return blog;
 	}
 
-	public static Feed from(BdfDictionary d) throws FormatException {
-		String url = d.getString(KEY_FEED_URL);
-		GroupId blogId = new GroupId(d.getRaw(KEY_BLOG_GROUP_ID));
-		String title = d.getOptionalString(KEY_FEED_TITLE);
-		String desc = d.getOptionalString(KEY_FEED_DESC);
-		String author = d.getOptionalString(KEY_FEED_AUTHOR);
-		long added = d.getLong(KEY_FEED_ADDED, 0L);
-		long updated = d.getLong(KEY_FEED_UPDATED, 0L);
-		long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L);
-		return new Feed(url, blogId, title, desc, author, added, updated,
-				lastEntryTime);
+	public LocalAuthor getLocalAuthor() {
+		return localAuthor;
 	}
 
-	@Nullable
 	public String getTitle() {
-		return title;
+		return blog.getName();
 	}
 
 	@Nullable
@@ -118,20 +89,9 @@ public class Feed {
 		if (this == o) return true;
 		if (o instanceof Feed) {
 			Feed f = (Feed) o;
-			return url.equals(f.url) && blogId.equals(f.getBlogId()) &&
-					equalsWithNull(title, f.getTitle()) &&
-					equalsWithNull(description, f.getDescription()) &&
-					equalsWithNull(author, f.getAuthor()) &&
-					added == f.getAdded() &&
-					updated == f.getUpdated() &&
-					lastEntryTime == f.getLastEntryTime();
+			return blog.equals(f.blog);
 		}
 		return false;
 	}
 
-	private boolean equalsWithNull(@Nullable Object a, @Nullable Object b) {
-		if (a == b) return true;
-		if (a == null || b == null) return false;
-		return a.equals(b);
-	}
 }
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java
index 8be05d5681fb7b1db2c26d71a484b4d5d69f2515..0fddb3aa73ed7ff3124f3984c1d2c82eec2f6387 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedConstants.java
@@ -18,8 +18,9 @@ public interface FeedConstants {
 	// group metadata keys
 	String KEY_FEEDS = "feeds";
 	String KEY_FEED_URL = "feedURL";
-	String KEY_BLOG_GROUP_ID = "blogGroupId";
-	String KEY_FEED_TITLE = "feedTitle";
+	String KEY_BLOG_TITLE = "blogTitle";
+	String KEY_PUBLIC_KEY = "publicKey";
+	String KEY_PRIVATE_KEY = "privateKey";
 	String KEY_FEED_DESC = "feedDesc";
 	String KEY_FEED_AUTHOR = "feedAuthor";
 	String KEY_FEED_ADDED = "feedAdded";
diff --git a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java
index 5d31b448a04295a369317b3601af005132dce707..7d83b22c151d70e37e69e7d04272001464b10a3b 100644
--- a/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java
+++ b/briar-api/src/main/java/org/briarproject/briar/api/feed/FeedManager.java
@@ -3,7 +3,6 @@ package org.briarproject.briar.api.feed;
 import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.api.sync.ClientId;
-import org.briarproject.bramble.api.sync.GroupId;
 
 import java.io.IOException;
 import java.util.List;
@@ -17,14 +16,14 @@ public interface FeedManager {
 	ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
 
 	/**
-	 * Adds an RSS feed.
+	 * Adds an RSS feed as a new dedicated blog.
 	 */
-	void addFeed(String url, GroupId g) throws DbException, IOException;
+	void addFeed(String url) throws DbException, IOException;
 
 	/**
 	 * Removes an RSS feed.
 	 */
-	void removeFeed(String url) throws DbException;
+	void removeFeed(Feed feed) throws DbException;
 
 	/**
 	 * Returns a list of all added RSS feeds
diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
index a9a7bfc506e344f4e400919327e29083d4e96c53..99d5879aa26e4f0657cce5f62a5221b252d09817 100644
--- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
+++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java
@@ -2,6 +2,7 @@ package org.briarproject.briar;
 
 import org.briarproject.briar.blog.BlogModule;
 import org.briarproject.briar.client.BriarClientModule;
+import org.briarproject.briar.feed.DnsModule;
 import org.briarproject.briar.feed.FeedModule;
 import org.briarproject.briar.forum.ForumModule;
 import org.briarproject.briar.introduction.IntroductionModule;
@@ -16,6 +17,7 @@ import dagger.Module;
 		BlogModule.class,
 		BriarClientModule.class,
 		FeedModule.class,
+		DnsModule.class,
 		ForumModule.class,
 		GroupInvitationModule.class,
 		IntroductionModule.class,
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java
index 26c1eb108c0f9e75301e3e2a8f854adb6bc91660..c7d3a2001ab1a99cb8790ba700c8f3f59ef33f7e 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogFactoryImpl.java
@@ -14,6 +14,9 @@ import org.briarproject.briar.api.blog.BlogFactory;
 import javax.annotation.concurrent.Immutable;
 import javax.inject.Inject;
 
+import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
+import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
+
 @Immutable
 @NotNullByDefault
 class BlogFactoryImpl implements BlogFactory {
@@ -33,28 +36,46 @@ class BlogFactoryImpl implements BlogFactory {
 
 	@Override
 	public Blog createBlog(Author a) {
+		return createBlog(a, false);
+	}
+
+	@Override
+	public Blog createFeedBlog(Author a) {
+		return createBlog(a, true);
+	}
+
+	private Blog createBlog(Author a, boolean rssFeed) {
 		try {
 			BdfList blog = BdfList.of(
 					a.getName(),
-					a.getPublicKey()
+					a.getPublicKey(),
+					rssFeed
 			);
 			byte[] descriptor = clientHelper.toByteArray(blog);
 			Group g = groupFactory
 					.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
-			return new Blog(g, a);
+			return new Blog(g, a, rssFeed);
 		} catch (FormatException e) {
 			throw new RuntimeException(e);
 		}
 	}
 
 	@Override
-	public Blog parseBlog(Group g) throws FormatException {
-		byte[] descriptor = g.getDescriptor();
+	public Blog parseBlog(Group group) throws FormatException {
+		byte[] descriptor = group.getDescriptor();
 		// Author Name, Public Key
 		BdfList blog = clientHelper.toList(descriptor);
-		Author a =
-				authorFactory.createAuthor(blog.getString(0), blog.getRaw(1));
-		return new Blog(g, a);
+		String name = blog.getString(0);
+		if (name.length() > MAX_AUTHOR_NAME_LENGTH)
+			throw new IllegalArgumentException();
+		byte[] publicKey = blog.getRaw(1);
+		if (publicKey.length > MAX_PUBLIC_KEY_LENGTH)
+			throw new IllegalArgumentException();
+
+		Author author =
+				authorFactory.createAuthor(name, publicKey);
+		boolean rssFeed = blog.getBoolean(2);
+		return new Blog(group, author, rssFeed);
 	}
 
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
index 496b308fb59513dfc948b0a5d21d7c02d5b98196..39ccb5241087c915669c032226f3e63d7de9c770 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogManagerImpl.java
@@ -61,6 +61,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ;
+import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
@@ -224,6 +225,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		}
 	}
 
+	@Override
+	public void removeBlog(Transaction txn, Blog b) throws DbException {
+		removeBlog(txn, b, false);
+	}
+
 	private void removeBlog(Transaction txn, Blog b, boolean forced)
 			throws DbException {
 		if (!forced && !canBeRemoved(txn, b.getId()))
@@ -248,15 +254,18 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 	@Override
 	public void addLocalPost(Transaction txn, BlogPost p) throws DbException {
 		try {
+			GroupId groupId = p.getMessage().getGroupId();
+			Blog b = getBlog(txn, groupId);
+
 			BdfDictionary meta = new BdfDictionary();
 			meta.put(KEY_TYPE, POST.getInt());
 			meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
 			meta.put(KEY_AUTHOR, authorToBdfDictionary(p.getAuthor()));
 			meta.put(KEY_READ, true);
+			meta.put(KEY_RSS_FEED, b.isRssFeed());
 			clientHelper.addLocalMessage(txn, p.getMessage(), meta, true);
 
 			// broadcast event about new post
-			GroupId groupId = p.getMessage().getGroupId();
 			MessageId postId = p.getMessage().getId();
 			BlogPostHeader h =
 					getPostHeaderFromMetadata(txn, groupId, postId, meta);
@@ -345,6 +354,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 			wMessage = blogPostFactory
 					.wrapPost(groupId, wDescriptor, wTimestamp, body);
 			meta.put(KEY_TYPE, WRAPPED_POST.getInt());
+			meta.put(KEY_RSS_FEED, pOriginalHeader.isRssFeed());
 		} else if (type == COMMENT) {
 			Group wGroup = db.getGroup(txn, pOriginalHeader.getGroupId());
 			byte[] wDescriptor = wGroup.getDescriptor();
@@ -593,8 +603,11 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 		String name = d.getString(KEY_AUTHOR_NAME);
 		byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
 		Author author = new Author(authorId, name, publicKey);
+		boolean isFeedPost = meta.getBoolean(KEY_RSS_FEED, false);
 		Status authorStatus;
-		if (authorStatuses.containsKey(authorId)) {
+		if (isFeedPost) {
+			authorStatus = Status.NONE;
+		} else if (authorStatuses.containsKey(authorId)) {
 			authorStatus = authorStatuses.get(authorId);
 		} else {
 			authorStatus = identityManager.getAuthorStatus(txn, authorId);
@@ -611,7 +624,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 					timestamp, timeReceived, author, authorStatus, read);
 		} else {
 			return new BlogPostHeader(type, groupId, id, timestamp,
-					timeReceived, author, authorStatus, read);
+					timeReceived, author, authorStatus, isFeedPost, read);
 		}
 	}
 
diff --git a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
index 33fb0bddf76a4736df6bff337c7892f65520afca..5e74400c52ade433c2a175789d3e50648be6fbe7 100644
--- a/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
+++ b/briar-core/src/main/java/org/briarproject/briar/blog/BlogPostValidator.java
@@ -39,6 +39,7 @@ import static org.briarproject.briar.api.blog.BlogConstants.KEY_ORIGINAL_PARENT_
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PARENT_MSG_ID;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_READ;
+import static org.briarproject.briar.api.blog.BlogConstants.KEY_RSS_FEED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIMESTAMP;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.briar.api.blog.BlogConstants.KEY_TYPE;
@@ -123,6 +124,7 @@ class BlogPostValidator extends BdfMessageValidator {
 		BdfDictionary meta = new BdfDictionary();
 		meta.put(KEY_ORIGINAL_MSG_ID, m.getId());
 		meta.put(KEY_AUTHOR, authorToBdfDictionary(a));
+		meta.put(KEY_RSS_FEED, b.isRssFeed());
 		return new BdfMessageContext(meta);
 	}
 
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..dc6cd8a885d0b9740963b2647288c579a80c6ff3
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/DnsModule.java
@@ -0,0 +1,18 @@
+package org.briarproject.briar.feed;
+
+import dagger.Module;
+import dagger.Provides;
+import okhttp3.Dns;
+
+/**
+ * This is a dedicated module, so it can be replaced for testing.
+ */
+@Module
+public class DnsModule {
+
+	@Provides
+	Dns provideDns(NoDns noDns) {
+		return noDns;
+	}
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..192c4855eb47eccc8c21ae0275a4f94ca27cc6ea
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactory.java
@@ -0,0 +1,34 @@
+package org.briarproject.briar.feed;
+
+import com.rometools.rome.feed.synd.SyndFeed;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.briar.api.feed.Feed;
+
+interface FeedFactory {
+
+	/**
+	 * Create a new feed based on the feed url
+	 * and the metadata of an existing {@link SyndFeed}.
+	 */
+	Feed createFeed(String url, SyndFeed feed);
+
+	/**
+	 * Creates a new updated feed, based on the given existing feed,
+	 * new metadata from the given {@link SyndFeed}
+	 * and the time of the last feed entry.
+	 */
+	Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime);
+
+	/**
+	 * De-serializes a {@link BdfDictionary} into a {@link Feed}.
+	 */
+	Feed createFeed(BdfDictionary d) throws FormatException;
+
+	/**
+	 * Serializes a {@link Feed} into a {@link BdfDictionary}.
+	 */
+	BdfDictionary feedToBdfDictionary(Feed feed);
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..ec5d9c31b522cad7e335db12c4c373f984784afa
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedFactoryImpl.java
@@ -0,0 +1,112 @@
+package org.briarproject.briar.feed;
+
+import com.rometools.rome.feed.synd.SyndFeed;
+
+import org.briarproject.bramble.api.FormatException;
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.crypto.KeyPair;
+import org.briarproject.bramble.api.data.BdfDictionary;
+import org.briarproject.bramble.api.data.BdfEntry;
+import org.briarproject.bramble.api.identity.AuthorFactory;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.system.Clock;
+import org.briarproject.bramble.util.StringUtils;
+import org.briarproject.briar.api.blog.Blog;
+import org.briarproject.briar.api.blog.BlogFactory;
+import org.briarproject.briar.api.feed.Feed;
+
+import javax.inject.Inject;
+
+import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_TITLE;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_PRIVATE_KEY;
+import static org.briarproject.briar.api.feed.FeedConstants.KEY_PUBLIC_KEY;
+
+class FeedFactoryImpl implements FeedFactory {
+
+	private final CryptoComponent cryptoComponent;
+	private final AuthorFactory authorFactory;
+	private final BlogFactory blogFactory;
+	private final Clock clock;
+
+	@Inject
+	FeedFactoryImpl(CryptoComponent cryptoComponent,
+			AuthorFactory authorFactory, BlogFactory blogFactory, Clock clock) {
+		this.cryptoComponent = cryptoComponent;
+		this.authorFactory = authorFactory;
+		this.blogFactory = blogFactory;
+		this.clock = clock;
+	}
+
+	@Override
+	public Feed createFeed(String url, SyndFeed syndFeed) {
+		String title = syndFeed.getTitle();
+		if (title == null) title = "RSS";
+		title = StringUtils.truncateUtf8(title, MAX_AUTHOR_NAME_LENGTH);
+
+		KeyPair keyPair = cryptoComponent.generateSignatureKeyPair();
+		LocalAuthor localAuthor = authorFactory
+				.createLocalAuthor(title,
+						keyPair.getPublic().getEncoded(),
+						keyPair.getPrivate().getEncoded());
+		Blog blog = blogFactory.createFeedBlog(localAuthor);
+		long added = clock.currentTimeMillis();
+
+		return new Feed(url, blog, localAuthor, added);
+	}
+
+	@Override
+	public Feed createFeed(Feed feed, SyndFeed f, long lastEntryTime) {
+		long updated = clock.currentTimeMillis();
+		return new Feed(feed.getUrl(), feed.getBlog(), feed.getLocalAuthor(),
+				f.getDescription(), f.getAuthor(), feed.getAdded(), updated,
+				lastEntryTime);
+	}
+
+	@Override
+	public Feed createFeed(BdfDictionary d) throws FormatException {
+		String url = d.getString(KEY_FEED_URL);
+
+		String blogTitle = d.getString(KEY_BLOG_TITLE);
+		byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
+		byte[] privateKey = d.getRaw(KEY_PRIVATE_KEY);
+		LocalAuthor localAuthor = authorFactory
+				.createLocalAuthor(blogTitle, publicKey, privateKey);
+		Blog blog = blogFactory.createFeedBlog(localAuthor);
+
+		String desc = d.getOptionalString(KEY_FEED_DESC);
+		String author = d.getOptionalString(KEY_FEED_AUTHOR);
+		long added = d.getLong(KEY_FEED_ADDED, 0L);
+		long updated = d.getLong(KEY_FEED_UPDATED, 0L);
+		long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L);
+
+		return new Feed(url, blog, localAuthor, desc, author, added,
+				updated, lastEntryTime);
+	}
+
+	@Override
+	public BdfDictionary feedToBdfDictionary(Feed feed) {
+		BdfDictionary d = BdfDictionary.of(
+				new BdfEntry(KEY_FEED_URL, feed.getUrl()),
+				new BdfEntry(KEY_BLOG_TITLE, feed.getLocalAuthor().getName()),
+				new BdfEntry(KEY_PUBLIC_KEY,
+						feed.getLocalAuthor().getPublicKey()),
+				new BdfEntry(KEY_PRIVATE_KEY,
+						feed.getLocalAuthor().getPrivateKey()),
+				new BdfEntry(KEY_FEED_ADDED, feed.getAdded()),
+				new BdfEntry(KEY_FEED_UPDATED, feed.getUpdated()),
+				new BdfEntry(KEY_FEED_LAST_ENTRY, feed.getLastEntryTime())
+		);
+		if (feed.getDescription() != null)
+			d.put(KEY_FEED_DESC, feed.getDescription());
+		if (feed.getAuthor() != null) d.put(KEY_FEED_AUTHOR, feed.getAuthor());
+		return d;
+	}
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
index d92df5001d023cdd18777d355fffb687dc2ff90b..bb97bb0d48cf41956e00a4d89a84267e6792e7de 100644
--- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedManagerImpl.java
@@ -18,7 +18,6 @@ import org.briarproject.bramble.api.db.DbException;
 import org.briarproject.bramble.api.db.Transaction;
 import org.briarproject.bramble.api.event.Event;
 import org.briarproject.bramble.api.event.EventListener;
-import org.briarproject.bramble.api.identity.IdentityManager;
 import org.briarproject.bramble.api.identity.LocalAuthor;
 import org.briarproject.bramble.api.lifecycle.IoExecutor;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -31,6 +30,7 @@ import org.briarproject.bramble.api.sync.GroupId;
 import org.briarproject.bramble.api.system.Clock;
 import org.briarproject.bramble.api.system.Scheduler;
 import org.briarproject.bramble.util.StringUtils;
+import org.briarproject.briar.api.blog.Blog;
 import org.briarproject.briar.api.blog.BlogManager;
 import org.briarproject.briar.api.blog.BlogPost;
 import org.briarproject.briar.api.blog.BlogPostFactory;
@@ -39,8 +39,6 @@ import org.briarproject.briar.api.feed.FeedManager;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -75,12 +73,12 @@ import static org.briarproject.briar.util.HtmlUtils.clean;
 
 @ThreadSafe
 @NotNullByDefault
-class FeedManagerImpl implements FeedManager, Client, EventListener {
+class FeedManagerImpl implements FeedManager, Client, EventListener,
+		BlogManager.RemoveBlogHook {
 
 	private static final Logger LOG =
 			Logger.getLogger(FeedManagerImpl.class.getName());
 
-	private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
 	private static final int CONNECT_TIMEOUT = 60 * 1000; // Milliseconds
 
 	private final ScheduledExecutorService scheduler;
@@ -88,31 +86,33 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 	private final DatabaseComponent db;
 	private final ContactGroupFactory contactGroupFactory;
 	private final ClientHelper clientHelper;
-	private final IdentityManager identityManager;
 	private final BlogManager blogManager;
 	private final BlogPostFactory blogPostFactory;
+	private final FeedFactory feedFactory;
 	private final SocketFactory torSocketFactory;
 	private final Clock clock;
+	private final Dns noDnsLookups;
 	private final AtomicBoolean fetcherStarted = new AtomicBoolean(false);
 
 	@Inject
 	FeedManagerImpl(@Scheduler ScheduledExecutorService scheduler,
 			@IoExecutor Executor ioExecutor, DatabaseComponent db,
 			ContactGroupFactory contactGroupFactory, ClientHelper clientHelper,
-			IdentityManager identityManager, BlogManager blogManager,
-			BlogPostFactory blogPostFactory, SocketFactory torSocketFactory,
-			Clock clock) {
+			BlogManager blogManager, BlogPostFactory blogPostFactory,
+			FeedFactory feedFactory, SocketFactory torSocketFactory,
+			Clock clock, Dns noDnsLookups) {
 
 		this.scheduler = scheduler;
 		this.ioExecutor = ioExecutor;
 		this.db = db;
 		this.contactGroupFactory = contactGroupFactory;
 		this.clientHelper = clientHelper;
-		this.identityManager = identityManager;
 		this.blogManager = blogManager;
 		this.blogPostFactory = blogPostFactory;
+		this.feedFactory = feedFactory;
 		this.torSocketFactory = torSocketFactory;
 		this.clock = clock;
+		this.noDnsLookups = noDnsLookups;
 	}
 
 	@Override
@@ -158,21 +158,21 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 	}
 
 	@Override
-	public void addFeed(String url, GroupId g) throws DbException, IOException {
-		LOG.info("Adding new RSS feed...");
-
-		// TODO check for existing feed?
-		// fetch feed to get its metadata
-		Feed feed = new Feed(url, g, clock.currentTimeMillis());
+	public void addFeed(String url) throws DbException, IOException {
+		// fetch syndication feed to get its metadata
+		SyndFeed f;
 		try {
-			feed = fetchFeed(feed, false);
+			f = fetchSyndFeed(url);
 		} catch (FeedException e) {
 			throw new IOException(e);
 		}
 
-		// store feed
+		Feed feed = feedFactory.createFeed(url, f);
+
+		// store feed and new blog
 		Transaction txn = db.startTransaction(false);
 		try {
+			blogManager.addBlog(txn, feed.getBlog());
 			List<Feed> feeds = getFeeds(txn);
 			feeds.add(feed);
 			storeFeeds(txn, feeds);
@@ -181,10 +181,10 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 			db.endTransaction(txn);
 		}
 
-		// fetch feed again, post entries this time
+		// fetch feed again and post entries
 		Feed updatedFeed;
 		try {
-			updatedFeed = fetchFeed(feed, true);
+			updatedFeed = fetchFeed(feed);
 		} catch (FeedException e) {
 			throw new IOException(e);
 		}
@@ -203,27 +203,35 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 	}
 
 	@Override
-	public void removeFeed(String url) throws DbException {
+	public void removeFeed(Feed feed) throws DbException {
 		LOG.info("Removing RSS feed...");
 		Transaction txn = db.startTransaction(false);
 		try {
-			List<Feed> feeds = getFeeds(txn);
-			boolean found = false;
-			for (Feed feed : feeds) {
-				if (feed.getUrl().equals(url)) {
-					found = true;
-					feeds.remove(feed);
-					break;
-				}
-			}
-			if (!found) throw new DbException();
-			storeFeeds(txn, feeds);
+			// this will call removingBlog() where the feed itself gets removed
+			blogManager.removeBlog(txn, feed.getBlog());
 			db.commitTransaction(txn);
 		} finally {
 			db.endTransaction(txn);
 		}
 	}
 
+	@Override
+	public void removingBlog(Transaction txn, Blog b) throws DbException {
+		if (!b.isRssFeed()) return;
+
+		// delete blog's RSS feed if we have it
+		boolean found = false;
+		List<Feed> feeds = getFeeds(txn);
+		for (Feed f : feeds) {
+			if (f.getBlogId().equals(b.getId())) {
+				found = true;
+				feeds.remove(f);
+				break;
+			}
+		}
+		if (found) storeFeeds(txn, feeds);
+	}
+
 	@Override
 	public List<Feed> getFeeds() throws DbException {
 		List<Feed> feeds;
@@ -246,7 +254,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 			for (Object object : d.getList(KEY_FEEDS)) {
 				if (!(object instanceof BdfDictionary))
 					throw new FormatException();
-				feeds.add(Feed.from((BdfDictionary) object));
+				feeds.add(feedFactory.createFeed((BdfDictionary) object));
 			}
 		} catch (FormatException e) {
 			throw new DbException(e);
@@ -259,7 +267,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 
 		BdfList feedList = new BdfList();
 		for (Feed feed : feeds) {
-			feedList.add(feed.toBdfDictionary());
+			feedList.add(feedFactory.feedToBdfDictionary(feed));
 		}
 		BdfDictionary gm = BdfDictionary.of(new BdfEntry(KEY_FEEDS, feedList));
 		try {
@@ -300,7 +308,7 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 		List<Feed> newFeeds = new ArrayList<Feed>(feeds.size());
 		for (Feed feed : feeds) {
 			try {
-				newFeeds.add(fetchFeed(feed, true));
+				newFeeds.add(fetchFeed(feed));
 			} catch (FeedException e) {
 				if (LOG.isLoggable(WARNING))
 					LOG.log(WARNING, e.toString(), e);
@@ -323,49 +331,52 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 		LOG.info("Done updating RSS feeds");
 	}
 
-	private Feed fetchFeed(Feed feed, boolean post)
-			throws FeedException, IOException, DbException {
-		String title, description, author;
-		long updated = clock.currentTimeMillis();
-		long lastEntryTime = feed.getLastEntryTime();
+	private SyndFeed fetchSyndFeed(String url)
+			throws FeedException, IOException {
+		// fetch feed
+		SyndFeed f = getSyndFeed(getFeedInputStream(url));
+
+		if (f.getEntries().size() == 0)
+			throw new FeedException("Feed has no entries");
 
-		SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl()));
-		title = StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle();
+		// clean title
+		String title =
+				StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle();
 		if (title != null) title = clean(title, STRIP_ALL);
-		description = StringUtils.isNullOrEmpty(f.getDescription()) ? null :
-				f.getDescription();
+		f.setTitle(title);
+
+		// clean description
+		String description =
+				StringUtils.isNullOrEmpty(f.getDescription()) ? null :
+						f.getDescription();
 		if (description != null) description = clean(description, STRIP_ALL);
-		author =
+		f.setDescription(description);
+
+		// clean author
+		String author =
 				StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor();
 		if (author != null) author = clean(author, STRIP_ALL);
+		f.setAuthor(author);
 
-		if (f.getEntries().size() == 0)
-			throw new FeedException("Feed has no entries");
+		return f;
+	}
+
+	private Feed fetchFeed(Feed feed)
+			throws FeedException, IOException, DbException {
+		// fetch and clean feed
+		SyndFeed f = fetchSyndFeed(feed.getUrl());
 
 		// sort and add new entries
-		if (post) {
-			lastEntryTime = postFeedEntries(feed, f.getEntries());
-		}
-		return new Feed(feed.getUrl(), feed.getBlogId(), title, description,
-				author, feed.getAdded(), updated, lastEntryTime);
+		long lastEntryTime = postFeedEntries(feed, f.getEntries());
+
+		return feedFactory.createFeed(feed, f, lastEntryTime);
 	}
 
 	private InputStream getFeedInputStream(String url) throws IOException {
-		// Don't make local DNS lookups
-		Dns noLookups = new Dns() {
-			@Override
-			public List<InetAddress> lookup(String hostname)
-					throws UnknownHostException {
-				InetAddress unspecified =
-						InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS);
-				return Collections.singletonList(unspecified);
-			}
-		};
-
 		// Build HTTP Client
 		OkHttpClient client = new OkHttpClient.Builder()
 				.socketFactory(torSocketFactory)
-				.dns(noLookups)
+				.dns(noDnsLookups) // Don't make local DNS lookups
 				.connectTimeout(CONNECT_TIMEOUT, MILLISECONDS)
 				.build();
 
@@ -422,9 +433,8 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 
 		// build post body
 		StringBuilder b = new StringBuilder();
-		if (feed.getTitle() != null) {
-			b.append("<h3>").append(feed.getTitle()).append("</h3>");
-		}
+		b.append("<h3>").append(feed.getTitle()).append("</h3>");
+
 		if (!StringUtils.isNullOrEmpty(entry.getTitle())) {
 			b.append("<h1>").append(entry.getTitle()).append("</h1>");
 		}
@@ -461,9 +471,9 @@ class FeedManagerImpl implements FeedManager, Client, EventListener {
 		String body = getPostBody(b.toString());
 		try {
 			// create and store post
-			LocalAuthor author = identityManager.getLocalAuthor(txn);
+			LocalAuthor localAuthor = feed.getLocalAuthor();
 			BlogPost post = blogPostFactory
-					.createBlogPost(groupId, time, null, author, body);
+					.createBlogPost(groupId, time, null, localAuthor, body);
 			blogManager.addLocalPost(txn, post);
 		} catch (DbException e) {
 			if (LOG.isLoggable(WARNING))
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java
index d47a088b88000a74ee1974c7ab22c6d3e486c472..c4b567c189977888c2e2927fc956e9cb2c40f079 100644
--- a/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/FeedModule.java
@@ -2,6 +2,7 @@ package org.briarproject.briar.feed;
 
 import org.briarproject.bramble.api.event.EventBus;
 import org.briarproject.bramble.api.lifecycle.LifecycleManager;
+import org.briarproject.briar.api.blog.BlogManager;
 import org.briarproject.briar.api.feed.FeedManager;
 
 import javax.inject.Inject;
@@ -21,11 +22,18 @@ public class FeedModule {
 	@Provides
 	@Singleton
 	FeedManager provideFeedManager(FeedManagerImpl feedManager,
-			LifecycleManager lifecycleManager, EventBus eventBus) {
+			LifecycleManager lifecycleManager, EventBus eventBus,
+			BlogManager blogManager) {
 
 		lifecycleManager.registerClient(feedManager);
 		eventBus.addListener(feedManager);
+		blogManager.registerRemoveBlogHook(feedManager);
 		return feedManager;
 	}
 
+	@Provides
+	FeedFactory provideFeedFactory(FeedFactoryImpl feedFactory) {
+		return feedFactory;
+	}
+
 }
diff --git a/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f5cc8c44b3650a7c169fd41ea04e1f242124631
--- /dev/null
+++ b/briar-core/src/main/java/org/briarproject/briar/feed/NoDns.java
@@ -0,0 +1,28 @@
+package org.briarproject.briar.feed;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import okhttp3.Dns;
+
+class NoDns implements Dns {
+
+	private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
+
+	@Inject
+	public NoDns() {
+	}
+
+	@Override
+	public List<InetAddress> lookup(String hostname)
+			throws UnknownHostException {
+		InetAddress unspecified =
+				InetAddress.getByAddress(hostname, UNSPECIFIED_ADDRESS);
+		return Collections.singletonList(unspecified);
+	}
+
+}
diff --git a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java
index 6f0df8e473b3db7b1e1361850adb2cff997523a8..f9b378ef7878628b4c29f323b54437e393d47f8d 100644
--- a/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java
+++ b/briar-core/src/main/java/org/briarproject/briar/sharing/BlogSharingValidator.java
@@ -39,14 +39,20 @@ class BlogSharingValidator extends SharingValidator {
 	@Override
 	protected GroupId validateDescriptor(BdfList descriptor)
 			throws FormatException {
-		checkSize(descriptor, 2);
+		checkSize(descriptor, 3);
 		String name = descriptor.getString(0);
 		checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
 		byte[] publicKey = descriptor.getRaw(1);
 		checkLength(publicKey, 1, MAX_PUBLIC_KEY_LENGTH);
+		boolean rssFeed = descriptor.getBoolean(2);
 
 		Author author = authorFactory.createAuthor(name, publicKey);
-		Blog blog = blogFactory.createBlog(author);
+		Blog blog;
+		if (rssFeed) {
+			blog = blogFactory.createFeedBlog(author);
+		} else {
+			blog = blogFactory.createBlog(author);
+		}
 		return blog.getId();
 	}
 
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
index e2b5e8ef5206d162cc029c3cd945362f894eaa96..4ff1bb75c4826d35c093fc7a8815f9acaa32975f 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
@@ -297,7 +297,7 @@ public class BlogManagerImplTest extends BriarTestCase {
 		final LocalAuthor localAuthor =
 				new LocalAuthor(authorId, "Author", publicKey, privateKey,
 						created);
-		return new Blog(group, localAuthor);
+		return new Blog(group, localAuthor, false);
 	}
 
 	private BdfDictionary authorToBdfDictionary(Author a) {
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
index 643fe482c3b939047029731aecce9773324221c7..165c1fab52d764f43b4cd2cd8858b0ac082c2400 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerIntegrationTest.java
@@ -1,5 +1,7 @@
 package org.briarproject.briar.blog;
 
+import org.briarproject.bramble.api.db.Transaction;
+import org.briarproject.bramble.api.identity.Author;
 import org.briarproject.bramble.api.sync.MessageId;
 import org.briarproject.bramble.test.TestDatabaseModule;
 import org.briarproject.briar.api.blog.Blog;
@@ -32,7 +34,7 @@ public class BlogManagerIntegrationTest
 		extends BriarIntegrationTest<BriarIntegrationTestComponent> {
 
 	private BlogManager blogManager0, blogManager1;
-	private Blog blog0, blog1;
+	private Blog blog0, blog1, rssBlog;
 
 	@Rule
 	public ExpectedException thrown = ExpectedException.none();
@@ -50,6 +52,12 @@ public class BlogManagerIntegrationTest
 
 		blog0 = blogFactory.createBlog(author0);
 		blog1 = blogFactory.createBlog(author1);
+
+		rssBlog = blogFactory.createFeedBlog(author0);
+		Transaction txn = db0.startTransaction(false);
+		blogManager0.addBlog(txn, rssBlog);
+		db0.commitTransaction(txn);
+		db0.endTransaction(txn);
 	}
 
 	@Override
@@ -393,4 +401,63 @@ public class BlogManagerIntegrationTest
 		assertEquals(2, headers0.size());
 	}
 
+	@Test
+	public void testFeedPost() throws Exception {
+		assertTrue(rssBlog.isRssFeed());
+
+		// add a feed post to rssBlog
+		final String body = getRandomString(42);
+		BlogPost p = blogPostFactory
+				.createBlogPost(rssBlog.getId(), clock.currentTimeMillis(),
+						null, author0, body);
+		blogManager0.addLocalPost(p);
+
+		// make sure it got saved as an RSS feed post
+		Collection<BlogPostHeader> headers =
+				blogManager0.getPostHeaders(rssBlog.getId());
+		assertEquals(1, headers.size());
+		BlogPostHeader header = headers.iterator().next();
+		assertEquals(POST, header.getType());
+		assertEquals(Author.Status.NONE, header.getAuthorStatus());
+		assertTrue(header.isRssFeed());
+	}
+
+	@Test
+	public void testFeedReblog() throws Exception {
+		// add a feed post to rssBlog
+		final String body = getRandomString(42);
+		BlogPost p = blogPostFactory
+				.createBlogPost(rssBlog.getId(), clock.currentTimeMillis(),
+						null, author0, body);
+		blogManager0.addLocalPost(p);
+
+		// reblog feed post to own blog
+		Collection<BlogPostHeader> headers =
+				blogManager0.getPostHeaders(rssBlog.getId());
+		assertEquals(1, headers.size());
+		BlogPostHeader header = headers.iterator().next();
+		blogManager0.addLocalComment(author0, blog0.getId(), null, header);
+
+		// make sure it got saved as an RSS feed post
+		headers = blogManager0.getPostHeaders(blog0.getId());
+		assertEquals(1, headers.size());
+		BlogCommentHeader commentHeader =
+				(BlogCommentHeader) headers.iterator().next();
+		assertEquals(COMMENT, commentHeader.getType());
+		assertTrue(commentHeader.getParent().isRssFeed());
+
+		// reblog reblogged post again to own blog
+		blogManager0
+				.addLocalComment(author0, blog0.getId(), null, commentHeader);
+
+		// make sure it got saved as an RSS feed post
+		headers = blogManager0.getPostHeaders(blog0.getId());
+		assertEquals(2, headers.size());
+		for (BlogPostHeader h: headers) {
+			assertTrue(h instanceof BlogCommentHeader);
+			assertEquals(COMMENT, h.getType());
+			assertTrue(((BlogCommentHeader) h).getRootPost().isRssFeed());
+		}
+	}
+
 }
diff --git a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java
index 1ac77a550e8d3737eadd84e447e9981abb0d443f..d3731d19de9c9fcea112144ab37589e782081793 100644
--- a/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/blog/BlogPostValidatorTest.java
@@ -79,7 +79,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 				new BdfEntry(KEY_AUTHOR_NAME, author.getName()),
 				new BdfEntry(KEY_PUBLIC_KEY, author.getPublicKey())
 		);
-		blog = new Blog(group, author);
+		blog = new Blog(group, author, false);
 
 		MessageId messageId = new MessageId(TestUtils.getRandomId());
 		long timestamp = System.currentTimeMillis();
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2fc24aae58f3f082491e5ca6011ca7ca05d9100a
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
@@ -0,0 +1,127 @@
+package org.briarproject.briar.feed;
+
+import org.briarproject.bramble.api.lifecycle.LifecycleManager;
+import org.briarproject.bramble.contact.ContactModule;
+import org.briarproject.bramble.crypto.CryptoModule;
+import org.briarproject.bramble.identity.IdentityModule;
+import org.briarproject.bramble.lifecycle.LifecycleModule;
+import org.briarproject.bramble.sync.SyncModule;
+import org.briarproject.bramble.system.SystemModule;
+import org.briarproject.bramble.test.TestDatabaseModule;
+import org.briarproject.bramble.test.TestUtils;
+import org.briarproject.bramble.transport.TransportModule;
+import org.briarproject.briar.api.blog.Blog;
+import org.briarproject.briar.api.blog.BlogManager;
+import org.briarproject.briar.api.blog.BlogPostHeader;
+import org.briarproject.briar.api.feed.Feed;
+import org.briarproject.briar.api.feed.FeedManager;
+import org.briarproject.briar.blog.BlogModule;
+import org.briarproject.briar.test.BriarTestCase;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Collection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class FeedManagerIntegrationTest extends BriarTestCase {
+
+	private LifecycleManager lifecycleManager;
+	private FeedManager feedManager;
+	private BlogManager blogManager;
+	private final File testDir = TestUtils.getTestDirectory();
+	private final File testFile = new File(testDir, "feedTest");
+
+	@Before
+	public void setUp() throws Exception {
+		assertTrue(testDir.mkdirs());
+		FeedManagerIntegrationTestComponent component =
+				DaggerFeedManagerIntegrationTestComponent.builder()
+						.testDatabaseModule(new TestDatabaseModule(testFile))
+						.build();
+		component.inject(this);
+		injectEagerSingletons(component);
+
+		lifecycleManager = component.getLifecycleManager();
+		lifecycleManager.startServices("feedTest");
+		lifecycleManager.waitForStartup();
+
+		feedManager = component.getFeedManager();
+		blogManager = component.getBlogManager();
+	}
+
+	@Test
+	public void testFeedImportAndRemoval() throws Exception {
+		// initially, there's only the one personal blog
+		Collection<Blog> blogs = blogManager.getBlogs();
+		assertEquals(1, blogs.size());
+		Blog personalBlog = blogs.iterator().next();
+
+		// add feed into a dedicated blog
+		String url = "https://www.schneier.com/blog/atom.xml";
+		feedManager.addFeed(url);
+
+		// then there's the feed's blog now
+		blogs = blogManager.getBlogs();
+		assertEquals(2, blogs.size());
+		Blog feedBlog = null;
+		for (Blog blog : blogs) {
+			if (!blog.equals(personalBlog)) feedBlog = blog;
+		}
+		assertNotNull(feedBlog);
+
+		// check the feed got saved as expected
+		Collection<Feed> feeds = feedManager.getFeeds();
+		assertEquals(1, feeds.size());
+		Feed feed = feeds.iterator().next();
+		assertTrue(feed.getLastEntryTime() > 0);
+		assertTrue(feed.getAdded() > 0);
+		assertTrue(feed.getUpdated() > 0);
+		assertEquals(url, feed.getUrl());
+		assertEquals(feedBlog, feed.getBlog());
+		assertEquals("Schneier on Security", feed.getTitle());
+		assertEquals("A blog covering security and security technology.",
+				feed.getDescription());
+		assertEquals(feed.getTitle(), feed.getBlog().getName());
+		assertEquals(feed.getTitle(), feed.getLocalAuthor().getName());
+
+		// check the feed entries have been added to the blog as expected
+		Collection<BlogPostHeader> headers =
+				blogManager.getPostHeaders(feedBlog.getId());
+		for (BlogPostHeader header : headers) {
+			assertTrue(header.isRssFeed());
+		}
+
+		// now let's remove the feed's blog again
+		blogManager.removeBlog(feedBlog);
+		blogs = blogManager.getBlogs();
+		assertEquals(1, blogs.size());
+		assertEquals(personalBlog, blogs.iterator().next());
+		assertEquals(0, feedManager.getFeeds().size());
+	}
+
+	@After
+	public void tearDown() throws Exception {
+		lifecycleManager.stopServices();
+		lifecycleManager.waitForShutdown();
+		TestUtils.deleteTestDirectory(testDir);
+	}
+
+	protected void injectEagerSingletons(
+			FeedManagerIntegrationTestComponent component) {
+		component.inject(new FeedModule.EagerSingletons());
+		component.inject(new BlogModule.EagerSingletons());
+		component.inject(new ContactModule.EagerSingletons());
+		component.inject(new CryptoModule.EagerSingletons());
+		component.inject(new IdentityModule.EagerSingletons());
+		component.inject(new LifecycleModule.EagerSingletons());
+		component.inject(new SyncModule.EagerSingletons());
+		component.inject(new SystemModule.EagerSingletons());
+		component.inject(new TransportModule.EagerSingletons());
+	}
+
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce5ec5a00dfecbc9d2b5c8b6f0af92abb5d776e1
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
@@ -0,0 +1,79 @@
+package org.briarproject.briar.feed;
+
+import org.briarproject.bramble.api.lifecycle.LifecycleManager;
+import org.briarproject.bramble.client.ClientModule;
+import org.briarproject.bramble.contact.ContactModule;
+import org.briarproject.bramble.crypto.CryptoModule;
+import org.briarproject.bramble.data.DataModule;
+import org.briarproject.bramble.db.DatabaseModule;
+import org.briarproject.bramble.event.EventModule;
+import org.briarproject.bramble.identity.IdentityModule;
+import org.briarproject.bramble.lifecycle.LifecycleModule;
+import org.briarproject.bramble.sync.SyncModule;
+import org.briarproject.bramble.system.SystemModule;
+import org.briarproject.bramble.test.TestDatabaseModule;
+import org.briarproject.bramble.test.TestPluginConfigModule;
+import org.briarproject.bramble.test.TestSeedProviderModule;
+import org.briarproject.bramble.test.TestSocksModule;
+import org.briarproject.bramble.transport.TransportModule;
+import org.briarproject.briar.api.blog.BlogManager;
+import org.briarproject.briar.api.feed.FeedManager;
+import org.briarproject.briar.blog.BlogModule;
+import org.briarproject.briar.client.BriarClientModule;
+import org.briarproject.briar.test.TestDnsModule;
+
+import javax.inject.Singleton;
+
+import dagger.Component;
+
+@Singleton
+@Component(modules = {
+		TestDatabaseModule.class,
+		TestPluginConfigModule.class,
+		TestSeedProviderModule.class,
+		TestSocksModule.class,
+		TestDnsModule.class,
+		LifecycleModule.class,
+		BriarClientModule.class,
+		ClientModule.class,
+		ContactModule.class,
+		CryptoModule.class,
+		BlogModule.class,
+		FeedModule.class,
+		DataModule.class,
+		DatabaseModule.class,
+		EventModule.class,
+		IdentityModule.class,
+		SyncModule.class,
+		SystemModule.class,
+		TransportModule.class
+})
+interface FeedManagerIntegrationTestComponent {
+
+	void inject(FeedManagerIntegrationTest testCase);
+
+	void inject(FeedModule.EagerSingletons init);
+
+	void inject(BlogModule.EagerSingletons init);
+
+	void inject(ContactModule.EagerSingletons init);
+
+	void inject(CryptoModule.EagerSingletons init);
+
+	void inject(IdentityModule.EagerSingletons init);
+
+	void inject(LifecycleModule.EagerSingletons init);
+
+	void inject(SyncModule.EagerSingletons init);
+
+	void inject(SystemModule.EagerSingletons init);
+
+	void inject(TransportModule.EagerSingletons init);
+
+	LifecycleManager getLifecycleManager();
+
+	FeedManager getFeedManager();
+
+	BlogManager getBlogManager();
+
+}
diff --git a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java
index c67360d648b933c8a543e45ea640893f8621a9db..7c4c96ab267cb9f52f769abc2771221b911b75f1 100644
--- a/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/sharing/BlogSharingValidatorTest.java
@@ -23,8 +23,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 	private final byte[] publicKey =
 			TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
 	private final Author author = new Author(authorId, authorName, publicKey);
-	private final Blog blog = new Blog(group, author);
-	private final BdfList descriptor = BdfList.of(authorName, publicKey);
+	private final Blog blog = new Blog(group, author, false);
+	private final BdfList descriptor = BdfList.of(authorName, publicKey, false);
 	private final String content =
 			TestUtils.getRandomString(MAX_INVITATION_MESSAGE_LENGTH);
 
@@ -64,7 +64,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsNullBlogName() throws Exception {
-		BdfList invalidDescriptor = BdfList.of(null, publicKey);
+		BdfList invalidDescriptor = BdfList.of(null, publicKey, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -72,7 +72,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsNonStringBlogName() throws Exception {
-		BdfList invalidDescriptor = BdfList.of(123, publicKey);
+		BdfList invalidDescriptor = BdfList.of(123, publicKey, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -80,7 +80,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsTooShortBlogName() throws Exception {
-		BdfList invalidDescriptor = BdfList.of("", publicKey);
+		BdfList invalidDescriptor = BdfList.of("", publicKey, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -89,7 +89,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 	@Test
 	public void testAcceptsMinLengthBlogName() throws Exception {
 		String shortBlogName = TestUtils.getRandomString(1);
-		BdfList validDescriptor = BdfList.of(shortBlogName, publicKey);
+		BdfList validDescriptor = BdfList.of(shortBlogName, publicKey, false);
 		expectCreateBlog(shortBlogName, publicKey);
 		expectEncodeMetadata(INVITE);
 		BdfMessageContext messageContext = v.validateMessage(message, group,
@@ -102,7 +102,8 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 	public void testRejectsTooLongBlogName() throws Exception {
 		String invalidBlogName =
 				TestUtils.getRandomString(MAX_BLOG_NAME_LENGTH + 1);
-		BdfList invalidDescriptor = BdfList.of(invalidBlogName, publicKey);
+		BdfList invalidDescriptor =
+				BdfList.of(invalidBlogName, publicKey, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -110,7 +111,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsNullPublicKey() throws Exception {
-		BdfList invalidDescriptor = BdfList.of(authorName, null);
+		BdfList invalidDescriptor = BdfList.of(authorName, null, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -118,7 +119,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 
 	@Test(expected = FormatException.class)
 	public void testRejectsNonRawPublicKey() throws Exception {
-		BdfList invalidDescriptor = BdfList.of(authorName, 123);
+		BdfList invalidDescriptor = BdfList.of(authorName, 123, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -127,7 +128,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 	@Test(expected = FormatException.class)
 	public void testRejectsTooLongPublicKey() throws Exception {
 		byte[] invalidKey = TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH + 1);
-		BdfList invalidDescriptor = BdfList.of(authorName, invalidKey);
+		BdfList invalidDescriptor = BdfList.of(authorName, invalidKey, false);
 		v.validateMessage(message, group,
 				BdfList.of(INVITE.getValue(), previousMsgId, invalidDescriptor,
 						null));
@@ -136,7 +137,7 @@ public class BlogSharingValidatorTest extends SharingValidatorTest {
 	@Test
 	public void testAcceptsMinLengthPublicKey() throws Exception {
 		byte[] key = TestUtils.getRandomBytes(1);
-		BdfList validDescriptor = BdfList.of(authorName, key);
+		BdfList validDescriptor = BdfList.of(authorName, key, false);
 
 		expectCreateBlog(authorName, key);
 		expectEncodeMetadata(INVITE);
diff --git a/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..13323b346fb57a10f6fe6bd8f76906a96c857dad
--- /dev/null
+++ b/briar-core/src/test/java/org/briarproject/briar/test/TestDnsModule.java
@@ -0,0 +1,15 @@
+package org.briarproject.briar.test;
+
+import dagger.Module;
+import dagger.Provides;
+import okhttp3.Dns;
+
+@Module
+public class TestDnsModule {
+
+	@Provides
+	Dns provideDns() {
+		return Dns.SYSTEM;
+	}
+
+}