diff --git a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
index 17a6de0f7..ff06173f5 100644
--- a/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitTokenEditController.php
@@ -1,100 +1,109 @@
 <?php
 
 final class PhabricatorConduitTokenEditController
   extends PhabricatorConduitController {
 
   public function handleRequest(AphrontRequest $request) {
     $viewer = $request->getViewer();
 
     $id = $request->getURIData('id');
     if ($id) {
       $token = id(new PhabricatorConduitTokenQuery())
         ->setViewer($viewer)
         ->withIDs(array($id))
         ->withExpired(false)
         ->requireCapabilities(
           array(
             PhabricatorPolicyCapability::CAN_VIEW,
             PhabricatorPolicyCapability::CAN_EDIT,
           ))
         ->executeOne();
       if (!$token) {
         return new Aphront404Response();
       }
 
       $object = $token->getObject();
 
       $is_new = false;
       $title = pht('View API Token');
     } else {
       $object = id(new PhabricatorObjectQuery())
         ->setViewer($viewer)
         ->withPHIDs(array($request->getStr('objectPHID')))
         ->requireCapabilities(
           array(
             PhabricatorPolicyCapability::CAN_VIEW,
             PhabricatorPolicyCapability::CAN_EDIT,
           ))
         ->executeOne();
       if (!$object) {
         return new Aphront404Response();
       }
 
       $token = PhabricatorConduitToken::initializeNewToken(
         $object->getPHID(),
         PhabricatorConduitToken::TYPE_STANDARD);
 
       $is_new = true;
       $title = pht('Generate API Token');
       $submit_button = pht('Generate Token');
     }
 
     if ($viewer->getPHID() == $object->getPHID()) {
       $panel_uri = '/settings/panel/apitokens/';
     } else {
       $panel_uri = '/settings/'.$object->getID().'/panel/apitokens/';
     }
 
     id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
       $viewer,
       $request,
       $panel_uri);
 
     if ($request->isFormPost()) {
       $token->save();
 
       if ($is_new) {
         $token_uri = '/conduit/token/edit/'.$token->getID().'/';
       } else {
         $token_uri = $panel_uri;
       }
 
       return id(new AphrontRedirectResponse())->setURI($token_uri);
     }
 
     $dialog = $this->newDialog()
       ->setTitle($title)
       ->addHiddenInput('objectPHID', $object->getPHID());
 
     if ($is_new) {
       $dialog
         ->appendParagraph(pht('Generate a new API token?'))
         ->addSubmitButton($submit_button)
         ->addCancelButton($panel_uri);
     } else {
       $form = id(new AphrontFormView())
-        ->setUser($viewer)
-        ->appendChild(
+        ->setUser($viewer);
+
+      if ($token->getTokenType() === PhabricatorConduitToken::TYPE_CLUSTER) {
+        $dialog->appendChild(
+          pht(
+            'This token is automatically generated by Phabricator, and used '.
+            'to make requests between nodes in a Phabricator cluster. You '.
+            'can not use this token in external applications.'));
+      } else {
+        $form->appendChild(
           id(new AphrontFormTextControl())
             ->setLabel(pht('Token'))
             ->setValue($token->getToken()));
+      }
 
       $dialog
         ->appendForm($form)
         ->addCancelButton($panel_uri, pht('Done'));
     }
 
     return $dialog;
   }
 
 }
