diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -79,7 +79,7 @@
     'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
     'rsrc/css/application/paste/paste.css' => 'aa1767d1',
     'rsrc/css/application/people/people-profile.css' => 'ba7b2762',
-    'rsrc/css/application/phame/phame.css' => '450826e1',
+    'rsrc/css/application/phame/phame.css' => '19ecc703',
     'rsrc/css/application/pholio/pholio-edit.css' => 'b9e59b6d',
     'rsrc/css/application/pholio/pholio-inline-comments.css' => '52be33f0',
     'rsrc/css/application/pholio/pholio.css' => '2fa97dbe',
@@ -734,7 +734,7 @@
     'phabricator-uiexample-reactor-sendclass' => 'bf97561d',
     'phabricator-uiexample-reactor-sendproperties' => '551add57',
     'phabricator-zindex-css' => '0d89d53c',
-    'phame-css' => '450826e1',
+    'phame-css' => '19ecc703',
     'pholio-css' => '2fa97dbe',
     'pholio-edit-css' => 'b9e59b6d',
     'pholio-inline-comments-css' => '52be33f0',
diff --git a/src/applications/phame/controller/PhameController.php b/src/applications/phame/controller/PhameController.php
--- a/src/applications/phame/controller/PhameController.php
+++ b/src/applications/phame/controller/PhameController.php
@@ -1,8 +1,5 @@
 <?php
 
