diff --git a/core/lib/Drupal/Core/TempStore/TempStore.php b/core/lib/Drupal/Core/TempStore/TempStore.php
new file mode 100644
index 0000000..708d13e
--- /dev/null
+++ b/core/lib/Drupal/Core/TempStore/TempStore.php
@@ -0,0 +1,212 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\TempStore\TempStore.
+ */
+
+namespace Drupal\Core\TempStore;
+
+use Exception;
+
+/**
+ * Handles reading and writing to a non-volatile temporary storage area.
+ *
+ * A TempStore is not a true cache, because it is non-volatile. While a cache
+ * can be reconstructed if the data disappears (i.e, a backend goes away
+ * or a cache is cleared), TempStore cannot tolerate the data disappearing.
+ *
+ * It is primarily used to handle in-progress edits on complicated objects
+ * in order to provide state to an ordinarily stateless HTTP transaction.
+ *
+ */
+class TempStore {
+
+  /**
+   * The subsystem or module that owns this TempStore.
+   *
+   * @var string
+   */
+  protected $subsystem;
+
+  /**
+   * The unique identifier for the owner of the temporary data.
+   *
+   * In order to ensure that users do not accidentally acquire each other's
+   * changes, session IDs can be used to differentiate them. However, there
+   * are cases where session IDs are not ideal. In these cases, an
+   * alternative ID can be set (such as a user ID or the number 0) which
+   * would indicate no special session handling is required.
+   *
+   * @var string
+   */
+  protected $sid;
+
+  /**
+   * Whether or not the $sid is actually a session ID or some other identifier.
+   *
+   * @var bool
+   */
+  public $useSessionTable;
+
+  /**
+   * Constructs a TempStore interaction object.
+   *
+   * @param string $subsystem
+   *   The module or subsystem. Possible values might include 'entity',
+   *   'form', 'views', etc.
+   *
+   * @param string $sid
+   *   (optional) A custom unique identifier for the owner of the TempStore
+   *   data. If no custom identifier is provided, the current session ID is
+   *   used. Defaults to NULL.
+   */
+  function __construct($subsystem, $sid = NULL) {
+    $this->subsystem = $subsystem;
+
+    if (!isset($sid)) {
+      $this->sid = session_id();
+      $this->useSessionTable = TRUE;
+    }
+    else {
+      $this->sid = $sid;
+      $this->useSessionTable = FALSE;
+    }
+  }
+
+  /**
+   * Fetches the data from the store.
+   *
+   * @param string $key
+   *   The key to the stored object. See TempStore::set() for details.
+   *
+   * @return object
+   *   The stored data.
+   */
+  function get($key) {
+    // @todo We may wish to add static caching here in the future, either
+    //   as a static cache on the object or using drupal_static().
+    $data = db_query(
+      'SELECT * FROM {temp_store} WHERE sid = :session_id AND subsystem = :subsystem AND key = :key',
+      array(
+        ':session_id' => $this->sid,
+        ':subsystem' => $this->subsystem,
+        ':key' => $key,
+      )
+    )
+    ->fetchObject();
+    if ($data) {
+      return unserialize($data->data);
+    }
+  }
+
+  /**
+   * Writes the data to the store.
+   *
+   * @param string $key
+   *   The key to the object being stored. For objects that already exist in
+   *   the database somewhere else, this is typically the primary key of that
+   *   object. For objects that do not already exist, this is typically 'new'
+   *   or some special key that indicates that the object does not yet exist.
+   * @param mixed $data
+   *   The data to be cached. It will be serialized.
+   *
+   * @todo
+   *   Using 'new' as a key might result in collisions if the same user tries
+   *   to create multiple new objects simultaneously. Document a workaround?
+   */
+  function set($key, $data) {
+    // Ensure that a session cookie is set for anonymous users.
+    if (!user_is_logged_in()) {
+      // A session is written so long as $_SESSION is not empty. Force this.
+      // @todo This feels really hacky. Is there a better way?
+      // @see http://drupalcode.org/project/ctools.git/blob/refs/heads/8.x-1.x:/includes/object-cache.inc#l69
+      $_SESSION['temp_store_use_session'] = TRUE;
+    }
+
+    // Clear any existing data for this key.
+    $this->delete($key);
+
+    // Store the new data.
+    db_insert('temp_store')
+      ->fields(array(
+        'sid' => $this->sid,
+        'subsystem' => $this->subsystem,
+        'key' => $key,
+        'data' => serialize($data),
+        'updated' => REQUEST_TIME,
+      ))
+      ->execute();
+  }
+
+  /**
+   * Removes one or more objects from this store for this session.
+   *
+   * @param string|array $key
+   *   The key to the stored object, or an array of keys.  See
+   *   TempStore::set() for details.
+   */
+  function delete($key) {
+    $this->deleteRecords($key);
+  }
+
+  /**
+   * Removes one or more objects from this store for all sessions.
+   *
+   * @param string|array $key
+   *   The key to the stored object, or an array of keys.  See
+   *   TempStore::set() for details.
+   */
+  function deleteAll($key) {
+    $this->deleteRecords($key, TRUE);
+  }
+
+  /**
+   * Deletes database records for objects.
+   *
+   * @param string|array $key
+   *   The key to the stored object, or an array of keys. See
+   *   TempStore::set() for details.
+   * @param bool $all
+   *   Whether to delete all records for this key (TRUE) or just the current
+   *   owner's (FALSE). Defaults to FALSE.
+   */
+  protected function deleteRecords($key, $all = FALSE) {
+    // The query builder will automatically use an IN condition when an array
+    // is passed.
+    $query = db_delete('temp_store')
+      ->condition('key', $key)
+      ->condition('subsystem', $this->subsystem);
+
+    if (!$all) {
+      $query->condition('sid', $this->sid);
+    }
+
+    $query->execute();
+
+    // @todo If we add static caching to get(), we will need to invalidate
+    //   that cache here as appropriate.
+  }
+
+  /**
+   * Determines if the object is in use by another store for locking purposes.
+   *
+   * @param string $key
+   *   The key to the stored object.  See TempStore::set() for details.
+   *
+   * @return string|null
+   *   The user ID of the data owner if it is in use, or NULL otherwise.
+   */
+  function getLockOwner($key) {
+    return db_query(
+      'SELECT s.uid, t.updated FROM {temp_store} t INNER JOIN {sessions} s ON t.sid = s.sid WHERE s.sid <> :session_id AND t.subsystem = :subsystem AND t.key = :key ORDER BY t.updated ASC',
+      array(
+        ':session_id' => $this->sid,
+        ':subsystem' => $this->subsystem,
+        ':key' => $key,
+      )
+    )
+    ->fetchObject();
+  }
+
+}
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 9ba7c4c..7b9727e 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1373,6 +1373,45 @@ function system_schema() {
     'primary key' => array('mlid'),
   );
 
