diff --git a/briar-api/src/org/briarproject/api/feed/Feed.java b/briar-api/src/org/briarproject/api/feed/Feed.java
index 7b394866989253bddaf2412359ce96330a4a489a..592e0d5432584835a229392e19704cf6dbea9693 100644
--- a/briar-api/src/org/briarproject/api/feed/Feed.java
+++ b/briar-api/src/org/briarproject/api/feed/Feed.java
@@ -43,6 +43,11 @@ public class Feed {
 		this(url, blogId, title, description, author, added, 0L, 0L);
 	}
 
+	public Feed(String url, GroupId blogId, long added) {
+
+		this(url, blogId, null, null, null, added, 0L, 0L);
+	}
+
 	public String getUrl() {
 		return url;
 	}
diff --git a/briar-api/src/org/briarproject/api/feed/FeedConstants.java b/briar-api/src/org/briarproject/api/feed/FeedConstants.java
index 68117a2f44bd31bd3bfff6e81341d54551161e1b..bc98ad7eeace3ffa0274608161925786e7117d5c 100644
--- a/briar-api/src/org/briarproject/api/feed/FeedConstants.java
+++ b/briar-api/src/org/briarproject/api/feed/FeedConstants.java
@@ -1,13 +1,20 @@
 package org.briarproject.api.feed;
 
+import java.util.concurrent.TimeUnit;
+
+import static java.util.concurrent.TimeUnit.MINUTES;
+
 public interface FeedConstants {
 
-	/* delay after start before fetching feed, in minutes */
+	/* delay after start before fetching feed */
 	int FETCH_DELAY_INITIAL = 1;
 
-	/* the interval the feed should be fetched, in minutes */
+	/* the interval the feed should be fetched */
 	int FETCH_INTERVAL = 30;
 
+	/* the unit that applies to the fetch times */
+	TimeUnit FETCH_UNIT = MINUTES;
+
 	// group metadata keys
 	String KEY_FEEDS = "feeds";
 	String KEY_FEED_URL = "feedURL";
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
index 182ea11869bb779e697910b5c9c73f5230ad75e9..a35bc0bc7fbad33d9b2928941fec1e91668ab16a 100644
--- a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -191,6 +191,7 @@ class BlogManagerImpl extends BdfIncomingMessageHook implements BlogManager,
 
 	@Override
 	public void removeBlog(Blog b) throws DbException {
+		// TODO if this gets used, check for RSS feeds posting into this blog
 		Transaction txn = db.startTransaction(false);
 		try {
 			for (RemoveBlogHook hook : removeHooks)
diff --git a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
index f0ea3eed6c24e1b419448c1aa5446ff34da88d6d..cf36c7be7e5ef91d32d9cd7dcd93d697823dedee 100644
--- a/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
+++ b/briar-core/src/org/briarproject/feed/FeedManagerImpl.java
@@ -8,7 +8,10 @@ import com.rometools.rome.io.SyndFeedInput;
 import com.rometools.rome.io.XmlReader;
 
 import org.briarproject.api.FormatException;
+import org.briarproject.api.blogs.Blog;
 import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPost;
+import org.briarproject.api.blogs.BlogPostFactory;
 import org.briarproject.api.clients.Client;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.clients.PrivateGroupFactory;
@@ -21,18 +24,27 @@ import org.briarproject.api.db.Transaction;
 import org.briarproject.api.feed.Feed;
 import org.briarproject.api.feed.FeedManager;
 import org.briarproject.api.lifecycle.IoExecutor;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.lifecycle.Service;
 import org.briarproject.api.lifecycle.ServiceException;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.system.Clock;
 import org.briarproject.util.StringUtils;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ScheduledExecutorService;
@@ -44,10 +56,12 @@ import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.Response;
 
-import static java.util.concurrent.TimeUnit.MINUTES;
+import static java.util.logging.Level.INFO;
 import static java.util.logging.Level.WARNING;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
 import static org.briarproject.api.feed.FeedConstants.FETCH_DELAY_INITIAL;
 import static org.briarproject.api.feed.FeedConstants.FETCH_INTERVAL;
+import static org.briarproject.api.feed.FeedConstants.FETCH_UNIT;
 import static org.briarproject.api.feed.FeedConstants.KEY_FEEDS;
 
 class FeedManagerImpl implements FeedManager, Service, Client {
@@ -65,19 +79,28 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 	private final DatabaseComponent db;
 	private final PrivateGroupFactory privateGroupFactory;
 	private final ClientHelper clientHelper;
+	private IdentityManager identityManager;
 	private final BlogManager blogManager;
 
+	@Inject
+	@SuppressWarnings("WeakerAccess")
+	BlogPostFactory blogPostFactory;
+	@Inject
+	@SuppressWarnings("WeakerAccess")
+	Clock clock;
+
 	@Inject
 	FeedManagerImpl(ScheduledExecutorService feedExecutor,
 			@IoExecutor Executor ioExecutor, DatabaseComponent db,
 			PrivateGroupFactory privateGroupFactory, ClientHelper clientHelper,
-			BlogManager blogManager) {
+			IdentityManager identityManager, BlogManager blogManager) {
 
 		this.feedExecutor = feedExecutor;
 		this.ioExecutor = ioExecutor;
 		this.db = db;
 		this.privateGroupFactory = privateGroupFactory;
 		this.clientHelper = clientHelper;
+		this.identityManager = identityManager;
 		this.blogManager = blogManager;
 	}
 
@@ -99,7 +122,7 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 			}
 		};
 		feedExecutor.scheduleWithFixedDelay(fetcher, FETCH_DELAY_INITIAL,
-				FETCH_INTERVAL, MINUTES);
+				FETCH_INTERVAL, FETCH_UNIT);
 	}
 
 	@Override