-/**
- * @group phame
- */
 abstract class PhameController extends PhabricatorController {
 
   protected function renderSideNavFilterView() {
@@ -32,7 +29,7 @@
 
   protected function renderPostList(
     array $posts,
-    PhabricatorUser $user,
+    PhabricatorUser $viewer,
     $nodata) {
     assert_instances_of($posts, 'PhamePost');
 
@@ -77,15 +74,33 @@
         ->setImage($bloggerImage)
         ->setImageHref($bloggerURI)
         ->setAppIcon('phame-dark')
-        ->setUser($user)
+        ->setUser($viewer)
         ->setPontification($phame_post, $phame_title);
 
+      if (PhabricatorPolicyFilter::hasCapability(
+        $viewer,
+        $post,
+        PhabricatorPolicyCapability::CAN_EDIT)) {
+
+        $story->addAction(id(new PHUIIconView())
+          ->setHref($this->getApplicationURI('post/edit/'.$post->getID().'/'))
+          ->setText(pht('Edit'))
+          ->setIconFont('fa-pencil'));
+      }
+
       if ($post->getDatePublished()) {
         $story->setEpoch($post->getDatePublished());
       }
+
       $stories[] = $story;
     }
 
+    if (empty($stories)) {
+      return id(new AphrontErrorView())
+        ->setSeverity(AphrontErrorView::SEVERITY_NODATA)
+        ->appendChild($nodata);
+    }
+
     return $stories;
   }
 
diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php
--- a/src/applications/phame/controller/blog/PhameBlogEditController.php
+++ b/src/applications/phame/controller/blog/PhameBlogEditController.php
@@ -1,8 +1,5 @@
 <?php
 
-/**
- * @group phame
- */
 final class PhameBlogEditController
   extends PhameController {
 
@@ -66,19 +63,28 @@
       $blog->setDescription($description);
       $blog->setDomain(nonempty($custom_domain, null));
       $blog->setSkin($skin);
+      $blog->setViewPolicy($request->getStr('can_view'));
+      $blog->setEditPolicy($request->getStr('can_edit'));
+      $blog->setJoinPolicy($request->getStr('can_join'));
 
       if (!empty($custom_domain)) {
-        $error = $blog->validateCustomDomain($custom_domain);
-        if ($error) {
-          $errors[] = $error;
-          $e_custom_domain = pht('Invalid');
+        list($error_label, $error_text) =
+          $blog->validateCustomDomain($custom_domain);
+        if ($error_label) {
+          $errors[] = $error_text;
+          $e_custom_domain = $error_label;
+        }
+        if ($blog->getJoinPolicy() != PhabricatorPolicies::POLICY_PUBLIC) {
+          $errors[] = pht(
+            'For custom domains to work, the blog must have a view policy of '.
+            'public.');
+          // Prefer earlier labels for the multiple error scenario.
+          if (!$e_custom_domain) {
+            $e_custom_domain = pht('Invalid Policy');
+          }
         }
       }
 
-      $blog->setViewPolicy($request->getStr('can_view'));
-      $blog->setEditPolicy($request->getStr('can_edit'));
-      $blog->setJoinPolicy($request->getStr('can_join'));
-
       // Don't let users remove their ability to edit blogs.
       PhabricatorPolicyFilter::mustRetainCapability(
         $user,
diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php
--- a/src/applications/phame/storage/PhameBlog.php
+++ b/src/applications/phame/storage/PhameBlog.php
@@ -1,8 +1,5 @@
 <?php
 
-/**
- * @group phame
- */
 final class PhameBlog extends PhameDAO
   implements PhabricatorPolicyInterface, PhabricatorMarkupInterface {
 
@@ -69,38 +66,59 @@
    */
   public function validateCustomDomain($custom_domain) {
     $example_domain = 'blog.example.com';
+    $label = pht('Invalid');
 
     // note this "uri" should be pretty busted given the desired input
     // so just use it to test if there's a protocol specified
     $uri = new PhutilURI($custom_domain);
     if ($uri->getProtocol()) {
-      return pht(
-        'The custom domain should not include a protocol. Just provide '.
-        'the bare domain name (for example, "%s").',
-        $example_domain);
+      return array($label,
+        pht(
+          'The custom domain should not include a protocol. Just provide '.
+          'the bare domain name (for example, "%s").',
+          $example_domain));
     }
 
     if ($uri->getPort()) {
-      return pht(
-        'The custom domain should not include a port number. Just provide '.
-        'the bare domain name (for example, "%s").',
-        $example_domain);
+      return array($label,
+        pht(
+          'The custom domain should not include a port number. Just provide '.
+          'the bare domain name (for example, "%s").',
+          $example_domain));
     }
 
     if (strpos($custom_domain, '/') !== false) {
-      return pht(
-        'The custom domain should not specify a path (hosting a Phame '.
-        'blog at a path is currently not supported). Instead, just provide '.
-        'the bare domain name (for example, "%s").',
-        $example_domain);
+      return array($label,
+        pht(
+          'The custom domain should not specify a path (hosting a Phame '.
+          'blog at a path is currently not supported). Instead, just provide '.
+          'the bare domain name (for example, "%s").',
+          $example_domain));
     }
 
     if (strpos($custom_domain, '.') === false) {
-      return pht(
-        'The custom domain should contain at least one dot (.) because '.
-        'some browsers fail to set cookies on domains without a dot. Instead, '.
-        'use a normal looking domain name like "%s".',
-        $example_domain);
+      return array($label,
+        pht(
+          'The custom domain should contain at least one dot (.) because '.
+          'some browsers fail to set cookies on domains without a dot. '.
+          'Instead, use a normal looking domain name like "%s".',
+          $example_domain));
+    }
+
+    if (!PhabricatorEnv::getEnvConfig('policy.allow-public')) {
+      $href = PhabricatorEnv::getProductionURI(
+        '/config/edit/policy.allow-public/');
+      return array(pht('Fix Configuration'),
+        pht(
+          'For custom domains to work, this Phabricator instance must be '.
+          'configured to allow the public access policy. Configure this '.
+          'setting %s, or ask an administrator to configure this setting. '.
+          'The domain can be specified later once this setting has been '.
+          'changed.',
+          phutil_tag(
+            'a',
+            array('href' => $href),
+            pht('here'))));
     }
 
     return null;
diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php
--- a/src/applications/phame/storage/PhamePost.php
+++ b/src/applications/phame/storage/PhamePost.php
@@ -1,8 +1,5 @@
 <?php
 
-/**
- * @group phame
- */
 final class PhamePost extends PhameDAO
   implements
     PhabricatorPolicyInterface,
@@ -59,6 +56,10 @@
     return PhabricatorEnv::getProductionURI($uri);
   }
 
+  public function getEditURI() {
+    return '/phame/post/edit/'.$this->getID().'/';
+  }
+
   public function isDraft() {
     return $this->getVisibility() == self::VISIBILITY_DRAFT;
   }
diff --git a/src/docs/user/userguide/phame.diviner b/src/docs/user/userguide/phame.diviner
--- a/src/docs/user/userguide/phame.diviner
+++ b/src/docs/user/userguide/phame.diviner
@@ -29,16 +29,22 @@
 Each blogger can also edit metadata about the blog and delete the blog
 outright.
 
-Soon, blogs will be useful for powering external websites, like
+NOTE: removing a blogger from a given blog does not remove their posts that
+are already associated with the blog. Rather, it removes their ability to edit
+metadata about and add posts to the blog.
+
+Blogs can be useful for powering external websites, like
 
   blog.yourcompany.com
 
 by making pertinent configuration changes with your DNS authority and
-Phabricator instance.
+Phabricator instance. For the Phabricator instance, you must
 
-NOTE: removing a blogger from a given blog does not remove their posts that
-are already associated with the blog. Rather, it removes their ability to edit
-metadata about and add posts to the blog.
+ - Enable `policy.allow-public` in Phabricator configuration.
+ - Configure the blog to have the view policy `public`.
+
+For your DNS authority, simply point the pertinent domain name at your
+Phabricator instance. e.g. by IP address.
 
 = Comment Widgets =
 
diff --git a/webroot/rsrc/css/application/phame/phame.css b/webroot/rsrc/css/application/phame/phame.css
--- a/webroot/rsrc/css/application/phame/phame.css
+++ b/webroot/rsrc/css/application/phame/phame.css
@@ -25,6 +25,14 @@
   max-width: 600px;
 }
 
+.phame-post-list .aphront-error-view {
+  margin: 0;
+}
+
+.phame-post-list .phui-icon-view:hover {
+  text-decoration: none;
+}
+
 .blog-post-list {
   clear: left;
   float: left;