+  $schema['temp_store'] = array(
+    'description' => t('A special cache used to store objects that are being edited; it serves to save state in an ordinarily stateless environment.'),
+    'fields' => array(
+      'sid' => array(
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => TRUE,
+        'description' => 'The session ID this object belongs to.',
+      ),
+      'subsystem' => array(
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => TRUE,
+        'description' => 'The type of the object this cache is attached to; this essentially represents the owner so that several sub-systems can use this cache.',
+      ),
+      'key' => array(
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => TRUE,
+        'description' => 'The key of the object this cache is attached to.',
+      ),
+      'updated' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The time this cache was created or updated.',
+      ),
+      'data' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Serialized data being stored.',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('sid', 'subsystem', 'key'),
+    'indexes' => array('updated' => array('updated')),
+  );
+
   $schema['queue'] = array(
     'description' => 'Stores items in queues.',
     'fields' => array(
@@ -1911,6 +1950,52 @@ function system_update_8009() {
   ));
 }
 
+/*
+ * Create the 'temp_store' table.
+ */
+function system_update_8010() {
+  $table = array(
+    'description' => t('A special cache used to store objects that are being edited; it serves to save state in an ordinarily stateless environment.'),
+    'fields' => array(
+      'sid' => array(
+        'type' => 'varchar',
+        'length' => '64',
+        'not null' => TRUE,
+        'description' => 'The session ID this object belongs to.',
+      ),
+      'subsystem' => array(
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => TRUE,
+        'description' => 'The type of the object this cache is attached to; this essentially represents the owner so that several sub-systems can use this cache.',
+      ),
+      'key' => array(
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => TRUE,
+        'description' => 'The key of the object this cache is attached to.',
+      ),
+      'updated' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The time this cache was created or updated.',
+      ),
+      'data' => array(
+        'type' => 'text',
+        'size' => 'big',
+        'description' => 'Serialized data being stored.',
+        'serialize' => TRUE,
+      ),
+    ),
+    'primary key' => array('sid', 'subsystem', 'key'),
+    'indexes' => array('updated' => array('updated')),
+  );
+
+  db_create_table('temp_store', $table);
+}
+
 /**
  * Moves RSS system settings from variable to config.
  *
