diff --git a/briar-android/artwork/trust-indicator.svg b/briar-android/artwork/trust-indicator.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c8f94b6f65347cd2c7f195ebf7148db85ce80be4
--- /dev/null
+++ b/briar-android/artwork/trust-indicator.svg
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="49"
+   height="20"
+   viewBox="0 0 49.000004 20"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="trust-indicator.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="15.839192"
+     inkscape:cx="19.828141"
+     inkscape:cy="4.1791031"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     units="px"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0"
+     inkscape:window-width="1920"
+     inkscape:window-height="993"
+     inkscape:window-x="1440"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-216.17711,-507.04154)">
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="text4136"
+       transform="matrix(1,0,0,0.90497738,-18.96574,55.694085)">
+      <path
+         d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
+         style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
+         id="path4745"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       transform="matrix(1,0,0,0.90497738,-3.4657389,55.694085)"
+       id="g4755"
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
+      <path
+         inkscape:connector-curvature="0"
+         id="path4757"
+         style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
+         d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z" />
+    </g>
+    <g
+       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       id="g4139"
+       transform="matrix(1,0,0,0.90497738,12.034262,55.694085)">
+      <path
+         d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
+         style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
+         id="path4141"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/briar-android/res/drawable-hdpi/identity_anonymous.png b/briar-android/res/drawable-hdpi/identity_anonymous.png
deleted file mode 100644
index aa539d31b7aad168d93e4d2a45a28a6c3af18c7f..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-hdpi/identity_anonymous.png and /dev/null differ
diff --git a/briar-android/res/drawable-hdpi/identity_unknown.png b/briar-android/res/drawable-hdpi/identity_unknown.png
deleted file mode 100644
index b098e93aaea14e2c907662ff93b1f09f314f8bb3..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-hdpi/identity_unknown.png and /dev/null differ
diff --git a/briar-android/res/drawable-hdpi/identity_unverified.png b/briar-android/res/drawable-hdpi/identity_unverified.png
deleted file mode 100644
index f9e2486261cf419e1a629e08bf671dc5034334c5..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-hdpi/identity_unverified.png and /dev/null differ
diff --git a/briar-android/res/drawable-hdpi/identity_verified.png b/briar-android/res/drawable-hdpi/identity_verified.png
deleted file mode 100644
index 02e819fa1023e98c5124d9cdb17b9ee85629c766..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-hdpi/identity_verified.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/identity_anonymous.png b/briar-android/res/drawable-mdpi/identity_anonymous.png
deleted file mode 100644
index c91a016de8d6f692e8850f77156e4b0d5af82852..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-mdpi/identity_anonymous.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/identity_unknown.png b/briar-android/res/drawable-mdpi/identity_unknown.png
deleted file mode 100644
index 6ad29f8a0fa134f02856cdaf1de831cce8158b28..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-mdpi/identity_unknown.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/identity_unverified.png b/briar-android/res/drawable-mdpi/identity_unverified.png
deleted file mode 100644
index 9b8a13d4bfe10e6a710842ae079324dba4234c2b..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-mdpi/identity_unverified.png and /dev/null differ
diff --git a/briar-android/res/drawable-mdpi/identity_verified.png b/briar-android/res/drawable-mdpi/identity_verified.png
deleted file mode 100644
index 79cbaedc29a153ed0df16987b656ce659a876b1b..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-mdpi/identity_verified.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/identity_anonymous.png b/briar-android/res/drawable-xhdpi/identity_anonymous.png
deleted file mode 100644
index c8f254fdac6b62ee348a82b48d5e166eeb6cfb11..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-xhdpi/identity_anonymous.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/identity_unknown.png b/briar-android/res/drawable-xhdpi/identity_unknown.png
deleted file mode 100644
index e3b91a85841af0985cc2635ea3ec9e516719520e..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-xhdpi/identity_unknown.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/identity_unverified.png b/briar-android/res/drawable-xhdpi/identity_unverified.png
deleted file mode 100644
index 85785c09cceadbf4357e8ac8e0f25366ab5c5f19..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-xhdpi/identity_unverified.png and /dev/null differ
diff --git a/briar-android/res/drawable-xhdpi/identity_verified.png b/briar-android/res/drawable-xhdpi/identity_verified.png
deleted file mode 100644
index d93ae1df22bc1530da908a3ad53d12b92e68da8d..0000000000000000000000000000000000000000
Binary files a/briar-android/res/drawable-xhdpi/identity_verified.png and /dev/null differ
diff --git a/briar-android/res/drawable/trust_indicator_anonymous.xml b/briar-android/res/drawable/trust_indicator_anonymous.xml
new file mode 100644
index 0000000000000000000000000000000000000000..99ccadd1bea9fae5483b327fcfc48f89d574638f
--- /dev/null
+++ b/briar-android/res/drawable/trust_indicator_anonymous.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40dp"
+        android:height="15dp"
+        android:viewportHeight="20"
+        android:viewportWidth="49">
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
+L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
+L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
+L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
+L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
+L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
+L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
+L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
+L10.5752,8.38194 L10.5752,11.8208 Z"/>
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
+L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
+L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
+L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
+L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
+L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
+L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
+L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
+L26.0752,8.38194 L26.0752,11.8208 Z"/>
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
+L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
+L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
+L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
+L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
+L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
+L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
+L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
+L41.5752,8.38194 L41.5752,11.8208 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/res/drawable/trust_indicator_unknown.xml b/briar-android/res/drawable/trust_indicator_unknown.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ec65b5a5528a0f636c76c6f990c0e35b91afbfbb
--- /dev/null
+++ b/briar-android/res/drawable/trust_indicator_unknown.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40dp"
+        android:height="15dp"
+        android:viewportHeight="20"
+        android:viewportWidth="49">
+	<path
+		android:fillColor="#c34032"
+		android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
+L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
+L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
+L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
+L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
+L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
+L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
+L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
+L10.5752,8.38194 L10.5752,11.8208 Z"/>
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
+L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
+L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
+L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
+L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
+L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
+L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
+L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
+L26.0752,8.38194 L26.0752,11.8208 Z"/>
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
+L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
+L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
+L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
+L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
+L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
+L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
+L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
+L41.5752,8.38194 L41.5752,11.8208 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/res/drawable/trust_indicator_unverified.xml b/briar-android/res/drawable/trust_indicator_unverified.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3697685b0666be83b532c311431426749aa993e7
--- /dev/null
+++ b/briar-android/res/drawable/trust_indicator_unverified.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40dp"
+        android:height="15dp"
+        android:viewportHeight="20"
+        android:viewportWidth="49">
+	<path
+		android:fillColor="#fcd53a"
+		android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
+L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
+L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
+L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
+L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
+L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
+L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
+L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
+L10.5752,8.38194 L10.5752,11.8208 Z"/>
+	<path
+		android:fillColor="#fcd53a"
+		android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
+L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
+L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
+L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
+L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
+L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
+L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
+L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
+L26.0752,8.38194 L26.0752,11.8208 Z"/>
+	<path
+		android:fillColor="#b7b7b7"
+		android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
+L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
+L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
+L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
+L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
+L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
+L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
+L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
+L41.5752,8.38194 L41.5752,11.8208 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/res/drawable/trust_indicator_verified.xml b/briar-android/res/drawable/trust_indicator_verified.xml
new file mode 100644
index 0000000000000000000000000000000000000000..40a8ff035f262a9294255d6279bae1a0de241240
--- /dev/null
+++ b/briar-android/res/drawable/trust_indicator_verified.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="40dp"
+        android:height="15dp"
+        android:viewportHeight="20"
+        android:viewportWidth="49">
+	<path
+		android:fillColor="#7fac49"
+		android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
+L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
+L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
+L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
+L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
+L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
+L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
+L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
+L10.5752,8.38194 L10.5752,11.8208 Z"/>
+	<path
+		android:fillColor="#7fac49"
+		android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
+L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
+L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
+L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
+L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
+L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
+L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
+L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
+L26.0752,8.38194 L26.0752,11.8208 Z"/>
+	<path
+		android:fillColor="#7fac49"
+		android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
+L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
+L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
+L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
+L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
+L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
+L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
+L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
+L41.5752,8.38194 L41.5752,11.8208 Z"/>
+</vector>
\ No newline at end of file
diff --git a/briar-android/res/layout/author_view.xml b/briar-android/res/layout/author_view.xml
deleted file mode 100644
index 4fb0b9d6b458428cf493759548ea4df8d582a540..0000000000000000000000000000000000000000
--- a/briar-android/res/layout/author_view.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
-	xmlns:android="http://schemas.android.com/apk/res/android"
-	xmlns:tools="http://schemas.android.com/tools"
-	android:layout_width="match_parent"
-	android:layout_height="match_parent">
-
-	<de.hdodenhof.circleimageview.CircleImageView
-		android:id="@+id/avatarView"
-		style="@style/BriarAvatar"
-		android:layout_width="@dimen/listitem_picture_size"
-		android:layout_height="@dimen/listitem_picture_size"
-		android:layout_centerVertical="true"
-		android:layout_marginLeft="@dimen/listitem_horizontal_margin"
-		android:layout_marginStart="@dimen/listitem_horizontal_margin"
-		tools:src="@drawable/ic_launcher"/>
-
-	<ImageView
-		android:id="@+id/statusView"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_alignParentEnd="true"
-		android:layout_alignParentRight="true"
-		android:layout_centerVertical="true"
-		android:src="@drawable/identity_anonymous"/>
-
-	<TextView
-		android:id="@+id/nameView"
-		android:layout_width="wrap_content"
-		android:layout_height="wrap_content"
-		android:layout_alignParentLeft="true"
-		android:layout_alignParentStart="true"
-		android:layout_centerVertical="true"
-		android:layout_marginEnd="@dimen/margin_small"
-		android:layout_marginLeft="@dimen/listitem_text_left_margin"
-		android:layout_marginRight="@dimen/margin_small"
-		android:layout_marginStart="@dimen/listitem_text_left_margin"
-		android:layout_toLeftOf="@id/statusView"
-		android:layout_toStartOf="@id/statusView"
-		android:ellipsize="end"
-		android:singleLine="true"
-		android:text="@string/anonymous"
-		android:textSize="@dimen/text_size_medium"/>
-
-</RelativeLayout>
\ No newline at end of file
diff --git a/briar-android/res/layout/forum_discussion_cell.xml b/briar-android/res/layout/forum_discussion_cell.xml
index 57383a720623ffc952528c8ab2eadabe2d6b5ae1..e8fbf8aa3707236c27602375676b7c50802897bc 100644
--- a/briar-android/res/layout/forum_discussion_cell.xml
+++ b/briar-android/res/layout/forum_discussion_cell.xml
@@ -131,6 +131,9 @@
 			android:layout_height="wrap_content"
 			android:layout_alignBaseline="@id/btn_reply"
 			android:layout_toLeftOf="@id/btn_reply"
