diff --git a/core/modules/page/config/page.front_page.yml b/core/modules/page/config/page.front_page.yml
new file mode 100644
index 0000000..97f0dda
--- /dev/null
+++ b/core/modules/page/config/page.front_page.yml
@@ -0,0 +1,7 @@
+id: front_page
+uuid: 5da81acf-3264-4d91-9b00-b2461c86e974
+label: 'Front page'
+visibility: '1'
+paths: '<front>'
+layout: '1'
+langcode: und
diff --git a/core/modules/page/config/page.not_admin_page.yml b/core/modules/page/config/page.not_admin_page.yml
new file mode 100644
index 0000000..89e9dc9
--- /dev/null
+++ b/core/modules/page/config/page.not_admin_page.yml
@@ -0,0 +1,7 @@
+id: not_admin_page
+uuid: 45e82840-d11b-4726-8d09-9be84a69457d
+label: 'Not admin page'
+visibility: '0'
+paths: "admin\r\nadmin/*"
+layout: '2'
+langcode: und
diff --git a/core/modules/page/config/page.user_page.yml b/core/modules/page/config/page.user_page.yml
new file mode 100644
index 0000000..643a916
--- /dev/null
+++ b/core/modules/page/config/page.user_page.yml
@@ -0,0 +1,7 @@
+id: user_page
+uuid: ccd213ff-54d4-4f3a-9a00-f8406842dff0
+label: 'User page'
+visibility: '1'
+paths: "user\r\nuser/*"
+layout: '0'
+langcode: und
diff --git a/core/modules/page/lib/Drupal/page/Page.php b/core/modules/page/lib/Drupal/page/Page.php
new file mode 100644
index 0000000..21a71e3
--- /dev/null
+++ b/core/modules/page/lib/Drupal/page/Page.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\page\Page.
+ */
+
+namespace Drupal\page;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+
+/**
+ * Defines the page entity.
+ */
+class Page extends ConfigEntityBase {
+
+  /**
+   * The page ID (machine name).
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The page UUID.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * The page label.
+   *
+   * @var string
+   */
+  public $label;
+
+  /**
+   * Whether to apply the path patterns or exclude them.
+   *
+   * @var $int
+   */
+  public $visibility;
+
+  /**
+   * Path pattern associated with this page.
+   *
+   * @var $string
+   */
+  public $paths;
+
+  /**
+   * Layout machine name associated with this page.
+   *
+   * @var $string
+   */
+  public $layout;
+
+}
diff --git a/core/modules/page/lib/Drupal/page/PageFormController.php b/core/modules/page/lib/Drupal/page/PageFormController.php
new file mode 100644
index 0000000..74e0c88
--- /dev/null
+++ b/core/modules/page/lib/Drupal/page/PageFormController.php
@@ -0,0 +1,105 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\page\PageFormController.
+ */
+
+namespace Drupal\page;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityFormController;
+
+/**
+ * Form controller for the page edit/add forms.
+ */
+class PageFormController extends EntityFormController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::form().
+   */
+  public function form(array $form, array &$form_state, EntityInterface $page) {
+    $form['label'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Label'),
+      '#maxlength' => 255,
+      '#default_value' => $page->label(),
+      '#description' => t("Example: 'Front page' or 'Section page'."),
+      '#required' => TRUE,
+    );
+    $form['id'] = array(
+      '#type' => 'machine_name',
+      '#default_value' => $page->id(),
+      '#machine_name' => array(
+        'exists' => 'page_load',
+        'source' => array('label'),
+      ),
+      '#disabled' => (bool) $page->id(),
+    );
+
+    // @todo make this actually rely on layouts from the patch at
+    // http://drupal.org/node/1787846 when committed.
+    $form['layout'] = array(
+      '#type' => 'select',
+      '#title' => t('Layout for this page'),
+      '#default_value' => isset($page->layout) ? $page->layout : 'foo',
+      '#options' => array('foo', 'bar', 'baz'),
+    );
+
+    // @todo this would ideally be pluggable and depend on general conditions
+    // and all, however, these are not yet abstracted.
+    $options = array(
+      0 => t('All pages except those listed'),
+      1 => t('Only the listed pages'),
+    );
+    $description = t("Enter one path per line. The '*' character is a wildcard. Example paths are %user for the current user's page and %user-wildcard for every user page. %front is the front page.", array('%user' => 'user', '%user-wildcard' => 'user/*', '%front' => '<front>'));
+    $form['visibility'] = array(
+      '#type' => 'radios',
+      '#title' => t('Apply to specific paths'),
+      '#options' => $options,
+      '#default_value' => isset($page->visibility) ? $page->visibility : 0,
+    );
+    $form['paths'] = array(
+      '#type' => 'textarea',
+      '#title' => '<span class="element-invisible">' . t('Paths') . '</span>',
+      '#default_value' => isset($page->paths) ? $page->paths : '',
+      '#description' => $description,
+    );
+
+    return parent::form($form, $form_state, $page);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::actions().
+   */
+  protected function actions(array $form, array &$form_state) {
+    // Only includes a Save action for the entity, no direct Delete button.
+    return array(
+      'submit' => array(
+        '#value' => t('Save'),
+        '#validate' => array(
+          array($this, 'validate'),
+        ),
+        '#submit' => array(
+          array($this, 'submit'),
+          array($this, 'save'),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityFormController::save().
+   */
+  public function save(array $form, array &$form_state) {
+    $page = $this->getEntity($form_state);
+    $page->save();
+
+    watchdog('page', 'Page @label saved.', array('@label' => $page->label()), WATCHDOG_NOTICE);
+    drupal_set_message(t('Page %label saved.', array('%label' => $page->label())));
+
+    $form_state['redirect'] = 'admin/structure/pages';
+  }
+
+}
+
diff --git a/core/modules/page/lib/Drupal/page/PageListController.php b/core/modules/page/lib/Drupal/page/PageListController.php
new file mode 100644
index 0000000..4c8f4fc
--- /dev/null
+++ b/core/modules/page/lib/Drupal/page/PageListController.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\page\PageListController.
+ */
+
+namespace Drupal\page;
+
+use Drupal\Core\Entity\EntityListController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Provides a listing of pages.
+ */
+class PageListController extends EntityListController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::__constuct();
+   */
+  public function __construct($entity_type, EntityStorageControllerInterface $storage) {
+    parent::__construct($entity_type, $storage);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildHeader();
+   *
+   * Replace machine name with paths.
+   */
+  public function buildHeader() {
+    $row['label'] = t('Label');
+    $row['paths'] = t('Paths');
+    $row['operations'] = t('Operations');
+    return $row;
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::buildRow();
+   *
+   * Replace machine name with paths.
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['label'] = $entity->label();
+    $row['paths'] = nl2br(check_plain($entity->paths));
+    $operations = $this->buildOperations($entity);
+    $row['operations']['data'] = $operations;
+    return $row;
+  }
+
+}
diff --git a/core/modules/page/page.admin.inc b/core/modules/page/page.admin.inc
new file mode 100644
index 0000000..16c4d86
--- /dev/null
+++ b/core/modules/page/page.admin.inc
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Administration functions to maintain a set of pages using layouts.
+ */
+
+use Drupal\page\Page;
+
+/**
+ * Page callback: Presents list of pages.
+ *
+ * @see page_menu()
+ */
+function page_page_list() {
+  $controller = entity_list_controller('page');
+  return $controller->render();
+}
+
+/**
+ * Page callback: Presents the page editing form.
+ *
+ * @see page_menu()
+ */
+function page_page_edit(Page $page) {
+  drupal_set_title(t('<em>Edit page</em> @label', array('@label' => $page->label())), PASS_THROUGH);
+  return entity_get_form($page);
+}
+
+/**
+ * Page callback: Provides the new page addition form.
+ *
+ * @see page_menu()
+ */
+function page_page_add() {
+  $page = entity_create('page', array());
+  return entity_get_form($page);
+}
+
+/**
+ * Page callback: Form constructor for page deletion confirmation form.
+ *
+ * @see page_menu()
+ */
+function page_delete_confirm($form, &$form_state, Page $page) {
+  // Always provide entity id in the same form key as in the entity edit form.
+  $form['id'] = array('#type' => 'value', '#value' => $page->id());
+  $form_state['page'] = $page;
+  return confirm_form($form,
+    t('Are you sure you want to remove the page %title?', array('%title' => $page->label())),
+    'admin/structure/pages',
+    t('This action cannot be undone.'),
+    t('Delete'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Form submission handler for page_delete_confirm().
+ */
+function page_delete_confirm_submit($form, &$form_state) {
+  $page = $form_state['page'];
+  $page->delete();
+  drupal_set_message(t('Page %label has been deleted.', array('%label' => $page->label())));
+  watchdog('page', 'Page %label has been deleted.', array('%label' => $page->label()), WATCHDOG_NOTICE);
+  $form_state['redirect'] = 'admin/structure/pages';
+}
diff --git a/core/modules/page/page.info b/core/modules/page/page.info
new file mode 100644
index 0000000..ecf60f4
--- /dev/null
+++ b/core/modules/page/page.info
@@ -0,0 +1,7 @@
+name = Page
+description = Makes it possible to swap different page layouts.
+package = Core
+version = VERSION
+core = 8.x
+dependencies[] = config
+configure = admin/structure/pages
diff --git a/core/modules/page/page.module b/core/modules/page/page.module
new file mode 100644
index 0000000..c49e0ee
--- /dev/null
+++ b/core/modules/page/page.module
@@ -0,0 +1,159 @@
+<?php
+
+/**
+ * @file
+ * Module to maintain a set of pages using layouts.
+ */
+
+use Drupal\page\Page;
+
+/**
+ * Implements hook_menu().
+ */
+function page_menu() {
+  $items = array();
+  $items['admin/structure/pages'] = array(
+    'title' => 'Page library',
+    'description' => 'Manage pages using layouts that allow content to be placed.',
+    'page callback' => 'page_page_list',
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pages'),
+    'file' => 'page.admin.inc',
+  );
+  $items['admin/structure/pages/add'] = array(
+    'title' => 'Add page',
+    'page callback' => 'page_page_add',
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pages'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'page.admin.inc',
+  );
+  $items['admin/structure/pages/page/%page/edit'] = array(
+    'title' => 'Edit page',
+    'page callback' => 'page_page_edit',
+    'page arguments' => array(4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pages'),
+    'type' => MENU_CALLBACK,
+    'file' => 'page.admin.inc',
+  );
+  $items['admin/structure/pages/page/%page/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('page_delete_confirm', 4),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer pages'),
+    'type' => MENU_CALLBACK,
+    'file' => 'page.admin.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_permission().
+ */
+function page_permission() {
+  return array(
+    'administer pages' => array(
+      'title' => t('Administer pages'),
+      'description' => t('Manage the set of pages with distinct layouts on the site.'),
+    ),
+  );
+}
+
+/**
+ * @todo remove this in favor of actually integrating with layout mappers.
+ */
+function page_init() {
+  if ($page = page_get_matched_page()) {
+    drupal_set_message(t('%page matched', array('%page' => $page->label())));
+  }
+}
+
+/**
+ * API function to look up if the current page is matched by a config.
+ *
+ * @return Drupal\page\Page|FALSE
+ */
+function page_get_matched_page() {
+  $pages = page_load_all();
+  $page_match = FALSE;
+  foreach ($pages as $page) {
+    if (!empty($page->paths)) {
+      // Convert paths to lowercase. This allows comparison of the same path
+      // with different case. Ex: /Page, /page, /PAGE.
+      $paths = drupal_strtolower($page->paths);
+      // Compare the lowercase path alias (if any) and internal path.
+      $path = current_path();
+      $path_alias = drupal_strtolower(drupal_get_path_alias($path));
+      $page_match = drupal_match_path($path_alias, $paths) || (($path != $path_alias) && drupal_match_path($path, $paths));
+      // When $page->visibility has a value of 0, the page is used on all
+      // paths except those listed in $page->paths. When set to 1, it is
+      // used only on those pages listed in $page->paths.
+      $page_match = !($page->visibility xor $page_match);
+      if ($page_match) {
+        break;
+      }
+    }
+  }
+  return $page_match ? $page : FALSE;
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function page_entity_info() {
+  $types['page'] = array(
+    'label' => 'Page',
+    'entity class' => 'Drupal\page\Page',
+    'controller class' => 'Drupal\Core\Config\Entity\ConfigStorageController',
+    'form controller class' => array(
+      'default' => 'Drupal\page\PageFormController',
+    ),
+    'list controller class' => 'Drupal\Core\Config\Entity\ConfigEntityListController',
+    'list path' => 'admin/structure/pages',
+    'uri callback' => 'page_uri',
+    'config prefix' => 'page',
+    'entity keys' => array(
+      'id' => 'id',
+      'label' => 'label',
+      'uuid' => 'uuid',
+    ),
+  );
+  return $types;
+}
+
+/**
+ * Entity URI callback.
+ *
+ * @param Drupal\page\Page $page
+ *   Page configuration entity instance.
+ *
+ * @return array
+ *   Entity URI information.
+ */
+function page_uri(Page $page) {
+  return array(
+    'path' => 'admin/structure/pages/page/' . $page->id(),
+  );
+}
+
+/**
+ * Load one page object by its identifier.
+ *
+ * @return Drupal\page\Page
+ *   Page configuration entity instance.
+ */
+function page_load($id) {
+  return entity_load('page', $id);
+}
+
+/**
+ * Load all page objects.
+ *
+ * @return array
+ *   List of Drupal\page\Page instances keyed by id.
+ */
+function page_load_all() {
+  return entity_load_multiple('page');
+}