diff --git a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
index e0d088627..38a258c4e 100644
--- a/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
+++ b/src/applications/conduit/query/PhabricatorConduitTokenQuery.php
@@ -1,115 +1,128 @@
 <?php
 
 final class PhabricatorConduitTokenQuery
   extends PhabricatorCursorPagedPolicyAwareQuery {
 
   private $ids;
   private $objectPHIDs;
   private $expired;
   private $tokens;
+  private $tokenTypes;
 
   public function withExpired($expired) {
     $this->expired = $expired;
     return $this;
   }
 
   public function withIDs(array $ids) {
     $this->ids = $ids;
     return $this;
   }
 
   public function withObjectPHIDs(array $phids) {
     $this->objectPHIDs = $phids;
     return $this;
   }
 
   public function withTokens(array $tokens) {
     $this->tokens = $tokens;
     return $this;
   }
 
+  public function withTokenTypes(array $types) {
+    $this->tokenTypes = $types;
+    return $this;
+  }
+
   public function loadPage() {
     $table = new PhabricatorConduitToken();
     $conn_r = $table->establishConnection('r');
 
     $data = queryfx_all(
       $conn_r,
       'SELECT * FROM %T %Q %Q %Q',
       $table->getTableName(),
       $this->buildWhereClause($conn_r),
       $this->buildOrderClause($conn_r),
       $this->buildLimitClause($conn_r));
 
     return $table->loadAllFromArray($data);;
   }
 
   private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
     $where = array();
 
     if ($this->ids !== null) {
       $where[] = qsprintf(
         $conn_r,
         'id IN (%Ld)',
         $this->ids);
     }
 
     if ($this->objectPHIDs !== null) {
       $where[] = qsprintf(
         $conn_r,
         'objectPHID IN (%Ls)',
         $this->objectPHIDs);
     }
 
     if ($this->tokens !== null) {
       $where[] = qsprintf(
         $conn_r,
         'token IN (%Ls)',
         $this->tokens);
     }
 
+    if ($this->tokenTypes !== null) {
+      $where[] = qsprintf(
+        $conn_r,
+        'tokenType IN (%Ls)',
+        $this->tokenTypes);
+    }
+
     if ($this->expired !== null) {
       if ($this->expired) {
         $where[] = qsprintf(
           $conn_r,
           'expires <= %d',
           PhabricatorTime::getNow());
       } else {
         $where[] = qsprintf(
           $conn_r,
           'expires IS NULL OR expires > %d',
           PhabricatorTime::getNow());
       }
     }
 
     $where[] = $this->buildPagingClause($conn_r);
 
     return $this->formatWhereClause($where);
   }
 
   protected function willFilterPage(array $tokens) {
     $object_phids = mpull($tokens, 'getObjectPHID');
     $objects = id(new PhabricatorObjectQuery())
       ->setViewer($this->getViewer())
       ->setParentQuery($this)
       ->withPHIDs($object_phids)
       ->execute();
     $objects = mpull($objects, null, 'getPHID');
 
     foreach ($tokens as $key => $token) {
       $object = idx($objects, $token->getObjectPHID(), null);
       if (!$object) {
         $this->didRejectResult($token);
         unset($tokens[$key]);
         continue;
       }
       $token->attachObject($object);
     }
 
     return $tokens;
   }
 
   public function getQueryApplicationClass() {
     return 'PhabricatorConduitApplication';
   }
 
 }