@@ -124,16 +147,11 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 	@Override
 	public void addFeed(String url, GroupId g) throws DbException, IOException {
 		LOG.info("Adding new RSS feed...");
-		Feed feed;
+
 		// TODO check for existing feed?
+		Feed feed = new Feed(url, g, clock.currentTimeMillis());
 		try {
-			SyndFeed f = getSyndFeed(getFeedInputStream(url));
-			String title = StringUtils.isNullOrEmpty(f.getTitle()) ? null :
-					f.getTitle();
-			String description = f.getDescription();
-			String author = f.getAuthor();
-			long added = System.currentTimeMillis();
-			feed = new Feed(url, g, title, description, author, added);
+			feed = fetchFeed(feed);
 		} catch (FeedException e) {
 			throw new IOException(e);
 		}
@@ -142,7 +160,7 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 		try {
 			List<Feed> feeds = getFeeds(txn);
 			feeds.add(feed);
-			storeFeeds(feeds);
+			storeFeeds(txn, feeds);
 		} finally {
 			db.endTransaction(txn);
 		}
@@ -150,6 +168,7 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 
 	@Override
 	public void removeFeed(String url) throws DbException {
+		LOG.info("Removing RSS feed...");
 		Transaction txn = db.startTransaction(false);
 		try {
 			List<Feed> feeds = getFeeds(txn);
@@ -222,6 +241,11 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 		storeFeeds(null, feeds);
 	}
 
+	/**
+	 * This method is called periodically from a background service.
+	 * It fetches all available feeds and posts new entries to the respective
+	 * blog.
+	 */
 	private void fetchFeeds() {
 		LOG.info("Updating RSS feeds...");
 
@@ -238,7 +262,15 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 		// Fetch and update all feeds
 		List<Feed> newFeeds = new ArrayList<Feed>(feeds.size());
 		for (Feed feed : feeds) {
-			newFeeds.add(fetchFeed(feed));
+			try {
+				newFeeds.add(fetchFeed(feed));
+			} catch (FeedException e) {
+				if (LOG.isLoggable(WARNING))
+					LOG.log(WARNING, e.toString(), e);
+			} catch (IOException e) {
+				if (LOG.isLoggable(WARNING))
+					LOG.log(WARNING, e.toString(), e);
+			}
 		}
 
 		// Store updated feeds
@@ -248,61 +280,45 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 			if (LOG.isLoggable(WARNING))
 				LOG.log(WARNING, e.toString(), e);
 		}
+		LOG.info("Done updating RSS feeds");
 	}
 
-	private Feed fetchFeed(Feed feed) {
+	private Feed fetchFeed(Feed feed) throws FeedException, IOException {
+		if (LOG.isLoggable(INFO))
+			LOG.info("Fetching feed from " + feed.getUrl());
+
 		String title, description, author;
-		long updated = System.currentTimeMillis();
+		long updated = clock.currentTimeMillis();
 		long lastEntryTime = feed.getLastEntryTime();
-		try {
-			SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl()));
-			title = f.getTitle();
-			description = f.getDescription();
-			author = f.getAuthor();
-
-			LOG.info("Title: " + f.getTitle());
-			LOG.info("Description: " + f.getDescription());
-			LOG.info("Author: " + f.getAuthor());
-			LOG.info("Number of Entries: " + f.getEntries().size());
-			LOG.info("------------------------------");
-
-			for (SyndEntry entry : f.getEntries()) {
-				LOG.info("Entry Title: " + entry.getTitle());
-				LOG.info("Entry Author: " + entry.getAuthor());
-				LOG.info("Entry Published Date: " + entry.getPublishedDate());
-				LOG.info("Entry Updated Date: " + entry.getUpdatedDate());
-				LOG.info("Entry Link: " + entry.getLink());
-				LOG.info("Entry URI: " + entry.getUri());
-				//LOG.info("Entry Description: " + entry.getDescription());
-				long entryTime;
-				if (entry.getPublishedDate() != null) {
-					entryTime = entry.getPublishedDate().getTime();
-				} else if (entry.getUpdatedDate() != null) {
-					entryTime = entry.getUpdatedDate().getTime();
-				} else {
-					// no time information available, ignore this entry
-					if (LOG.isLoggable(WARNING))
-						LOG.warning("Entry has no date: " + entry.getTitle());
-					continue;
-				}
-				if (entryTime > feed.getLastEntryTime()) {
-					LOG.info("Adding new entry...");
-					// TODO Pass any new entries down the pipeline to be posted (#486)
-					for (SyndContent content : entry.getContents()) {
-						LOG.info("Content: " + content.getValue());
-					}
-					if (entryTime > lastEntryTime) lastEntryTime = entryTime;
-				}
-				LOG.info("------------------------------");
+
+		SyndFeed f = getSyndFeed(getFeedInputStream(feed.getUrl()));
+		title = StringUtils.isNullOrEmpty(f.getTitle()) ? null : f.getTitle();
+		if (title != null) title = stripHTML(title);
+		description = StringUtils.isNullOrEmpty(f.getDescription()) ? null :
+				f.getDescription();
+		if (description != null) description = stripHTML(description);
+		author =
+				StringUtils.isNullOrEmpty(f.getAuthor()) ? null : f.getAuthor();
+		if (author != null) author = stripHTML(author);
+
+		// sort and add new entries
+		Collections.sort(f.getEntries(), getEntryComparator());
+		for (SyndEntry entry : f.getEntries()) {
+			long entryTime;
+			if (entry.getPublishedDate() != null) {
+				entryTime = entry.getPublishedDate().getTime();
+			} else if (entry.getUpdatedDate() != null) {
+				entryTime = entry.getUpdatedDate().getTime();
+			} else {
+				// no time information available, ignore this entry
+				if (LOG.isLoggable(WARNING))
+					LOG.warning("Entry has no date: " + entry.getTitle());
+				continue;
+			}
+			if (entryTime > feed.getLastEntryTime()) {
+				postEntry(feed, entry);
+				if (entryTime > lastEntryTime) lastEntryTime = entryTime;
 			}
-		} catch (FeedException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
-			return feed;
-		} catch (IOException e) {
-			if (LOG.isLoggable(WARNING))
-				LOG.log(WARNING, e.toString(), e);
-			return feed;
 		}
 		return new Feed(feed.getUrl(), feed.getBlogId(), title, description,
 				author, feed.getAdded(), updated, lastEntryTime);
@@ -313,7 +329,7 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 		// TODO verify and use local Tor proxy address/port
 		String proxyHost = "localhost";
 		int proxyPort = 59050;
-		Proxy proxy = new Proxy(Proxy.Type.HTTP,
+		Proxy proxy = new Proxy(Proxy.Type.SOCKS,
 				new InetSocketAddress(proxyHost, proxyPort));
 
 		// Build HTTP Client
@@ -338,6 +354,98 @@ class FeedManagerImpl implements FeedManager, Service, Client {
 		return input.build(new XmlReader(stream));
 	}
 
+	private void postEntry(Feed feed, SyndEntry entry) {
+		LOG.info("Adding new entry...");
+
+		// build post body
+		StringBuilder b = new StringBuilder();
+		if (!StringUtils.isNullOrEmpty(entry.getTitle())) {
+			b.append(stripHTML(entry.getTitle())).append("\n\n");
+		}
+		for (SyndContent content : entry.getContents()) {
+			// extract content and do a very simple HTML tag stripping
+			if (content.getValue() != null)
+				b.append(stripHTML(content.getValue()));
+		}
+		if (entry.getContents().size() == 0) {
+			if (entry.getDescription().getValue() != null)
+				b.append(stripHTML(entry.getDescription().getValue()));
+		}
+		if (!StringUtils.isNullOrEmpty(entry.getAuthor())) {
+			b.append("\n\n-- ").append(stripHTML(entry.getAuthor()));
+		}
+		if (entry.getPublishedDate() != null) {
+			b.append(" (").append(entry.getPublishedDate().toString())
+					.append(")");
+		} else if (entry.getUpdatedDate() != null) {
+			b.append(" (").append(entry.getUpdatedDate().toString())
+					.append(")");
+		}
+		if (!StringUtils.isNullOrEmpty(entry.getLink())) {
+			b.append("\n\n").append(stripHTML(entry.getLink()));
+		}
+
+		// get other information for post
+		GroupId groupId = feed.getBlogId();
+		long time = clock.currentTimeMillis();
+		byte[] body = getPostBody(b.toString());
+		try {
+			// create and store post
+			Blog blog = blogManager.getBlog(groupId);
+			AuthorId authorId = blog.getAuthor().getId();
+			LocalAuthor author = identityManager.getLocalAuthor(authorId);
+			BlogPost post = blogPostFactory
+					.createBlogPost(groupId, null, time, null, author,
+							"text/plain", body);
+			blogManager.addLocalPost(post);
+		} catch (DbException e) {
+			if (LOG.isLoggable(WARNING))
+				LOG.log(WARNING, e.toString(), e);
+		} catch (GeneralSecurityException e) {
+			if (LOG.isLoggable(WARNING))
+				LOG.log(WARNING, e.toString(), e);
+		} catch (FormatException e) {
+			if (LOG.isLoggable(WARNING))
+				LOG.log(WARNING, e.toString(), e);
+		} catch (IllegalArgumentException e) {
+			// yes even catch this, so we at least get a stacktrace
+			// and the executor doesn't just die a silent death
+			if (LOG.isLoggable(WARNING))
+				LOG.log(WARNING, e.toString(), e);
+		}
+	}
+
+	private String stripHTML(String s) {
+		return StringUtils.trim(s.replaceAll("<.*?>", ""));
+	}
+
+	private byte[] getPostBody(String text) {
+		byte[] body = StringUtils.toUtf8(text);
+		if (body.length <= MAX_BLOG_POST_BODY_LENGTH) return body;
+		else return Arrays.copyOfRange(body, 0, MAX_BLOG_POST_BODY_LENGTH - 1);
+	}
+
+	/**
+	 * This Comparator assumes that SyndEntry returns a valid Date either for
+	 * getPublishedDate() or getUpdatedDate().
+	 */
+	private Comparator<SyndEntry> getEntryComparator() {
+		return new Comparator<SyndEntry>() {
+			@Override
+			public int compare(SyndEntry e1, SyndEntry e2) {
+				Date d1 =
+						e1.getPublishedDate() != null ? e1.getPublishedDate() :
+								e1.getUpdatedDate();
+				Date d2 =
+						e2.getPublishedDate() != null ? e2.getPublishedDate() :
+								e2.getUpdatedDate();
+				if (d1.after(d2)) return 1;
+				if (d1.before(d2)) return -1;
+				return 0;
+			}
+		};
+	}
+
 	private Group getLocalGroup() {
 		return privateGroupFactory.createLocalGroup(getClientId());
 	}