+			android:layout_toRightOf="@+id/trustIndicator"
+			android:gravity="right|end"
+			android:maxLines="1"
 			android:padding="@dimen/margin_medium"
 			android:textSize="@dimen/text_size_tiny"
 			tools:text="2 replies"/>
@@ -140,13 +143,23 @@
 			android:layout_width="wrap_content"
 			android:layout_height="wrap_content"
 			android:layout_alignBaseline="@id/replies"
-			android:layout_toLeftOf="@id/replies"
 			android:layout_toRightOf="@id/avatar"
 			android:ellipsize="end"
 			android:maxLines="1"
 			android:textSize="@dimen/text_size_tiny"
 			tools:text="09:09 John Smith"/>
 
+		<org.briarproject.android.util.TrustIndicatorView
+			android:id="@+id/trustIndicator"
+			android:layout_width="wrap_content"
+			android:layout_height="wrap_content"
+			android:layout_alignBottom="@+id/date"
+			android:layout_alignTop="@+id/date"
+			android:layout_marginLeft="@dimen/margin_small"
+			android:layout_marginStart="@dimen/margin_small"
+			android:layout_toRightOf="@+id/date"
+			tools:src="@drawable/trust_indicator_verified"/>
+
 		<View
 			android:id="@+id/bottom_divider"
 			style="@style/Divider.ForumList"
diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
index 712a7f3fe24714519c2541c9d44fe0a83ed6d6c8..921f26ec375390695a43779cbdae68a62e5bfdab 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java
@@ -29,6 +29,7 @@ import org.briarproject.android.BriarActivity;
 import org.briarproject.android.api.AndroidNotificationManager;
 import org.briarproject.android.controller.handler.UiResultHandler;
 import org.briarproject.android.util.BriarRecyclerView;
+import org.briarproject.android.util.TrustIndicatorView;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.util.StringUtils;
 
@@ -275,6 +276,7 @@ public class ForumActivity extends BriarActivity implements
 		public final TextView textView, lvlText, dateText, repliesText;
 		public final View[] lvls;
 		public final ImageView avatar;
+		final TrustIndicatorView trust;
 		public final View chevron, replyButton;
 		public final ViewGroup cell;
 		public final View bottomDivider;
@@ -295,6 +297,7 @@ public class ForumActivity extends BriarActivity implements
 				lvls[i] = v.findViewById(nestedLineIds[i]);
 			}
 			avatar = (ImageView) v.findViewById(R.id.avatar);
+			trust = (TrustIndicatorView) v.findViewById(R.id.trustIndicator);
 			chevron = v.findViewById(R.id.chevron);
 			replyButton = v.findViewById(R.id.btn_reply);
 			cell = (ViewGroup) v.findViewById(R.id.forum_cell);
@@ -513,6 +516,7 @@ public class ForumActivity extends BriarActivity implements
 			ui.dateText.setText(DateUtils
 					.getRelativeTimeSpanString(ForumActivity.this,
 							data.getTimestamp()) + " " + data.getAuthor());