diff --git a/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php b/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
index 40c982477..1e2d1d9d5 100644
--- a/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
+++ b/src/applications/conduit/settings/PhabricatorConduitSettingsPanel.php
@@ -1,116 +1,116 @@
 <?php
 
 final class PhabricatorConduitSettingsPanel
   extends PhabricatorSettingsPanel {
 
   public function isEditableByAdministrators() {
     return true;
   }
 
   public function getPanelKey() {
     return 'apitokens';
   }
 
   public function getPanelName() {
     return pht('Conduit API Tokens');
   }
 
   public function getPanelGroup() {
     return pht('Sessions and Logs');
   }
 
   public function isEnabled() {
     return true;
   }
 
   public function processRequest(AphrontRequest $request) {
     $viewer = $this->getViewer();
     $user = $this->getUser();
 
     $tokens = id(new PhabricatorConduitTokenQuery())
       ->setViewer($viewer)
       ->withObjectPHIDs(array($user->getPHID()))
       ->withExpired(false)
       ->requireCapabilities(
         array(
           PhabricatorPolicyCapability::CAN_VIEW,
           PhabricatorPolicyCapability::CAN_EDIT,
         ))
       ->execute();
 
     $rows = array();
     foreach ($tokens as $token) {
       $rows[] = array(
         javelin_tag(
           'a',
           array(
             'href' => '/conduit/token/edit/'.$token->getID().'/',
             'sigil' => 'workflow',
           ),
-          substr($token->getToken(), 0, 8).'...'),
+          $token->getPublicTokenName()),
         PhabricatorConduitToken::getTokenTypeName($token->getTokenType()),
         phabricator_datetime($token->getDateCreated(), $viewer),
         ($token->getExpires()
           ? phabricator_datetime($token->getExpires(), $viewer)
           : pht('Never')),
         javelin_tag(
           'a',
           array(
             'class' => 'button small grey',
             'href' => '/conduit/token/terminate/'.$token->getID().'/',
             'sigil' => 'workflow',
           ),
           pht('Terminate')),
       );
     }
 
     $table = new AphrontTableView($rows);
     $table->setNoDataString(pht("You don't have any active API tokens."));
     $table->setHeaders(
       array(
         pht('Token'),
         pht('Type'),
         pht('Created'),
         pht('Expires'),
         null,
       ));
     $table->setColumnClasses(
       array(
         'wide pri',
         '',
         'right',
         'right',
         'action',
       ));
 
     $generate_icon = id(new PHUIIconView())
       ->setIconFont('fa-plus');
     $generate_button = id(new PHUIButtonView())
       ->setText(pht('Generate API Token'))
       ->setHref('/conduit/token/edit/?objectPHID='.$user->getPHID())
       ->setTag('a')
       ->setWorkflow(true)
       ->setIcon($generate_icon);
 
     $terminate_icon = id(new PHUIIconView())
       ->setIconFont('fa-exclamation-triangle');
     $terminate_button = id(new PHUIButtonView())
       ->setText(pht('Terminate All Tokens'))
       ->setHref('/conduit/token/terminate/?objectPHID='.$user->getPHID())
       ->setTag('a')
       ->setWorkflow(true)
       ->setIcon($terminate_icon);
 
     $header = id(new PHUIHeaderView())
       ->setHeader(pht('Active API Tokens'))
       ->addActionLink($generate_button)
       ->addActionLink($terminate_button);
 
     $panel = id(new PHUIObjectBoxView())
       ->setHeader($header)
       ->appendChild($table);
 
     return $panel;
   }
 
 }
diff --git a/src/applications/conduit/storage/PhabricatorConduitToken.php b/src/applications/conduit/storage/PhabricatorConduitToken.php
index 8ebcecb16..5c39173c0 100644
--- a/src/applications/conduit/storage/PhabricatorConduitToken.php
+++ b/src/applications/conduit/storage/PhabricatorConduitToken.php
@@ -1,118 +1,165 @@
 <?php
 
 final class PhabricatorConduitToken
   extends PhabricatorConduitDAO
   implements PhabricatorPolicyInterface {
 
   protected $objectPHID;
   protected $tokenType;
   protected $token;
   protected $expires;
 
   private $object = self::ATTACHABLE;
 
   const TYPE_STANDARD = 'api';
-  const TYPE_TEMPORARY = 'tmp';
   const TYPE_COMMANDLINE = 'cli';
+  const TYPE_CLUSTER = 'clr';
 
   public function getConfiguration() {
     return array(
       self::CONFIG_COLUMN_SCHEMA => array(
         'tokenType' => 'text32',
         'token' => 'text32',
         'expires' => 'epoch?',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'key_object' => array(
           'columns' => array('objectPHID', 'tokenType'),
         ),
         'key_token' => array(
           'columns' => array('token'),
           'unique' => true,
         ),
         'key_expires' => array(
           'columns' => array('expires'),
         ),
       ),
     ) + parent::getConfiguration();
   }
 
+  public static function loadClusterTokenForUser(PhabricatorUser $user) {
+    if (!$user->isLoggedIn()) {
+      return null;
+    }
+
+    $tokens = id(new PhabricatorConduitTokenQuery())
+      ->setViewer($user)
+      ->withObjectPHIDs(array($user->getPHID()))
+      ->withTokenTypes(array(self::TYPE_CLUSTER))
+      ->withExpired(false)
+      ->execute();
+
+    // Only return a token if it has at least 5 minutes left before
+    // expiration. Cluster tokens cycle regularly, so we don't want to use
+    // one that's going to expire momentarily.
+    $now = PhabricatorTime::getNow();
+    $must_expire_after = $now + phutil_units('5 minutes in seconds');
+
+    foreach ($tokens as $token) {
+      if ($token->getExpires() > $must_expire_after) {
+        return $token;
+      }
+    }
+
+    // We didn't find any existing tokens (or the existing tokens are all about
+    // to expire) so generate a new token.
+
+    $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+      $token = PhabricatorConduitToken::initializeNewToken(
+        $user->getPHID(),
+        self::TYPE_CLUSTER);
+      $token->save();
+    unset($unguarded);
+
+    return $token;
+  }
+
   public static function initializeNewToken($object_phid, $token_type) {
     $token = new PhabricatorConduitToken();
     $token->objectPHID = $object_phid;
     $token->tokenType = $token_type;
     $token->expires = $token->getTokenExpires($token_type);
 
     $secret = $token_type.'-'.Filesystem::readRandomCharacters(32);
     $secret = substr($secret, 0, 32);
     $token->token = $secret;
 
     return $token;
   }
 
   public static function getTokenTypeName($type) {
     $map = array(
       self::TYPE_STANDARD => pht('Standard API Token'),
-      self::TYPE_TEMPORARY => pht('Temporary API Token'),
       self::TYPE_COMMANDLINE => pht('Command Line API Token'),
+      self::TYPE_CLUSTER => pht('Cluster API Token'),
     );
 
     return idx($map, $type, $type);
   }
 
   public static function getAllTokenTypes() {
     return array(
       self::TYPE_STANDARD,
-      self::TYPE_TEMPORARY,
       self::TYPE_COMMANDLINE,
+      self::TYPE_CLUSTER,
     );
   }
 
   private function getTokenExpires($token_type) {
+    $now = PhabricatorTime::getNow();
     switch ($token_type) {
       case self::TYPE_STANDARD:
         return null;
-      case self::TYPE_TEMPORARY:
-        return PhabricatorTime::getNow() + phutil_units('24 hours in seconds');
       case self::TYPE_COMMANDLINE:
-        return PhabricatorTime::getNow() + phutil_units('1 hour in seconds');
+        return $now + phutil_units('1 hour in seconds');
+      case self::TYPE_CLUSTER:
+        return $now + phutil_units('30 minutes in seconds');
       default:
         throw new Exception(
           pht('Unknown Conduit token type "%s"!', $token_type));
     }
   }
 
+  public function getPublicTokenName() {
+    switch ($this->getTokenType()) {
+      case self::TYPE_CLUSTER:
+        return pht('Cluster API Token');
+      default:
+        return substr($this->getToken(), 0, 8).'...';
+    }
+  }
+
   public function getObject() {
     return $this->assertAttached($this->object);
   }
 
   public function attachObject(PhabricatorUser $object) {
     $this->object = $object;
     return $this;
   }
 
 
 /* -(  PhabricatorPolicyInterface  )----------------------------------------- */
 
 
   public function getCapabilities() {
     return array(
       PhabricatorPolicyCapability::CAN_VIEW,
       PhabricatorPolicyCapability::CAN_EDIT,
     );
   }
 
   public function getPolicy($capability) {
     return $this->getObject()->getPolicy($capability);
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return $this->getObject()->hasAutomaticCapability($capability, $viewer);
   }
 
   public function describeAutomaticCapability($capability) {
     return pht(
       'Conduit tokens inherit the policies of the user they authenticate.');
   }
 
 }