+			ui.trust.setTrustLevel(data.getStatus());
 
 			int replies = getReplyCount(data);
 			if (replies == 0) {
diff --git a/briar-android/src/org/briarproject/android/forum/ForumAdapter.java b/briar-android/src/org/briarproject/android/forum/ForumAdapter.java
deleted file mode 100644
index df2e4e425fe795756948ed1dc03f4cfd2a64d2b9..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/forum/ForumAdapter.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package org.briarproject.android.forum;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.format.DateUtils;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import org.briarproject.R;
-import org.briarproject.android.util.AuthorView;
-import org.briarproject.android.util.LayoutUtils;
-import org.briarproject.api.forum.ForumPostHeader;
-import org.briarproject.api.identity.Author;
-import org.briarproject.util.StringUtils;
-
-import java.util.ArrayList;
-
-import static android.view.Gravity.CENTER_HORIZONTAL;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.widget.LinearLayout.HORIZONTAL;
-import static android.widget.LinearLayout.VERTICAL;
-import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1;
-
-class ForumAdapter extends ArrayAdapter<ForumItem> {
-
-	private final int pad;
-
-	ForumAdapter(Context ctx) {
-		super(ctx, android.R.layout.simple_expandable_list_item_1,
-				new ArrayList<ForumItem>());
-		pad = LayoutUtils.getPadding(ctx);
-	}
-
-	@Override
-	public View getView(int position, View convertView, ViewGroup parent) {
-		ForumItem item = getItem(position);
-		ForumPostHeader header = item.getHeader();
-		Context ctx = getContext();
-		Resources res = ctx.getResources();
-
-		LinearLayout layout = new LinearLayout(ctx);
-		layout.setOrientation(VERTICAL);
-		layout.setGravity(CENTER_HORIZONTAL);
-		if (!header.isRead())
-			layout.setBackgroundColor(res.getColor(R.color.unread_background));
-
-		LinearLayout headerLayout = new LinearLayout(ctx);
-		headerLayout.setOrientation(HORIZONTAL);
-		headerLayout.setGravity(CENTER_VERTICAL);
-
-		AuthorView authorView = new AuthorView(ctx);
-		authorView.setLayoutParams(WRAP_WRAP_1);
-		authorView.setPadding(0, pad, pad, pad);
-		Author author = header.getAuthor();
-		if (author == null) {
-			authorView.init(null, null, header.getAuthorStatus());
-		} else {
-			authorView.init(author.getName(), author.getId(),
-					header.getAuthorStatus());
-		}
-		headerLayout.addView(authorView);
-
-		TextView date = new TextView(ctx);
-		date.setPadding(pad, pad, pad, pad);
-		long timestamp = header.getTimestamp();
-		date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
-		headerLayout.addView(date);
-		layout.addView(headerLayout);
-
-		if (item.getBody() == null) {
-			TextView ellipsis = new TextView(ctx);
-			ellipsis.setPadding(pad, 0, pad, pad);
-			ellipsis.setText("\u2026");
-			layout.addView(ellipsis);
-		} else if (header.getContentType().equals("text/plain")) {
-			TextView text = new TextView(ctx);
-			text.setPadding(pad, 0, pad, pad);
-			text.setText(StringUtils.fromUtf8(item.getBody()));
-			layout.addView(text);
-		} else {
-			ImageButton attachment = new ImageButton(ctx);
-			attachment.setPadding(pad, 0, pad, pad);
-			attachment.setImageResource(R.drawable.content_attachment);
-			layout.addView(attachment);
-		}
-
-		return layout;
-	}
-}
\ No newline at end of file
diff --git a/briar-android/src/org/briarproject/android/forum/ForumEntry.java b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
index c3c668f7a052d46c0cadefd3e05e1d524c960fd2..a24d94bbcf457d57eb5dc140878895f24c734a28 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumEntry.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumEntry.java
@@ -1,6 +1,7 @@
 package org.briarproject.android.forum;
 
 import org.briarproject.api.forum.ForumPostHeader;
+import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.MessageId;
 
@@ -12,23 +13,25 @@ public class ForumEntry {
 	private final long timestamp;
 	private final String author;
 	private final AuthorId authorId;
+	private Status status;
 	private boolean isShowingDescendants = true;
 	private boolean isRead = true;
 
 	public ForumEntry(ForumPostHeader h, String text, int level) {
 		this(h.getId(), text, level, h.getTimestamp(), h.getAuthor().getName(),
-				h.getAuthor().getId());
+				h.getAuthor().getId(), h.getAuthorStatus());
 		this.isRead = h.isRead();
 	}
 
 	public ForumEntry(MessageId messageId, String text, int level,
-			long timestamp, String author, AuthorId authorId) {
+			long timestamp, String author, AuthorId authorId, Status status) {
 		this.messageId = messageId;
 		this.text = text;
 		this.level = level;
 		this.timestamp = timestamp;
 		this.author = author;
 		this.authorId = authorId;
+		this.status = status;
 	}
 
 	public String getText() {
@@ -51,6 +54,10 @@ public class ForumEntry {
 		return authorId;
 	}
 
+	public Status getStatus() {
+		return status;
+	}
+
 	public boolean isShowingDescendants() {
 		return isShowingDescendants;
 	}
diff --git a/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java b/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java
index ec1470ed85ceeaa6a34436bd6b5b0cc1a1c3de5d..2aef2ae5e121fe61e60789800aea97ac75e51a0b 100644
--- a/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java
+++ b/briar-android/src/org/briarproject/android/forum/ForumTestControllerImpl.java
@@ -15,6 +15,8 @@ import java.util.logging.Logger;
 
 import javax.inject.Inject;
 
+import static org.briarproject.api.identity.Author.Status.UNVERIFIED;
+
 public class ForumTestControllerImpl implements ForumController {
 
 	private static final Logger LOG =
@@ -115,7 +117,7 @@ public class ForumTestControllerImpl implements ForumController {
 			forumEntries[e] =
 					new ForumEntry(new MessageId(b), SAGA.substring(0, i[e]),
 							l[e], timestamp, AUTHORS[authorIndex],
-							AUTHOR_ID[authorIndex]);
+							AUTHOR_ID[authorIndex], UNVERIFIED);
 		}
 		LOG.info("forum entries: " + forumEntries.length);
 		resultHandler.onResult(true);
diff --git a/briar-android/src/org/briarproject/android/util/AuthorView.java b/briar-android/src/org/briarproject/android/util/AuthorView.java
deleted file mode 100644
index 4b11d51e8de38760b7ecff84a9790ec06b0793d4..0000000000000000000000000000000000000000
--- a/briar-android/src/org/briarproject/android/util/AuthorView.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package org.briarproject.android.util;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import org.briarproject.R;
-import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.identity.Author;
-import org.briarproject.api.identity.AuthorId;
-
-import javax.inject.Inject;
-
-import im.delight.android.identicons.IdenticonDrawable;
-
-public class AuthorView extends FrameLayout {
-
-	private ImageView avatarView;
-	private TextView nameView;
-	private ImageView statusView;
-
-	public AuthorView(Context ctx) {
-		super(ctx);
-
-		initViews();
-	}
-
-	public AuthorView(Context context, AttributeSet attrs) {
-		super(context, attrs);
-
-		initViews();
-	}
-
-	public AuthorView(Context context, AttributeSet attrs,
-							 int defStyle) {
-		super(context, attrs, defStyle);
-
-		initViews();
-	}
-
-	private void initViews() {
-		if (isInEditMode())
-			return;
-
-		View v = LayoutInflater.from(getContext()).inflate(
-				R.layout.author_view, this, true);
-
-		avatarView = (ImageView) v.findViewById(R.id.avatarView);
-		nameView = (TextView) v.findViewById(R.id.nameView);
-		statusView = (ImageView) v.findViewById(R.id.statusView);
-	}
-
-	public void init(String name, AuthorId id, Author.Status status) {
-		if (name == null) {
-			nameView.setText(R.string.anonymous);
-		} else {
-			nameView.setText(name);
-			avatarView.setImageDrawable(
-					new IdenticonDrawable(id.getBytes()));
-		}
-
-		switch(status) {
-		case ANONYMOUS:
-			statusView.setImageResource(R.drawable.identity_anonymous);
-			break;
-		case UNKNOWN:
-			statusView.setImageResource(R.drawable.identity_unknown);
-			break;
-		case UNVERIFIED:
-			statusView.setImageResource(R.drawable.identity_unverified);
-			break;
-		case VERIFIED:
-			statusView.setImageResource(R.drawable.identity_verified);
-			break;
-		}
-	}
-}
diff --git a/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java
new file mode 100644
index 0000000000000000000000000000000000000000..ce01bf301e34e1176d307e81e23ef80dd226fb62
--- /dev/null
+++ b/briar-android/src/org/briarproject/android/util/TrustIndicatorView.java
@@ -0,0 +1,44 @@
+package org.briarproject.android.util;
+
+import android.content.Context;
+import android.support.v4.content.ContextCompat;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+import org.briarproject.R;
+import org.briarproject.api.identity.Author.Status;
+
+public class TrustIndicatorView extends ImageView {
+
+	public TrustIndicatorView(Context context) {
+		super(context);
+	}
+
+	public TrustIndicatorView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+	}
+
+	public TrustIndicatorView(Context context, AttributeSet attrs,
+			int defStyleAttr) {
+		super(context, attrs, defStyleAttr);
+	}
+
+	public void setTrustLevel(Status status) {
+		int res;
+		switch (status) {
+			case ANONYMOUS:
+				res = R.drawable.trust_indicator_anonymous;
+				break;
+			case UNVERIFIED:
+				res = R.drawable.trust_indicator_unverified;
+				break;
+			case VERIFIED:
+				res = R.drawable.trust_indicator_verified;
+				break;
+			default:
+				res = R.drawable.trust_indicator_unknown;
+		}
+		setImageDrawable(ContextCompat.getDrawable(getContext(), res));
+	}
+
+}
diff --git a/briar-api/build.gradle b/briar-api/build.gradle
index d38a8b52035036596caac62784a2fbe3da2bd767..479b38fdcba2e4c60c74408cf16460b445c8da50 100644
--- a/briar-api/build.gradle
+++ b/briar-api/build.gradle
@@ -7,14 +7,16 @@ apply plugin: 'witness'
 dependencies {
 	compile "com.google.dagger:dagger:2.0.2"
 	compile 'com.google.dagger:dagger-compiler:2.0.2'
+	compile 'org.jetbrains:annotations-java5:15.0'
 }
 
 dependencyVerification {
 	verify = [
 			'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
 			'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
-			'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
+			'org.jetbrains:annotations-java5:c84e6e9947f802ec2183bdc415dd496df02a749cac92e805f697e60f628a1e24',
 			'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
+			'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
 			'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
 	]
 }
diff --git a/briar-api/src/org/briarproject/api/blogs/Blog.java b/briar-api/src/org/briarproject/api/blogs/Blog.java
new file mode 100644
index 0000000000000000000000000000000000000000..c97e42be2aae72185845fa2b5ea05ee257ebd17c
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/Blog.java
@@ -0,0 +1,32 @@
+package org.briarproject.api.blogs;
+
+import org.briarproject.api.forum.Forum;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.Group;
+import org.jetbrains.annotations.NotNull;
+
+public class Blog extends Forum {
+
+	@NotNull
+	private final String description;
+	@NotNull
+	private final Author author;
+
+	public Blog(@NotNull Group group, @NotNull String name,
+			@NotNull String description, @NotNull Author author) {
+		super(group, name, null);
+
+		this.description = description;
+		this.author = author;
+	}
+
+	@NotNull
+	public String getDescription() {
+		return description;
+	}
+
+	@NotNull
+	public Author getAuthor() {
+		return author;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogConstants.java b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
new file mode 100644
index 0000000000000000000000000000000000000000..60f06a641b5591ed79c35d32e785d3265358cff6
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogConstants.java
@@ -0,0 +1,39 @@
+package org.briarproject.api.blogs;
+
+import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
+
+public interface BlogConstants {
+
+	/** The maximum length of a blogs's name in UTF-8 bytes. */
+	int MAX_BLOG_TITLE_LENGTH = 100;
+
+	/** The length of a blogs's description in UTF-8 bytes. */
+	int MAX_BLOG_DESC_LENGTH = 240;
+
+	/** The maximum length of a blog post's content type in UTF-8 bytes. */
+	int MAX_CONTENT_TYPE_LENGTH = 50;
+
+	/** The length of a blog post's title in UTF-8 bytes. */
+	int MAX_BLOG_POST_TITLE_LENGTH = 100;
+
+	/** The length of a blog post's teaser in UTF-8 bytes. */
+	int MAX_BLOG_POST_TEASER_LENGTH = 240;
+
+	/** The maximum length of a blog post's body in bytes. */
+	int MAX_BLOG_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
+
+	// Metadata keys
+	String KEY_DESCRIPTION = "description";
+	String KEY_TITLE = "title";
+	String KEY_TEASER = "teaser";
+	String KEY_HAS_BODY = "hasBody";
+	String KEY_TIMESTAMP = "timestamp";
+	String KEY_PARENT = "parent";
+	String KEY_AUTHOR_ID = "id";
+	String KEY_AUTHOR_NAME = "name";
+	String KEY_PUBLIC_KEY = "publicKey";
+	String KEY_AUTHOR = "author";
+	String KEY_CONTENT_TYPE = "contentType";
+	String KEY_READ = "read";
+
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogFactory.java b/briar-api/src/org/briarproject/api/blogs/BlogFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..8da9db1926f11d4dc31ed633fb61d794aa1444fb
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogFactory.java
@@ -0,0 +1,17 @@
+package org.briarproject.api.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.Group;
+import org.jetbrains.annotations.NotNull;
+
+public interface BlogFactory {
+
+	/** Creates a blog with the given name, description and author. */
+	Blog createBlog(@NotNull String name, @NotNull String description,
+			@NotNull Author author);
+
+	/** Parses a blog with the given Group and description */
+	Blog parseBlog(@NotNull Group g, @NotNull String description)
+			throws FormatException;
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogManager.java b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..e01fa5efb9a8bc30cf6dbd4dd3f97d063dae713c
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogManager.java
@@ -0,0 +1,48 @@
+package org.briarproject.api.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.Transaction;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.ClientId;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+
+public interface BlogManager {
+
+	/** Returns the unique ID of the blog client. */
+	ClientId getClientId();
+
+	/** Creates a new Blog. */
+	Blog addBlog(LocalAuthor localAuthor, String name, String description)
+			throws DbException;
+
+	/** Stores a local blog post. */
+	void addLocalPost(BlogPost p) throws DbException;
+
+	/** Returns the blog with the given ID. */
+	Blog getBlog(GroupId g) throws DbException;
+
+	/** Returns the blog with the given ID. */
+	Blog getBlog(Transaction txn, GroupId g) throws DbException;
+
+	/** Returns all blogs to which the localAuthor created. */
+	Collection<Blog> getBlogs(LocalAuthor localAuthor) throws DbException;
+
+	/** Returns all blogs to which the user subscribes. */
+	Collection<Blog> getBlogs() throws DbException;
+
+	/** Returns the body of the blog post with the given ID. */
+	@Nullable
+	byte[] getPostBody(MessageId m) throws DbException;
+
+	/** Returns the headers of all posts in the given blog. */
+	Collection<BlogPostHeader> getPostHeaders(GroupId g) throws DbException;
+
+	/** Marks a blog post as read or unread. */
+	void setReadFlag(MessageId m, boolean read) throws DbException;
+
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPost.java b/briar-api/src/org/briarproject/api/blogs/BlogPost.java
new file mode 100644
index 0000000000000000000000000000000000000000..a6aa1167aebc9c24ac00bfc81fa1f5ffc157a064
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPost.java
@@ -0,0 +1,43 @@
+package org.briarproject.api.blogs;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import org.briarproject.api.forum.ForumPost;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageId;
+
+public class BlogPost extends ForumPost {
+
+	@Nullable
+	private final String title;
+	@NotNull
+	private final String teaser;
+	private final boolean hasBody;
+
+	public BlogPost(@Nullable String title, @NotNull String teaser,
+			boolean hasBody, @NotNull Message message,
+			@Nullable MessageId parent, @NotNull Author author,
+			@NotNull String contentType) {
+		super(message, parent, author, contentType);
+
+		this.title = title;
+		this.teaser = teaser;
+		this.hasBody = hasBody;
+	}
+
+	@Nullable
+	public String getTitle() {
+		return title;
+	}
+
+	@NotNull
+	public String getTeaser() {
+		return teaser;
+	}
+
+	public boolean hasBody() {
+		return hasBody;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..2565bfb0e731dc532e8cdc0a691538f8c8166d3a
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
@@ -0,0 +1,19 @@
+package org.briarproject.api.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.security.GeneralSecurityException;
+
+public interface BlogPostFactory {
+
+	BlogPost createBlogPost(@NotNull GroupId groupId, @Nullable String title,
+			@NotNull String teaser, long timestamp, @Nullable MessageId parent,
+			@NotNull LocalAuthor author, @NotNull String contentType,
+			@Nullable byte[] body)
+			throws FormatException, GeneralSecurityException;
+}
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
new file mode 100644
index 0000000000000000000000000000000000000000..24d421c13959ddb173c91130f70f42bad408bc07
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostHeader.java
@@ -0,0 +1,43 @@
+package org.briarproject.api.blogs;
+
+import org.briarproject.api.clients.PostHeader;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.Author.Status;
+import org.briarproject.api.sync.MessageId;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class BlogPostHeader extends PostHeader {
+
+	@Nullable
+	private final String title;
+	@NotNull
+	private final String teaser;
+	private final boolean hasBody;
+
+	public BlogPostHeader(@Nullable String title, @NotNull String teaser,
+			boolean hasBody, @NotNull MessageId id,
+			@Nullable MessageId parentId, long timestamp,
+			@NotNull Author author,	@NotNull Status authorStatus,
+			@NotNull String contentType, boolean read) {
+		super(id, parentId, timestamp, author, authorStatus, contentType, read);
+
+		this.title = title;
+		this.teaser = teaser;
+		this.hasBody = hasBody;
+	}
+
+	@Nullable
+	public String getTitle() {
+		return title;
+	}
+
+	@NotNull
+	public String getTeaser() {
+		return teaser;
+	}
+
+	public boolean hasBody() {
+		return hasBody;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/clients/PostHeader.java b/briar-api/src/org/briarproject/api/clients/PostHeader.java
new file mode 100644
index 0000000000000000000000000000000000000000..33f0d28771c9ecc7f30eeb87798d18b3b3f4c1b2
--- /dev/null
+++ b/briar-api/src/org/briarproject/api/clients/PostHeader.java
@@ -0,0 +1,55 @@
+package org.briarproject.api.clients;
+
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.MessageId;
+
+public abstract class PostHeader {
+
+	private final MessageId id;
+	private final MessageId parentId;
+	private final long timestamp;
+	private final Author author;
+	private final Author.Status authorStatus;
+	private final String contentType;
+	private final boolean read;
+
+	public PostHeader(MessageId id, MessageId parentId, long timestamp,
+			Author author, Author.Status authorStatus, String contentType,
+			boolean read) {
+		this.id = id;
+		this.parentId = parentId;
+		this.timestamp = timestamp;
+		this.author = author;
+		this.authorStatus = authorStatus;
+		this.contentType = contentType;
+		this.read = read;
+	}
+
+	public MessageId getId() {
+		return id;
+	}
+
+	public Author getAuthor() {
+		return author;
+	}
+
+	public Author.Status getAuthorStatus() {
+		return authorStatus;
+	}
+
+	public String getContentType() {
+		return contentType;
+	}
+
+	public long getTimestamp() {
+		return timestamp;
+	}
+
+	public boolean isRead() {
+		return read;
+	}
+
+	public MessageId getParentId() {
+		return parentId;
+	}
+}
diff --git a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
index 40779390539cd3dff457cc70470d1443780f1755..c25a9e251b1436e3beda8207656abba1112a0674 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumPostHeader.java
@@ -1,56 +1,17 @@
 package org.briarproject.api.forum;
 
 import org.briarproject.api.clients.MessageTree;
+import org.briarproject.api.clients.PostHeader;
 import org.briarproject.api.identity.Author;
 import org.briarproject.api.sync.MessageId;
 
-public class ForumPostHeader implements MessageTree.MessageNode {
-
-	private final MessageId id;
-	private final MessageId parentId;
-	private final long timestamp;
-	private final Author author;
-	private final Author.Status authorStatus;
-	private final String contentType;
-	private final boolean read;
+public class ForumPostHeader extends PostHeader
+		implements MessageTree.MessageNode {
 
 	public ForumPostHeader(MessageId id, MessageId parentId, long timestamp,
 			Author author, Author.Status authorStatus, String contentType,
 			boolean read) {
-		this.id = id;
-		this.parentId = parentId;
-		this.timestamp = timestamp;
-		this.author = author;
-		this.authorStatus = authorStatus;
-		this.contentType = contentType;
-		this.read = read;
-	}
-
-	public MessageId getId() {
-		return id;
-	}
-
-	public Author getAuthor() {
-		return author;
-	}
-
-	public Author.Status getAuthorStatus() {
-		return authorStatus;
+		super(id, parentId, timestamp, author, authorStatus, contentType, read);
 	}
 
-	public String getContentType() {
-		return contentType;
-	}
-
-	public long getTimestamp() {
-		return timestamp;
-	}
-
-	public boolean isRead() {
-		return read;
-	}
-
-	public MessageId getParentId() {
-		return parentId;
-	}
 }
diff --git a/briar-api/src/org/briarproject/api/identity/IdentityManager.java b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
index f2a4db2d906f1a6cec2e8292ce7a7437d94ae158..e5420a043f267c9a643fd24987369552839e560e 100644
--- a/briar-api/src/org/briarproject/api/identity/IdentityManager.java
+++ b/briar-api/src/org/briarproject/api/identity/IdentityManager.java
@@ -2,6 +2,7 @@ package org.briarproject.api.identity;
 
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.identity.Author.Status;
 
 import java.util.Collection;
 
@@ -25,6 +26,9 @@ public interface IdentityManager {
 	/** Removes a local pseudonym and all associated state. */
 	void removeLocalAuthor(AuthorId a) throws DbException;
 
+	/** Returns the trust-level status of the author */
+	Status getAuthorStatus(AuthorId a) throws DbException;
+
 	interface AddIdentityHook {
 		void addingIdentity(Transaction txn, LocalAuthor a) throws DbException;
 	}
diff --git a/briar-core/src/org/briarproject/CoreEagerSingletons.java b/briar-core/src/org/briarproject/CoreEagerSingletons.java
index 174ee9c570b572e77861cff17229a19f0f9d8a71..6847f461220bebd4b3131b09af88e6d253c5c677 100644
--- a/briar-core/src/org/briarproject/CoreEagerSingletons.java
+++ b/briar-core/src/org/briarproject/CoreEagerSingletons.java
@@ -1,5 +1,6 @@
 package org.briarproject;
 
+import org.briarproject.blogs.BlogsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
 import org.briarproject.db.DatabaseExecutorModule;
@@ -15,6 +16,8 @@ import org.briarproject.transport.TransportModule;
 
 public interface CoreEagerSingletons {
 
+	void inject(BlogsModule.EagerSingletons init);
+
 	void inject(ContactModule.EagerSingletons init);
 
 	void inject(CryptoModule.EagerSingletons init);
diff --git a/briar-core/src/org/briarproject/CoreModule.java b/briar-core/src/org/briarproject/CoreModule.java
index 238c859e7fa8f1d79b00613c14e92938d7ace480..6a909b3f6d8110b63a4348cee50012283ef6d2d2 100644
--- a/briar-core/src/org/briarproject/CoreModule.java
+++ b/briar-core/src/org/briarproject/CoreModule.java
@@ -1,5 +1,6 @@
 package org.briarproject;
 
+import org.briarproject.blogs.BlogsModule;
 import org.briarproject.clients.ClientsModule;
 import org.briarproject.contact.ContactModule;
 import org.briarproject.crypto.CryptoModule;
@@ -26,6 +27,7 @@ import org.briarproject.transport.TransportModule;
 import dagger.Module;
 
 @Module(includes = {
+		BlogsModule.class,
 		ClientsModule.class,
 		ContactModule.class,
 		CryptoModule.class,
@@ -52,6 +54,7 @@ import dagger.Module;
 public class CoreModule {
 
 	public static void initEagerSingletons(CoreEagerSingletons c) {
+		c.inject(new BlogsModule.EagerSingletons());
 		c.inject(new ContactModule.EagerSingletons());
 		c.inject(new CryptoModule.EagerSingletons());
 		c.inject(new DatabaseExecutorModule.EagerSingletons());
diff --git a/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b04401f5d1297854af0e45c6694afa5f26edc796
--- /dev/null
+++ b/briar-core/src/org/briarproject/blogs/BlogFactoryImpl.java
@@ -0,0 +1,61 @@
+package org.briarproject.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogFactory;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.GroupFactory;
+import org.jetbrains.annotations.NotNull;
+
+import javax.inject.Inject;
+
+class BlogFactoryImpl implements BlogFactory {
+
+	private final GroupFactory groupFactory;
+	private final AuthorFactory authorFactory;
+	private final ClientHelper clientHelper;
+
+	@Inject
+	BlogFactoryImpl(GroupFactory groupFactory, AuthorFactory authorFactory,
+			ClientHelper clientHelper) {
+
+		this.groupFactory = groupFactory;
+		this.authorFactory = authorFactory;
+		this.clientHelper = clientHelper;
+	}
+
+	@Override
+	public Blog createBlog(@NotNull String name, @NotNull String description,
+			@NotNull Author author) {
+		try {
+			BdfList blog = BdfList.of(
+					name,
+					author.getName(),
+					author.getPublicKey()
+			);
+			byte[] descriptor = clientHelper.toByteArray(blog);
+			Group g = groupFactory
+					.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
+			return new Blog(g, name, description, author);
+		} catch (FormatException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override
+	public Blog parseBlog(@NotNull Group g, @NotNull String description)
+			throws FormatException {
+
+		byte[] descriptor = g.getDescriptor();
+		// Blog Name, Author Name, Public Key
+		BdfList blog = clientHelper.toList(descriptor, 0, descriptor.length);
+		Author a =
+				authorFactory.createAuthor(blog.getString(1), blog.getRaw(2));
+		return new Blog(g, blog.getString(0), description, a);
+	}
+
+}
diff --git a/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..eabfc251e3c7284557952ca8766196646cb063a9
--- /dev/null
+++ b/briar-core/src/org/briarproject/blogs/BlogManagerImpl.java
@@ -0,0 +1,260 @@
+package org.briarproject.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogFactory;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPost;
+import org.briarproject.api.blogs.BlogPostHeader;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfEntry;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.db.DbException;
+import org.briarproject.api.db.Transaction;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.Author.Status;
+import org.briarproject.api.identity.AuthorId;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.ClientId;
+import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.util.StringUtils;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+import javax.inject.Inject;
+
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR;
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_ID;
+import static org.briarproject.api.blogs.BlogConstants.KEY_AUTHOR_NAME;
+import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
+import static org.briarproject.api.blogs.BlogConstants.KEY_DESCRIPTION;
+import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
+import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
+import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
+import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
+
+class BlogManagerImpl implements BlogManager {
+
+	private static final Logger LOG =
+			Logger.getLogger(BlogManagerImpl.class.getName());
+
+	static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
+			"dafbe56f0c8971365cea4bb5f08ec9a6" +
+					"1d686e058b943997b6ff259ba423f613"));
+
+	private final DatabaseComponent db;
+	private final IdentityManager identityManager;
+	private final ClientHelper clientHelper;
+	private final BlogFactory blogFactory;
+
+	@Inject
+	BlogManagerImpl(DatabaseComponent db, IdentityManager identityManager,
+			ClientHelper clientHelper, BlogFactory blogFactory) {
+
+		this.db = db;
+		this.identityManager = identityManager;
+		this.clientHelper = clientHelper;
+		this.blogFactory = blogFactory;
+	}
+
+	@Override
+	public ClientId getClientId() {
+		return CLIENT_ID;
+	}
+
+	@Override
+	public Blog addBlog(LocalAuthor localAuthor, String name,
+			String description) throws DbException {
+
+		Blog b = blogFactory
+				.createBlog(name, description, localAuthor);
+		BdfDictionary metadata = BdfDictionary.of(
+				new BdfEntry(KEY_DESCRIPTION, b.getDescription())
+		);
+
+		Transaction txn = db.startTransaction(false);
+		try {
+			db.addGroup(txn, b.getGroup());
+			clientHelper.mergeGroupMetadata(txn, b.getId(), metadata);
+			txn.setComplete();
+		} catch (FormatException e) {
+			throw new DbException(e);
+		} finally {
+			db.endTransaction(txn);
+		}
+		return b;
+	}
+
+	@Override
+	public void addLocalPost(BlogPost p) throws DbException {
+		try {
+			BdfDictionary meta = new BdfDictionary();
+			if (p.getTitle() != null) meta.put(KEY_TITLE, p.getTitle());
+			meta.put(KEY_TEASER, p.getTeaser());
+			meta.put(KEY_TIMESTAMP, p.getMessage().getTimestamp());
+			meta.put(KEY_HAS_BODY, p.hasBody());
+			if (p.getParent() != null) meta.put(KEY_PARENT, p.getParent());
+
+			Author a = p.getAuthor();
+			BdfDictionary authorMeta = new BdfDictionary();
+			authorMeta.put(KEY_AUTHOR_ID, a.getId());
+			authorMeta.put(KEY_AUTHOR_NAME, a.getName());
+			authorMeta.put(KEY_PUBLIC_KEY, a.getPublicKey());
+			meta.put(KEY_AUTHOR, authorMeta);
+
+			meta.put(KEY_CONTENT_TYPE, p.getContentType());
+			meta.put(KEY_READ, true);
+			clientHelper.addLocalMessage(p.getMessage(), CLIENT_ID, meta, true);
+		} catch (FormatException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	@Override
+	public Blog getBlog(GroupId g) throws DbException {
+		Blog blog;
+		Transaction txn = db.startTransaction(true);
+		try {
+			blog = getBlog(txn, g);
+			txn.setComplete();
+		} finally {
+			db.endTransaction(txn);
+		}
+		return blog;
+	}
+
+	@Override
+	public Blog getBlog(Transaction txn, GroupId g) throws DbException {
+		try {
+			Group group = db.getGroup(txn, g);
+			String description = getBlogDescription(txn, g);
+			return blogFactory.parseBlog(group, description);
+		} catch (FormatException e) {
+			throw new DbException(e);
+		}
+	}
+
+	@Override
+	public Collection<Blog> getBlogs(LocalAuthor localAuthor)
+			throws DbException {
+
+		Collection<Blog> allBlogs = getBlogs();
+		List<Blog> blogs = new ArrayList<Blog>();
+		for (Blog b : allBlogs) {
+			if (b.getAuthor().equals(localAuthor)) {
+				blogs.add(b);
+			}
+		}
+		return Collections.unmodifiableList(blogs);
+	}
+
+	@Override
+	public Collection<Blog> getBlogs() throws DbException {
+		try {
+			List<Blog> blogs = new ArrayList<Blog>();
+			Collection<Group> groups;
+			Transaction txn = db.startTransaction(true);
+			try {
+				groups = db.getGroups(txn, CLIENT_ID);
+				for (Group g : groups) {
+					String description = getBlogDescription(txn, g.getId());
+					blogs.add(blogFactory.parseBlog(g, description));
+				}
+				txn.setComplete();
+			} finally {
+				db.endTransaction(txn);
+			}
+			return Collections.unmodifiableList(blogs);
+		} catch (FormatException e) {
+			throw new DbException(e);
+		}
+	}
+
+	@Override
+	@Nullable
+	public byte[] getPostBody(MessageId m) throws DbException {
+		try {
+			// content, signature
+			// content: parent, contentType, title, teaser, body, attachments
+			BdfList message = clientHelper.getMessageAsList(m);
+			BdfList content = message.getList(0);
+			return content.getRaw(4);
+		} catch (FormatException e) {
+			throw new DbException(e);
+		}
+	}
+
+	@Override
+	public Collection<BlogPostHeader> getPostHeaders(GroupId g)
+			throws DbException {
+
+		Map<MessageId, BdfDictionary> metadata;
+		try {
+			metadata = clientHelper.getMessageMetadataAsDictionary(g);
+		} catch (FormatException e) {
+			throw new DbException(e);
+		}
+		Collection<BlogPostHeader> headers = new ArrayList<BlogPostHeader>();
+		for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
+			try {
+				BdfDictionary meta = entry.getValue();
+				String title = meta.getOptionalString(KEY_TITLE);
+				String teaser = meta.getString(KEY_TEASER);
+				boolean hasBody = meta.getBoolean(KEY_HAS_BODY);
+				long timestamp = meta.getLong(KEY_TIMESTAMP);
+				MessageId parentId = null;
+				if (meta.containsKey(KEY_PARENT))
+					parentId = new MessageId(meta.getRaw(KEY_PARENT));
+
+				BdfDictionary d = meta.getDictionary(KEY_AUTHOR);
+				AuthorId authorId = new AuthorId(d.getRaw(KEY_AUTHOR_ID));
+				String name = d.getString(KEY_AUTHOR_NAME);
+				byte[] publicKey = d.getRaw(KEY_PUBLIC_KEY);
+				Author author = new Author(authorId, name, publicKey);
+				Status authorStatus = identityManager.getAuthorStatus(authorId);
+
+				String contentType = meta.getString(KEY_CONTENT_TYPE);
+				boolean read = meta.getBoolean(KEY_READ);
+				headers.add(new BlogPostHeader(title, teaser, hasBody,
+						entry.getKey(), parentId, timestamp, author,
+						authorStatus, contentType, read));
+			} catch (FormatException e) {
+				throw new DbException(e);
+			}
+		}
+		return headers;
+	}
+
+	@Override
+	public void setReadFlag(MessageId m, boolean read) throws DbException {
+		try {
+			BdfDictionary meta = new BdfDictionary();
+			meta.put(KEY_READ, read);
+			clientHelper.mergeMessageMetadata(m, meta);
+		} catch (FormatException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private String getBlogDescription(Transaction txn, GroupId g)
+			throws DbException, FormatException {
+		BdfDictionary d = clientHelper.getGroupMetadataAsDictionary(txn, g);
+		return d.getString(KEY_DESCRIPTION);
+	}
+
+}
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e9ced2d3fe86ec555eb19e580e4c8ec5a83cdbcc
--- /dev/null
+++ b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
@@ -0,0 +1,78 @@
+package org.briarproject.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.blogs.BlogPost;
+import org.briarproject.api.blogs.BlogPostFactory;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.KeyParser;
+import org.briarproject.api.crypto.PrivateKey;
+import org.briarproject.api.crypto.Signature;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.sync.GroupId;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.util.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.security.GeneralSecurityException;
+
+import javax.inject.Inject;
+
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
+
+class BlogPostFactoryImpl implements BlogPostFactory {
+
+	private final CryptoComponent crypto;
+	private final ClientHelper clientHelper;
+
+	@Inject
+	BlogPostFactoryImpl(CryptoComponent crypto, ClientHelper clientHelper) {
+		this.crypto = crypto;
+		this.clientHelper = clientHelper;
+	}
+
+	@Override
+	public BlogPost createBlogPost(@NotNull GroupId groupId,
+			@Nullable String title, @NotNull String teaser,	long timestamp,
+			@Nullable MessageId parent,	@NotNull LocalAuthor author,
+			@NotNull String contentType, @Nullable byte[] body)
+			throws FormatException, GeneralSecurityException {
+
+		// Validate the arguments
+		if (title != null &&
+				StringUtils.toUtf8(title).length > MAX_BLOG_POST_TITLE_LENGTH)
+			throw new IllegalArgumentException();
+		if (StringUtils.toUtf8(teaser).length > MAX_BLOG_POST_TEASER_LENGTH)
+			throw new IllegalArgumentException();
+		if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
+			throw new IllegalArgumentException();
+		if (body != null && body.length > MAX_BLOG_POST_BODY_LENGTH)
+			throw new IllegalArgumentException();
+
+		// Serialise the data to be signed
+		BdfList content =
+				BdfList.of(parent, contentType, title, teaser, body, null);
+		BdfList signed = BdfList.of(groupId, timestamp, content);
+
+		// Generate the signature
+		Signature signature = crypto.getSignature();
+		KeyParser keyParser = crypto.getSignatureKeyParser();
+		PrivateKey privateKey =
+				keyParser.parsePrivateKey(author.getPrivateKey());
+		signature.initSign(privateKey);
+		signature.update(clientHelper.toByteArray(signed));
+		byte[] sig = signature.sign();
+
+		// Serialise the signed message
+		BdfList message = BdfList.of(content, sig);
+		Message m = clientHelper.createMessage(groupId, timestamp, message);
+		return new BlogPost(title, teaser, body != null, m, parent, author,
+				contentType);
+	}
+}
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..bacf02dff64d45e59ca80113158cba6e2778c206
--- /dev/null
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -0,0 +1,125 @@
+package org.briarproject.blogs;
+
+import org.briarproject.api.FormatException;
+import org.briarproject.api.UniqueId;
+import org.briarproject.api.blogs.Blog;
+import org.briarproject.api.blogs.BlogFactory;
+import org.briarproject.api.clients.BdfMessageContext;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.KeyParser;
+import org.briarproject.api.crypto.PublicKey;
+import org.briarproject.api.crypto.Signature;
+import org.briarproject.api.data.BdfDictionary;
+import org.briarproject.api.data.BdfList;
+import org.briarproject.api.data.MetadataEncoder;
+import org.briarproject.api.identity.Author;
+import org.briarproject.api.sync.Group;
+import org.briarproject.api.sync.InvalidMessageException;
+import org.briarproject.api.sync.Message;
+import org.briarproject.api.sync.MessageId;
+import org.briarproject.api.system.Clock;
+import org.briarproject.clients.BdfMessageValidator;
+
+import java.security.GeneralSecurityException;
+import java.util.Collection;
+import java.util.Collections;
+
+import static org.briarproject.api.blogs.BlogConstants.KEY_CONTENT_TYPE;
+import static org.briarproject.api.blogs.BlogConstants.KEY_HAS_BODY;
+import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT;
+import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TEASER;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TIMESTAMP;
+import static org.briarproject.api.blogs.BlogConstants.KEY_TITLE;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TEASER_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_TITLE_LENGTH;
+import static org.briarproject.api.blogs.BlogConstants.MAX_CONTENT_TYPE_LENGTH;
+import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+
+class BlogPostValidator extends BdfMessageValidator {
+
+	private final CryptoComponent crypto;
+	private final BlogFactory blogFactory;
+
+	BlogPostValidator(CryptoComponent crypto, BlogFactory blogFactory,
+			ClientHelper clientHelper, MetadataEncoder metadataEncoder,
+			Clock clock) {
+		super(clientHelper, metadataEncoder, clock);
+
+		this.crypto = crypto;
+		this.blogFactory = blogFactory;
+	}
+
+	@Override
+	protected BdfMessageContext validateMessage(Message m, Group g,
+			BdfList body) throws InvalidMessageException, FormatException {
+
+		// Content, Signature
+		checkSize(body, 2);
+		BdfList content = body.getList(0);
+
+		// Content: Parent ID, content type, title (optional), teaser,
+		//          post body (optional), attachments (optional)
+		checkSize(body, 6);
+		// Parent ID is optional
+		byte[] parent = content.getOptionalRaw(0);
+		checkLength(parent, UniqueId.LENGTH);
+		// Content type
+		String contentType = content.getString(1);
+		checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
+		// Blog post title is optional
+		String title = content.getOptionalString(2);
+		checkLength(contentType, 0, MAX_BLOG_POST_TITLE_LENGTH);
+		// Blog teaser
+		String teaser = content.getString(3);
+		// TODO make sure that there is only text in the teaser
+		checkLength(contentType, 0, MAX_BLOG_POST_TEASER_LENGTH);
+		// Blog post body is optional
+		byte[] postBody = content.getOptionalRaw(4);
+		checkLength(postBody, 0, MAX_BLOG_POST_BODY_LENGTH);
+		// Attachments
+		BdfDictionary attachments = content.getOptionalDictionary(5);
+		// TODO handle attachments somehow
+
+		// Signature
+		byte[] sig = body.getRaw(1);
+		checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
+		// Verify the signature
+		try {
+			// Get the blog author
+			Blog b = blogFactory.parseBlog(g, ""); // description doesn't matter
+			Author a = b.getAuthor();
+			// Parse the public key
+			KeyParser keyParser = crypto.getSignatureKeyParser();
+			PublicKey key = keyParser.parsePublicKey(a.getPublicKey());
+			// Serialise the data to be signed
+			BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), content);
+			// Verify the signature
+			Signature signature = crypto.getSignature();
+			signature.initVerify(key);
+			signature.update(clientHelper.toByteArray(signed));
+			if (!signature.verify(sig)) {
+				throw new InvalidMessageException("Invalid signature");
+			}
+		} catch (GeneralSecurityException e) {
+			throw new InvalidMessageException("Invalid public key");
+		}
+
+		// Return the metadata and dependencies
+		BdfDictionary meta = new BdfDictionary();
+		Collection<MessageId> dependencies = null;
+		if (title != null) meta.put(KEY_TITLE, title);
+		meta.put(KEY_TEASER, teaser);
+		meta.put(KEY_HAS_BODY, postBody != null);
+		meta.put(KEY_TIMESTAMP, m.getTimestamp());
+		if (parent != null) {
+			meta.put(KEY_PARENT, parent);
+			dependencies = Collections.singletonList(new MessageId(parent));
+		}
+		meta.put(KEY_CONTENT_TYPE, contentType);
+		meta.put(KEY_READ, false);
+		return new BdfMessageContext(meta, dependencies);
+	}
+}
diff --git a/briar-core/src/org/briarproject/blogs/BlogsModule.java b/briar-core/src/org/briarproject/blogs/BlogsModule.java
new file mode 100644
index 0000000000000000000000000000000000000000..4cf7894d1b608b2557a8098f5509a1ebb6ec7b18
--- /dev/null
+++ b/briar-core/src/org/briarproject/blogs/BlogsModule.java
@@ -0,0 +1,63 @@
+package org.briarproject.blogs;
+
+import org.briarproject.api.blogs.BlogFactory;
+import org.briarproject.api.blogs.BlogManager;
+import org.briarproject.api.blogs.BlogPostFactory;
+import org.briarproject.api.clients.ClientHelper;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.data.MetadataEncoder;
+import org.briarproject.api.db.DatabaseComponent;
+import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.identity.IdentityManager;
+import org.briarproject.api.sync.GroupFactory;
+import org.briarproject.api.sync.ValidationManager;
+import org.briarproject.api.system.Clock;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class BlogsModule {
+
+	public static class EagerSingletons {
+		@Inject
+		BlogPostValidator blogPostValidator;
+	}
+
+	@Provides
+	@Singleton
+	BlogManager provideBlogManager(BlogManagerImpl blogManager) {
+		return blogManager;
+	}
+
+	@Provides
+	BlogPostFactory provideBlogPostFactory(CryptoComponent crypto,
+			ClientHelper clientHelper) {
+		return new BlogPostFactoryImpl(crypto, clientHelper);
+	}
+
+	@Provides
+	BlogFactory provideBlogFactory(GroupFactory groupFactory,
+			AuthorFactory authorFactory, ClientHelper clientHelper) {
+		return new BlogFactoryImpl(groupFactory, authorFactory, clientHelper);
+	}
+
+	@Provides
+	@Singleton
+	BlogPostValidator provideBlogPostValidator(
+			ValidationManager validationManager, CryptoComponent crypto,
+			BlogFactory blogFactory, ClientHelper clientHelper,
+			MetadataEncoder metadataEncoder, Clock clock) {
+
+		BlogPostValidator validator = new BlogPostValidator(crypto,
+				blogFactory, clientHelper, metadataEncoder, clock);
+		validationManager.registerMessageValidator(
+				BlogManagerImpl.CLIENT_ID, validator);
+
+		return validator;
+	}
+
+}
diff --git a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
index 29bf817aaea53b313f96dec67837a2ce0e1b9bb9..36f8357bafdfecf063c21a8f270d91271ad24b98 100644
--- a/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumManagerImpl.java
@@ -2,7 +2,6 @@ package org.briarproject.forum;
 
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
-import org.briarproject.api.contact.Contact;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.db.DatabaseComponent;
@@ -14,8 +13,9 @@ import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPost;
 import org.briarproject.api.forum.ForumPostHeader;
 import org.briarproject.api.identity.Author;
+import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.identity.AuthorId;
-import org.briarproject.api.identity.LocalAuthor;
+import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.sync.ClientId;
 import org.briarproject.api.sync.Group;
 import org.briarproject.api.sync.GroupId;
@@ -25,11 +25,9 @@ import org.briarproject.util.StringUtils;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.logging.Logger;
 
@@ -45,8 +43,6 @@ import static org.briarproject.api.forum.ForumConstants.KEY_PUBLIC_NAME;
 import static org.briarproject.api.forum.ForumConstants.KEY_READ;
 import static org.briarproject.api.forum.ForumConstants.KEY_TIMESTAMP;
 import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
-import static org.briarproject.api.identity.Author.Status.UNKNOWN;
-import static org.briarproject.api.identity.Author.Status.VERIFIED;
 
 class ForumManagerImpl implements ForumManager {
 
@@ -58,15 +54,17 @@ class ForumManagerImpl implements ForumManager {
 					+ "795af837abbf8c16d750b3c2ccc186ea"));
 
 	private final DatabaseComponent db;
+	private final IdentityManager identityManager;
 	private final ClientHelper clientHelper;
 	private final ForumFactory forumFactory;
 	private final List<RemoveForumHook> removeHooks;
 
 	@Inject
-	ForumManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
-			ForumFactory forumFactory) {
+	ForumManagerImpl(DatabaseComponent db, IdentityManager identityManager,
+			ClientHelper clientHelper, ForumFactory forumFactory) {
 
 		this.db = db;
+		this.identityManager = identityManager;
 		this.clientHelper = clientHelper;
 		this.forumFactory = forumFactory;
 		removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
@@ -183,24 +181,12 @@ class ForumManagerImpl implements ForumManager {
 	@Override
 	public Collection<ForumPostHeader> getPostHeaders(GroupId g)
 			throws DbException {
-		Set<AuthorId> localAuthorIds = new HashSet<AuthorId>();
-		Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>();
+
 		Map<MessageId, BdfDictionary> metadata;
-		Transaction txn = db.startTransaction(true);
 		try {
-			// Load the IDs of the user's identities
-			for (LocalAuthor a : db.getLocalAuthors(txn))
-				localAuthorIds.add(a.getId());
-			// Load the IDs of contacts' identities
-			for (Contact c : db.getContacts(txn))
-				contactAuthorIds.add(c.getAuthor().getId());
-			// Load the metadata
-			metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
-			txn.setComplete();
+			metadata = clientHelper.getMessageMetadataAsDictionary(g);
 		} catch (FormatException e) {
 			throw new DbException(e);
-		} finally {
-			db.endTransaction(txn);
 		}
 		// Parse the metadata
 		Collection<ForumPostHeader> headers = new ArrayList<ForumPostHeader>();
@@ -209,7 +195,7 @@ class ForumManagerImpl implements ForumManager {
 				BdfDictionary meta = entry.getValue();
 				long timestamp = meta.getLong(KEY_TIMESTAMP);
 				Author author = null;
-				Author.Status authorStatus = ANONYMOUS;
+				Status authorStatus = ANONYMOUS;
 				MessageId parentId = null;
 				if (meta.containsKey(KEY_PARENT))
 					parentId = new MessageId(meta.getRaw(KEY_PARENT));
@@ -219,11 +205,8 @@ class ForumManagerImpl implements ForumManager {
 					String name = d1.getString(KEY_NAME);
 					byte[] publicKey = d1.getRaw(KEY_PUBLIC_NAME);
 					author = new Author(authorId, name, publicKey);
-					if (localAuthorIds.contains(authorId))
-						authorStatus = VERIFIED;
-					else if (contactAuthorIds.contains(authorId))
-						authorStatus = VERIFIED;
-					else authorStatus = UNKNOWN;
+					authorStatus =
+							identityManager.getAuthorStatus(author.getId());
 				}
 				String contentType = meta.getString(KEY_CONTENT_TYPE);
 				boolean read = meta.getBoolean(KEY_READ);
diff --git a/briar-core/src/org/briarproject/forum/ForumModule.java b/briar-core/src/org/briarproject/forum/ForumModule.java
index e7494850e2f95c88aa851ec6abc60d8d4ad770f5..59f56af2acf07df4a8595430239f8cf8f541cc13 100644
--- a/briar-core/src/org/briarproject/forum/ForumModule.java
+++ b/briar-core/src/org/briarproject/forum/ForumModule.java
@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumManager;
 import org.briarproject.api.forum.ForumPostFactory;
 import org.briarproject.api.forum.ForumSharingManager;
 import org.briarproject.api.identity.AuthorFactory;
+import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.lifecycle.LifecycleManager;
 import org.briarproject.api.sync.GroupFactory;
 import org.briarproject.api.sync.ValidationManager;
@@ -38,9 +39,8 @@ public class ForumModule {
 
 	@Provides
 	@Singleton
-	ForumManager provideForumManager(DatabaseComponent db,
-			ClientHelper clientHelper, ForumFactory forumFactory) {
-		return new ForumManagerImpl(db, clientHelper, forumFactory);
+	ForumManager provideForumManager(ForumManagerImpl forumManager) {
+		return forumManager;
 	}
 
 	@Provides
@@ -88,7 +88,7 @@ public class ForumModule {
 			LifecycleManager lifecycleManager,
 			ContactManager contactManager,
 			MessageQueueManager messageQueueManager,
-			ForumManager forumManager, ForumFactory forumFactory,
+			ForumManager forumManager,
 			ForumSharingManagerImpl forumSharingManager) {
 
 		lifecycleManager.registerClient(forumSharingManager);
diff --git a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
index b3d7a1f4d8213f8427963457ff8a3d80b8c91a0f..e04e206020edd797cf1da082d7b075df9a7156c3 100644
--- a/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
+++ b/briar-core/src/org/briarproject/identity/IdentityManagerImpl.java
@@ -1,18 +1,25 @@
 package org.briarproject.identity;
 
+import org.briarproject.api.contact.Contact;
 import org.briarproject.api.db.DatabaseComponent;
 import org.briarproject.api.db.DbException;
 import org.briarproject.api.db.Transaction;
+import org.briarproject.api.identity.Author.Status;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.identity.IdentityManager;
 import org.briarproject.api.identity.LocalAuthor;
 
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 import javax.inject.Inject;
 
+import static org.briarproject.api.identity.Author.Status.UNKNOWN;
+import static org.briarproject.api.identity.Author.Status.VERIFIED;
+
 class IdentityManagerImpl implements IdentityManager {
 	private final DatabaseComponent db;
 	private final List<AddIdentityHook> addHooks;
@@ -87,4 +94,22 @@ class IdentityManagerImpl implements IdentityManager {
 			db.endTransaction(txn);
 		}
 	}
+
+	@Override
+	public Status getAuthorStatus(AuthorId authorId) throws DbException {
+		Transaction txn = db.startTransaction(false);
+		try {
+			// Compare to the IDs of the user's identities
+			for (LocalAuthor a : db.getLocalAuthors(txn))
+				if (a.getId().equals(authorId)) return VERIFIED;
+			// Compare to the IDs of contacts' identities
+			for (Contact c : db.getContacts(txn))
+				if (c.getAuthor().getId().equals(authorId)) return VERIFIED;
+
+			// TODO also handle UNVERIFIED when #261 is implemented
+			return UNKNOWN;
+		} finally {
+			db.endTransaction(txn);
+		}
+	}
 }