diff --git a/src/applications/diffusion/query/DiffusionQuery.php b/src/applications/diffusion/query/DiffusionQuery.php
index 87714249f..642927c06 100644
--- a/src/applications/diffusion/query/DiffusionQuery.php
+++ b/src/applications/diffusion/query/DiffusionQuery.php
@@ -1,252 +1,256 @@
 <?php
 
 abstract class DiffusionQuery extends PhabricatorQuery {
 
   private $request;
 
   final protected function __construct() {
     // <protected>
   }
 
   protected static function newQueryObject(
     $base_class,
     DiffusionRequest $request) {
 
     $repository = $request->getRepository();
 
     $obj = self::initQueryObject($base_class, $repository);
     $obj->request = $request;
 
     return $obj;
   }
 
   final protected static function initQueryObject(
     $base_class,
     PhabricatorRepository $repository) {
 
     $map = array(
       PhabricatorRepositoryType::REPOSITORY_TYPE_GIT        => 'Git',
       PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL  => 'Mercurial',
       PhabricatorRepositoryType::REPOSITORY_TYPE_SVN        => 'Svn',
     );
 
     $name = idx($map, $repository->getVersionControlSystem());
     if (!$name) {
       throw new Exception('Unsupported VCS!');
     }
 
     $class = str_replace('Diffusion', 'Diffusion'.$name, $base_class);
     $obj = new $class();
     return $obj;
   }
 
   final protected function getRequest() {
     return $this->request;
   }
 
   final public static function callConduitWithDiffusionRequest(
     PhabricatorUser $user,
     DiffusionRequest $drequest,
     $method,
     array $params = array()) {
 
     $repository = $drequest->getRepository();
 
     $core_params = array(
       'callsign' => $repository->getCallsign(),
     );
 
     if ($drequest->getBranch() !== null) {
       $core_params['branch'] = $drequest->getBranch();
     }
 
     $params = $params + $core_params;
 
     $service_phid = $repository->getAlmanacServicePHID();
     if ($service_phid === null) {
       return id(new ConduitCall($method, $params))
         ->setUser($user)
         ->execute();
     }
 
     $service = id(new AlmanacServiceQuery())
       ->setViewer(PhabricatorUser::getOmnipotentUser())
       ->withPHIDs(array($service_phid))
       ->needBindings(true)
       ->executeOne();
     if (!$service) {
       throw new Exception(
         pht(
           'The Alamnac service for this repository is invalid or could not '.
           'be loaded.'));
     }
 
     $bindings = $service->getBindings();
     if (!$bindings) {
       throw new Exception(
         pht(
           'The Alamanc service for this repository is not bound to any '.
           'interfaces.'));
     }
 
     $uris = array();
     foreach ($bindings as $binding) {
       $iface = $binding->getInterface();
 
       $protocol = $binding->getAlmanacPropertyValue('protocol');
       if ($protocol === 'http') {
         $uris[] = 'http://'.$iface->renderDisplayAddress().'/';
       } else if ($protocol === 'https' || $protocol === null) {
         $uris[] = 'https://'.$iface->renderDisplayAddress().'/';
       } else {
         throw new Exception(
           pht(
             'The Almanac service for this repository has a binding to an '.
             'invalid interface with an unknown protocol ("%s").',
             $protocol));
       }
     }
 
     shuffle($uris);
     $uri = head($uris);
 
     $domain = id(new PhutilURI(PhabricatorEnv::getURI('/')))->getDomain();
 
-    // TODO: This call needs authentication, which is blocked by T5955.
+    $client = id(new ConduitClient($uri))
+      ->setHost($domain);
 
-    return id(new ConduitClient($uri))
-      ->setHost($domain)
-      ->callMethodSynchronous($method, $params);
+    $token = PhabricatorConduitToken::loadClusterTokenForUser($user);
+    if ($token) {
+      $client->setConduitToken($token->getToken());
+    }
+
+    return $client->callMethodSynchronous($method, $params);
   }
 
   public function execute() {
     return $this->executeQuery();
   }
 
   abstract protected function executeQuery();
 
 
 /* -(  Query Utilities  )---------------------------------------------------- */
 
 
   final public static function loadCommitsByIdentifiers(
     array $identifiers,
     DiffusionRequest $drequest) {
     if (!$identifiers) {
       return array();
     }
 
     $commits = array();
     $commit_data = array();
 
     $repository = $drequest->getRepository();
 
     $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
       'repositoryID = %d AND commitIdentifier IN (%Ls)',
         $repository->getID(),
       $identifiers);
     $commits = mpull($commits, null, 'getCommitIdentifier');
 
     // Build empty commit objects for every commit, so we can show unparsed
     // commits in history views (as "Importing") instead of not showing them.
     // This makes the process of importing and parsing commits clearer to the
     // user.
 
     $commit_list = array();
     foreach ($identifiers as $identifier) {
       $commit_obj = idx($commits, $identifier);
       if (!$commit_obj) {
         $commit_obj = new PhabricatorRepositoryCommit();
         $commit_obj->setRepositoryID($repository->getID());
         $commit_obj->setCommitIdentifier($identifier);
         $commit_obj->makeEphemeral();
       }
       $commit_list[$identifier] = $commit_obj;
     }
     $commits = $commit_list;
 
     $commit_ids = array_filter(mpull($commits, 'getID'));
     if ($commit_ids) {
       $commit_data = id(new PhabricatorRepositoryCommitData())->loadAllWhere(
         'commitID in (%Ld)',
         $commit_ids);
       $commit_data = mpull($commit_data, null, 'getCommitID');
     }
 
     foreach ($commits as $commit) {
       if (!$commit->getID()) {
         continue;
       }
       if (idx($commit_data, $commit->getID())) {
         $commit->attachCommitData($commit_data[$commit->getID()]);
       }
     }
 
     return $commits;
   }
 
   final public static function loadHistoryForCommitIdentifiers(
     array $identifiers,
     DiffusionRequest $drequest) {
 
     if (!$identifiers) {
       return array();
     }
 
     $repository = $drequest->getRepository();
     $commits = self::loadCommitsByIdentifiers($identifiers, $drequest);
 
     if (!$commits) {
       return array();
     }
 
     $path = $drequest->getPath();
 
     $conn_r = $repository->establishConnection('r');
 
     $path_normal = DiffusionPathIDQuery::normalizePath($path);
     $paths = queryfx_all(
       $conn_r,
       'SELECT id, path FROM %T WHERE pathHash IN (%Ls)',
       PhabricatorRepository::TABLE_PATH,
       array(md5($path_normal)));
     $paths = ipull($paths, 'id', 'path');
     $path_id = idx($paths, $path_normal);
 
     $commit_ids = array_filter(mpull($commits, 'getID'));
 
     $path_changes = array();
     if ($path_id && $commit_ids) {
       $path_changes = queryfx_all(
         $conn_r,
         'SELECT * FROM %T WHERE commitID IN (%Ld) AND pathID = %d',
         PhabricatorRepository::TABLE_PATHCHANGE,
         $commit_ids,
         $path_id);
       $path_changes = ipull($path_changes, null, 'commitID');
     }
 
     $history = array();
     foreach ($identifiers as $identifier) {
       $item = new DiffusionPathChange();
       $item->setCommitIdentifier($identifier);
       $commit = idx($commits, $identifier);
       if ($commit) {
         $item->setCommit($commit);
         try {
           $item->setCommitData($commit->getCommitData());
         } catch (Exception $ex) {
           // Ignore, commit just doesn't have data.
         }
         $change = idx($path_changes, $commit->getID());
         if ($change) {
           $item->setChangeType($change['changeType']);
           $item->setFileType($change['fileType']);
         }
       }
       $history[] = $item;
     }
 
     return $history;
   }
 }