diff --git a/core/includes/config.inc b/core/includes/config.inc
index 39094f8..8b43120 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -3,6 +3,8 @@
 use Drupal\Core\Config\Config;
 use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\NullStorage;
+use Drupal\Core\Config\Metadata\ConfigWrapper;
+use Drupal\Core\Config\Metadata\MetadataLookup;
 use Drupal\Core\Config\StorageInterface;
 
 /**
@@ -104,6 +106,36 @@ function config_sync_get_changes(StorageInterface $source_storage, StorageInterf
 }
 
 /**
+ * Retrieves a metadata object for a configuration object or key.
+ *
+ * @param $name
+ *   The name of the configuration object for which we want to build a metadata object.
+ *
+ * @return Drupal\Core\Config\ConfigMetadata
+ *   An metadata object that contains the full metadata for the reguested name.
+ */
+function config_metadata($name) {
+  $metadata = &drupal_static(__FUNCTION__);
+  if (!$metadata) {
+    $metadata = new MetadataLookup();
+  }
+  return $metadata[$name];
+}
+
+/**
+ * Retrieves a configuration wrapper object to access data as typed properties.
+ *
+ * @param $name
+ *   The name of the configuration object to retrieve.
+ *
+ * @return Drupal\Core\Config\Metadata\Wrapper
+ *   A configuration wrapper object.
+ */
+function config_wrapper($name) {
+  return new ConfigWrapper(config($name));
+}
+
+/**
  * Writes an array of config file changes from a source storage to a target storage.
  *
  * @param array $config_changes
diff --git a/core/lib/Drupal/Core/Config/Metadata/ArrayConfigData.php b/core/lib/Drupal/Core/Config/Metadata/ArrayConfigData.php
new file mode 100644
index 0000000..ddf74e0
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ArrayConfigData.php
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ArrayConfigData.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Defines a nested configuration metadata object.
+ *
+ * This is basically a Drupal\Core\Config\Metadata\ConfigData that may
+ * contain some more metadata properties in the base array:
+ * - 'config.base', Name of the base metadata that will be used for all keys
+ *   in this class.
+ * - 'config.key', Key to use as element name for mapping additional
+ *   metadata for each of the nested objects.
+ *
+ * Example metadata from image.style.%.yml
+ *
+ * effects:
+ *   label: 'Style effects'
+ *   type: 'config_nested'
+ *   config.base: 'image.style.effects.*'
+ *   config.key: 'name'
+ *
+ * Example data from image.style.large.yml
+ *
+ * effects:
+ *   ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d:
+ *     name: image.style.effects.scale
+ *     data:
+ *       width: '480'
+ *       height: '480'
+ *       upscale: '1'
+ *     weight: '0'
+ *     ieid: ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d
+ *
+ * When building nested elements of this type we'll have:
+ * - A 'config.base' item that will be 'image.style.%', so this will be used
+ *   as the base metadata for these properties.
+ * - A 'config.key' item that will be 'name', which means of each of the
+ *   items, we'll use the 'name' key, which here is 'image.style.effects.scale'
+ *   and will load new metadata with this name.
+ *
+ * Example metadata from image.style.effects.%.yml + image.style.effects.scale.yml
+ *
+ * data:
+ *   label: 'Data'
+ *   type: 'config_structure'
+ *   width:
+ *     label: 'Width'
+ *     type: 'integer'
+ *   height:
+ *     label: 'Height'
+ *     type: 'integer'
+ *   upscale:
+ *     label: 'Upscale'
+ *     type: 'boolean'
+ */
+class ArrayConfigData extends ConfigData implements TypedDataInterface {
+  /**
+   * The configuration key relative to the parent's.
+   *
+   * @var string
+   */
+  protected $key;
+
+  /**
+   * The definition for this typed data.
+   *
+   * @var array
+   */
+  protected $definition;
+
+  /**
+   * Parent configuration data.
+   *
+   * @var Drupal\Core\Config\ConfigDataInterface
+   */
+  protected $parentConfigData;
+
+  /**
+   * Implements TypedDataInterface::__construct().
+   */
+  public function __construct(array $definition) {
+    $this->definition = $definition;
+  }
+
+  /**
+   * Set parent configuration data and relative key.
+   *
+   * @param Drupal\Core\Config\Metadata\ConfigDataInterface $parent
+   *   Parent ConfigDataInterface object.
+   * @param string $key
+   *   The key of the configuration object being constructed.
+   *
+   * @return Drupal\Core\Config\Metadata\NestedConfigData
+   *   The called object.
+   */
+  public function setParent($parent, $key) {
+    $this->parentConfigData = $parent;
+    $this->key = $key;
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getKey().
+   */
+  public function getKey($key = '') {
+    return $this->key . ($key ? '.' . $key : '');
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getConfigData().
+   */
+  public function getConfigData($key = '') {
+    return $this->parentConfigData->getConfigData($this->getKey($key));
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getConfigKey().
+   */
+  public function getConfigKey() {
+    $parent = $this->parentConfigData->getConfigKey();
+    return ($parent ? $parent . '.' : '') . $this->getKey();
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getMetadata().
+   */
+  public function getMetadata() {
+    return $this->parentConfigData->getElementMetadata($this->getKey());
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::set().
+   */
+  public function set($property_name, $value) {
+    // Set the value in the parent object that holds the Config.
+    $this->parentConfigData->set($this->getKey($property_name), $value);
+    return $this;
+  }
+
+  /**
+   * Implements TypedDataInterface::getType().
+   */
+  public function getType() {
+    return $this->definition['type'];
+  }
+
+  /**
+   * Implements TypedDataInterface::getDefinition().
+   */
+  public function getDefinition() {
+    return $this->definition;
+  }
+
+  /**
+   * Implements TypedDataInterface::getValue().
+   */
+  public function getValue() {
+    return $this->getConfigData();
+  }
+
+  /**
+   * Implements TypedDataInterface::setValue().
+   */
+  public function setValue($value) {
+    $this->parentConfigData->set($this->getKey(), $value);
+  }
+
+  /**
+   * Implements TypedDataInterface::getString().
+   */
+  public function getString() {
+    return (string) $this->getValue();
+  }
+
+  /**
+   * Implements TypedDataInterface::validate().
+   */
+  public function validate() {
+    // This will be ok if we have any config data at all.
+    $data = $this->getConfigData();
+    return isset($data);
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigData.php b/core/lib/Drupal/Core/Config/Metadata/ConfigData.php
new file mode 100644
index 0000000..f1c4a8c
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ConfigData.php
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ConfigData.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use ArrayIterator;
+use IteratorAggregate;
+use InvalidArgumentException;
+
+/**
+ * Base class for a properties wrapper using configuration data and metadata.
+ *
+ * Nested arrays of configuration data will be wrapped in multiple objects of
+ * this type.
+ *
+ * This can also be used as a nested array that provides the full configuration
+ * data and the metadata. The array keys will be the property names and the
+ * array values will be instances of TypedDataInterface objects.
+ *
+ * Example file formats that will be processed by this class:
+ *
+ * - Data: module/system/config/system.site.yml
+ *
+ *   name: Drupal
+ *     mail: ''
+ *     slogan: ''
+ *   page:
+ *     403: ''
+ *     404: ''
+ *     front: user
+ *
+ * - Metadata: module/system/meta/system.site.yml
+ *
+ *   name:
+ *     label: 'Site name'
+ *     type: 'string'
+ *   mail:
+ *     label: 'Site mail'
+ *     type: 'email'
+ *   slogan:
+ *     label: 'Site slogan'
+ *     type: 'string'
+ *   page:
+ *     403:
+ *       label: 'Default 403 (access denied) page'
+ *       type: 'path'
+ *     404:
+ *       label: 'Default 404 (not found) page'
+ *       type: 'path'
+ *     front:
+ *       label: 'Default front page'
+ *       type: 'path'
+ */
+abstract class ConfigData implements IteratorAggregate, ConfigDataInterface {
+
+  /**
+   * Builds properties using configuration data and metadata.
+   *
+   * @param string $key
+   *   Configuration key.
+   *
+   * @result Drupal\Core\TypedData\TypedDataInterface
+   *   A fully built TypedDataInterface object.
+   */
+  protected function buildElement($key) {
+    // Get metadata and split into parent and children's properties.
+    $metadata = $this->getElementMetadata($key);
+    list($basemeta, $children) = $this->splitMetadata($metadata);
+
+    switch ($basemeta['type']) {
+      case 'config_structure':
+        $nested = new ArrayConfigData($metadata);
+        $nested->setParent($this, $key);
+        return $nested;
+
+      case 'config_nested':
+        $nested = new NestedConfigData($metadata);
+        $nested->setParent($this, $key);
+        return $nested;
+
+      default:
+        // The element has no children so it is an end property and the metadata
+        // will be the property definition. We add a default type just in case.
+        return $this->buildProperty($key, $basemeta);
+    }
+  }
+
+  /**
+   * Split metadata in base element's properties and children.
+   *
+   * These are the properties that are not arrays, thus they're this element's metadata.
+   */
+  protected static function splitMetadata($metadata) {
+    // Children elements will be arrays of metadata themselves.
+    $children = array_filter($metadata, 'is_array');
+    // Parent properties will be scalar values.
+    $basemeta = array_filter($metadata, 'is_scalar');
+    // The default type will depend on whether we've got children or not.
+    $basemeta += array('type' => $children ? 'config_structure' : 'undefined');
+    return array($basemeta, $children);
+  }
+
+  /**
+   * Builds property as typed data.
+   *
+   * @param string $key
+   *   Property key in the configuration data.
+   * @return Drupal\Core\TypedData\DataInterface
+   *   Property as typed data.
+   */
+  protected function buildProperty($key, array $definition = array()) {
+    $value = $this->getConfigData($key);
+    $definition += array('type' => 'undefined');
+    $context = array(
+        'name' => $key,
+        'parent' => $this,
+    );
+    return typed_data()->create($definition, $value, $context);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getElementMetadata().
+   */
+  public function getElementMetadata($key) {
+    $metadata = $this->getMetadata();
+    return isset($metadata[$key]) ? $metadata[$key] : array();
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::toList().
+   */
+  public function toList() {
+    $list = array();
+    foreach ($this->getProperties() as $key => $property) {
+      if ($property instanceof ConfigData) {
+        foreach ($property->toList() as $index => $value) {
+          $list[$key . '.' . $index] = $value;
+        }
+      }
+      else {
+        $list[$key] = $property;
+      }
+    }
+    return $list;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::get().
+   */
+  public function get($property_name) {
+    $parts = explode('.', $property_name);
+    $first = array_shift($parts);
+    $data = $this->getConfigData($first);
+    if (isset($data)) {
+      // We have data and metadata, built typed data.
+      $element = $this->buildElement($first);
+      $key = implode('.', $parts);
+      if (!$key) {
+        return $element;
+      }
+      elseif ($element instanceof ConfigDataInterface) {
+        return $element->get($key);
+      }
+    }
+    else {
+      throw new InvalidArgumentException("The configuration property $property_name doesn't exist.");
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getProperties().
+   */
+  public function getProperties($include_computed = FALSE) {
+    $array = array();
+    foreach (array_keys($this->getConfigData()) as $key) {
+      $array[$key] = $this->buildElement($key);
+    }
+    return $array;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
+   */
+  public function getPropertyValues() {
+    return $this->getConfigData();
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
+   */
+  public function setPropertyValues($values) {
+    foreach ($values as $name => $value) {
+      $this->set($name, $value);
+    }
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
+   */
+  public function getPropertyDefinition($name) {
+    if ($meta = $this->getMetadata($name)) {
+      return $this->getBaseMetadata($meta);
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   */
+  public function getPropertyDefinitions() {
+    return $this->getChildrenMetadata($this->getMetadata());
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::toArray().
+   */
+  public function toArray() {
+    return $this->getConfigData();
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
+   */
+  public function isEmpty() {
+    return !(bool)$this->getConfigData();
+  }
+
+  /**
+   * Implements IteratorAggregate::getIterator().
+   */
+   public function getIterator() {
+     return new ArrayIterator($this->getProperties());
+   }
+
+   /**
+    * Implements ArrayAccess::offsetExists().
+    */
+   public function offsetExists($offset) {
+     $data = $this->getConfigData($offset);
+     return isset($data);
+   }
+
+   /**
+    * Implements ArrayAccess::offsetGet().
+    */
+   public function offsetGet($offset) {
+     return $this->get($offset);
+   }
+
+   /**
+    * Implements ArrayAccess::offsetSet().
+    */
+   public function offsetSet($offset, $value) {
+     $this->set($offset, $value);
+   }
+
+   /**
+    * Implements ArrayAccess::offsetUnset().
+    */
+   public function offsetUnset($offset) {
+     $this->set($offset, NULL);
+   }
+
+   /**
+    * Implements Countable::count().
+    */
+   public function count() {
+     return count($this->getConfigData());
+   }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigDataInterface.php b/core/lib/Drupal/Core/Config/Metadata/ConfigDataInterface.php
new file mode 100644
index 0000000..cfcb171
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ConfigDataInterface.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ConfigDataInterface.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\TypedData\ComplexDataInterface;
+use ArrayAccess;
+use Countable;
+
+/**
+ * Interface for data structures that contain configuration data and metadata.
+ *
+ * These may be nested structures mapping nested arrays of configuration data
+ */
+interface ConfigDataInterface extends ComplexDataInterface, ArrayAccess, Countable {
+
+  /**
+   * Get relative key for this structure or children of it.
+   *
+   * @param string $key
+   *   (optional) Child element's key.
+   *
+   * @return string
+   *   Configuration key relative to this structure.
+   */
+  public function getKey($key = '');
+
+  /**
+   * Gets raw medata array for this element.
+   *
+   * @return array
+   *   The metadata property that was requested or the full metadata array.
+   */
+  public function getMetadata();
+
+  /**
+   * Gets metadata array from this config metadata.
+   *
+   * An array of metadata contains the 'label' and 'type' keys as explained above.
+   * If $key is omitted, the full array is returned, where the array keys are the
+   * metadata keys and the values are the metadata arrays (see above).
+   *
+   * @param $key
+   *   String that maps to a key within the configuration metadata.
+   * @return array
+   *   The metadata for the element, defaults to empty array().
+   */
+  public function getElementMetadata($key);
+
+  /**
+   * Gets raw configuration data.
+   *
+   * @param $key
+   *   Optional string that maps to a key within the configuration data.
+   *
+   * @return array|string|null
+   *   Array or item of configuration data.
+   */
+  public function getConfigData($key = '');
+
+  /**
+   * Gets configuration key mapping to this configuration data.
+   *
+   * @param $key
+   *   Optional string that maps to a key within the configuration data.
+   *
+   * @return string
+   *   Array or item of configuration data.
+   */
+  public function getConfigKey();
+
+  /**
+   * Gets all typed data properties as plain array.
+   *
+   * @return array
+   *   Plain array of TypedDataInterface and ConfigComplexDataInterface objects
+   *   indexed by full name (keys with dot notation).
+   */
+  public function toList();
+
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php b/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php
new file mode 100644
index 0000000..1b69c42
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/ConfigWrapper.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\ConfigWrapper.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Config\Config;
+
+/**
+ * Defines the configuration wrapper object.
+ */
+class ConfigWrapper extends ConfigData {
+  /**
+   * The configuration object itself.
+   *
+   * @var Drupal\Core\Config\Config
+   */
+  protected $config;
+
+  /**
+   * The configuration metadata.
+   *
+   * @var array
+   */
+  protected $metadata;
+
+  /**
+   * Constructs a configuration wrapper object.
+   *
+   * @param Drupal\Core\Config $config
+   *   Configuration object.
+   */
+  public function __construct($config) {
+    $this->config = $config;
+  }
+
+  /**
+   * Gets the configuration object for this data.
+   *
+   * @return Drupal\Core\Config\Config
+   */
+  public function getConfig() {
+    return $this->config;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getKey().
+   */
+  public function getKey($key = '') {
+    // As this is the parent structure, there is no base key.
+    return $key;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getMetadata().
+   */
+  public function getMetadata() {
+    if (!isset($this->metadata)) {
+      $this->metadata = config_metadata($this->getConfig()->getName());
+    }
+    return $this->metadata;
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getConfigData().
+   */
+  public function getConfigData($key = '') {
+    // As this is the parent structure, we get it right from the config object.
+    return $this->config->get($key);
+  }
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getConfigData().
+   */
+  public function getConfigKey() {
+    // This is the parent ConfigDataInterface so the key is empty.
+    return '';
+  }
+
+  /**
+   * Implements Drupal\Core\TypedData\ComplexDataInterface::set().
+   */
+  public function set($property_name, $value) {
+    // Set the data into the configuration object but behave according
+    // to the interface specification when we've got a null value.
+    if (isset($value)) {
+      $this->config->set($property_name, $value);
+    }
+    else {
+      $this->config->clear($property_name);
+    }
+    return $this;
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php
new file mode 100644
index 0000000..f7f0639
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/MetadataLookup.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\MetadataLookup.
+ *
+ * Metadata lives in a modules 'meta' directory, next to the 'config' directory,
+ * which contains the actual configuration. For each entry in an actual
+ * configuration file, the corresponding metadata file contains an entry with
+ * the following sub-keys:
+ * - label: The human-readable name for this configuration item.
+ * - type: The data type of this configuration item. Valid types include string,
+ *   boolean, integer, and other types defined by hook_data_type_info() ...
+ *
+ * The name of the configuration object may contain the placeholder '__' as the
+ * last component, which denotes generic metadata for all objects of this
+ * type. For the 'large' image style, for example, both "image.style.__" and
+ * "image.style.large" would be valid metadata files. In case both of these
+ * files exist, we load the generic metadata (in this example from
+ * "image.style.__") first and add the specific metadata (in this example from
+ * "image.style.large") on top of it.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Utility\CacheArray;
+use Drupal\Component\Utility\NestedArray;
+use Symfony\Component\Yaml\Parser;
+
+/**
+ * Defines the default configuration metadata object.
+ */
+class MetadataLookup extends CacheArray {
+
+  /**
+   * Filesystem marker for base metadata name.
+   */
+  const BASE_MARK = '%';
+
+  /**
+   * Storage to retrieve the metadata.
+   *
+   * @var Drupal\Core\Config\Metadata\MetadataStorage
+   */
+  protected $metadataStorage;
+
+  /**
+   * Constructs a ConfigMetadata cache object.
+   */
+  public function __construct() {
+    parent::__construct('config:metadata', 'cache');
+  }
+
+  /**
+   * Overrides DrupalCacheArray::resolveCacheMiss().
+   */
+  protected function resolveCacheMiss($offset) {
+    $metadata = array();
+    // Get base metadata if available.
+    if ($basename = $this->getBaseName($offset)) {
+      $metadata = $this->offsetGet($basename);
+    }
+    // Merge current metadata on top of it.
+    if ($meta = $this->readMetadata($offset)) {
+      $metadata = NestedArray::mergeDeep($metadata, $meta);
+    }
+    $this->storage[$offset] = $metadata;
+    return $metadata;
+  }
+
+  /**
+   * Get parent metadata name.
+   */
+  protected static function getBaseName($name) {
+    $parts = explode('.', $name);
+    $last = array_pop($parts);
+    // If this is not the generic metadata (where the last component of the
+    // name is a '+'), try to load that first.
+    if ($last != self::BASE_MARK) {
+      return implode('.', array_merge($parts, array(self::BASE_MARK)));
+    }
+  }
+
+  /**
+   * Read metadata from file system.
+   *
+   * @return array
+   *   Metadata array if found or empty array if not.
+   */
+  protected function readMetadata($name) {
+    if (!isset($this->metadataStorage)) {
+      $this->metadataStorage = new MetadataStorage();
+    }
+    try {
+      return $this->metadataStorage->read($name);
+    }
+    catch (Symfony\Component\Yaml\Exception\ParseException $e) {
+      // @todo Just log an error, but return empty array.
+    }
+    return array();
+  }
+}
diff --git a/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
new file mode 100644
index 0000000..1806a01
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/MetadataStorage.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\MetadataStorage.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Defines the file storage controller.
+ */
+class MetadataStorage extends FileStorage {
+
+  /**
+   * Overrides Drupal\Core\Config\FileStorage::__construct().
+   *
+   * We don't need any directory here as it will be extracted from metadata name.
+   */
+  public function __construct() {
+  }
+
+  /**
+   * Overrides Drupal\Core\Config\FileStorage::getFilePath().
+   */
+  public function getFilePath($name) {
+    $module = strtok($name, '.');
+    // We are looking for a file in $module/meta/$name.yml
+    $directory = drupal_get_path('module', $module) . '/meta';
+    return $directory  . '/' . $name . '.yml';
+  }
+
+  /**
+   * Implements Drupal\Core\Config\StorageInterface::listAll().
+   */
+  public function listAll($prefix = '') {
+    // This will only workf for a prefix because otherwise we don't have any module name.
+    if (!$prefix) {
+      return array();
+    }
+    else {
+      $module = strtok($prefix, '.');
+      // We are looking for files in $module/meta
+      $this->directory = drupal_get_path('module', $module) . '/meta';
+      return parent::listAll($prefix);
+    }
+  }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Config/Metadata/NestedConfigData.php b/core/lib/Drupal/Core/Config/Metadata/NestedConfigData.php
new file mode 100644
index 0000000..0609aa5
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/Metadata/NestedConfigData.php
@@ -0,0 +1,107 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Config\Metadata\NestedConfigData.
+ */
+
+namespace Drupal\Core\Config\Metadata;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * Defines a nested configuration metadata object.
+ *
+ * This is basically a Drupal\Core\Config\Metadata\ConfigData that may
+ * contain some more metadata properties in the base array:
+ * - 'config_base', Name of the base metadata that will be used for all keys
+ *   in this class.
+ * - 'config_key', Key to use as element name for mapping additional
+ *   metadata for each of the nested objects.
+ *
+ * Example metadata from image.style.%.yml
+ *
+ * effects:
+ *   label: 'Style effects'
+ *   type: 'config_nested'
+ *   config_base: 'image.style.effects.*'
+ *   config_key: 'name'
+ *
+ * Example data from image.style.large.yml
+ *
+ * effects:
+ *   ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d:
+ *     name: image.style.effects.scale
+ *     data:
+ *       width: '480'
+ *       height: '480'
+ *       upscale: '1'
+ *     weight: '0'
+ *     ieid: ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d
+ *
+ * When building nested elements of this type we'll have:
+ * - A 'config_base' item that will be 'image.style.%', so this will be used
+ *   as the base metadata for these properties.
+ * - A 'config.key' item that will be 'name', which means of each of the
+ *   items, we'll use the 'name' key, which here is 'image.style.effects.scale'
+ *   and will load new metadata with this name.
+ *
+ * Example metadata from image.style.effects.%.yml + image.style.effects.scale.yml
+ *
+ * data:
+ *   width:
+ *     label: 'Width'
+ *     type: 'integer'
+ *   height:
+ *     label: 'Height'
+ *     type: 'integer'
+ *   upscale:
+ *     label: 'Upscale'
+ *     type: 'boolean'
+ */
+class NestedConfigData extends ArrayConfigData {
+
+  /**
+   * Implements Drupal\Core\Config\Metadata\ConfigDataInterface::getElementMetadata().
+   */
+  public function getElementMetadata($key) {
+    $metadata = $this->getBaseMetadata();
+    //This will depend on the actual data and the property we are using to map
+    // it to the configuration metadata name.
+    if ($name = $this->getMetadataName($key)) {
+      return NestedArray::mergeDeep($metadata, config_metadata($name));
+    }
+    else {
+      return $metadata;
+    }
+  }
+
+  /**
+   * Gets base metadata for all nested keys.
+   */
+  protected function getBaseMetadata() {
+    $definition = $this->getDefinition();
+    if (isset($this->definition['config_base'])) {
+      return config_metadata($this->definition['config_base']);
+    }
+    else {
+      return array();
+    }
+  }
+
+  /**
+   * Gets the metadata name that maps to a configuration data key.
+   *
+   * @param string $key
+   *   Element's key in the configuration data.
+   *
+   * @return string|null
+   *   Configuration metadata name for that element.
+   */
+  protected function getMetadataName($key) {
+    if (isset($this->definition['config_key'])) {
+      return $this->getConfigData($key . '.' . $this->definition['config_key']);
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/Primitive.php b/core/lib/Drupal/Core/TypedData/Primitive.php
index 523a047..d47f4a0 100644
--- a/core/lib/Drupal/Core/TypedData/Primitive.php
+++ b/core/lib/Drupal/Core/TypedData/Primitive.php
@@ -13,6 +13,11 @@ namespace Drupal\Core\TypedData;
 final class Primitive {
 
   /**
+   * The UNDEFINED primitive data type.
+   */
+  const UNDEFINED = 0;
+
+  /**
    * The BOOLEAN primitive data type.
    */
   const BOOLEAN = 1;
diff --git a/core/lib/Drupal/Core/TypedData/Type/Undefined.php b/core/lib/Drupal/Core/TypedData/Type/Undefined.php
new file mode 100644
index 0000000..4539da1
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Type/Undefined.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\TypedData\Type\Undefined.
+ */
+
+namespace Drupal\Core\TypedData\Type;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+
+/**
+ * The variant data type.
+ *
+ * The plain value of a string is a regular PHP string. For setting the value
+ * any PHP variable that casts to a string may be passed.
+ */
+class Undefined extends TypedData implements TypedDataInterface {
+
+  /**
+   * The data value.
+   *
+   * @var mixed
+   */
+  protected $value;
+
+  /**
+   * Implements TypedDataInterface::setValue().
+   */
+  public function setValue($value) {
+    $this->value = $value;
+  }
+
+  /**
+   * Implements TypedDataInterface::validate().
+   */
+  public function validate() {
+    // TODO: Implement validate() method.
+  }
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php
new file mode 100644
index 0000000..3c97d65
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigMetadataTest.php
@@ -0,0 +1,147 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigMetadataTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\Core\Config\ConfigWrapper;
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Tests Metadata for configuration objects.
+ */
+class ConfigMetadataTest extends WebTestBase {
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('image');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configuration metadata',
+      'description' => 'Tests Metadata for configuration objects.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Tests the basic metadata retrieval layer.
+   */
+  function testBasicMetadata() {
+    // Simple case, straight metadata.
+    $metadata = config_metadata('system.maintenance');
+    $expected = array(
+      'enabled' => array(
+        'label' => 'Put site into maintenance mode',
+        'type' => 'boolean'
+       ),
+      'message' => array(
+        'label' =>  'Message to display when in maintenance mode',
+        'type' => 'string'
+      ),
+    );
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for system.maintenance');
+    // More complex case, fallback to parent name.
+    $metadata = config_metadata('image.style.large');
+    $expected = array(
+      'name' => array(
+        'label' => 'Machine name',
+      ),
+      'label' => array(
+        'label' => 'Label',
+        'type' => 'string'
+      ),
+      'effects' => array(
+        'label' =>  'Style effects',
+        'type' => 'config_nested',
+        'config_base' => 'image.style.effects.*',
+        'config_key' => 'name'
+      )
+    );
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.large');
+
+    // Most complex case, fallback to parent name with merging
+    $metadata = config_metadata('image.style.effects.scale');
+    $expected = array(
+      'name' => array(
+        'label' => 'Style name'
+      ),
+      'data' => array(
+        'label' => 'Data',
+        'width' => array(
+          'label' => 'Width',
+          'type' => 'integer'
+        ),
+        'height' => array(
+          'label' =>  'Height',
+          'type' => 'integer',
+        ),
+        'upscale' => array(
+          'label' =>  'Upscale',
+          'type' => 'boolean',
+        )
+    ),
+      'weight' => array(
+        'label' => 'Weight',
+        'type' => 'integer'
+      ),
+      'ieid' => array(
+        'label' =>  'IEID',
+      ),
+
+    );
+    $this->assertEqual($metadata, $expected, 'Retrieved the right metadata for image.style.effects.scale');
+
+  }
+
+  /**
+   * Tests metadata applied to configuration objects.
+   */
+  function testConfigMetadata() {
+    // Try some simple properties.
+    $meta = config_wrapper('system.site');
+    $property = $meta->get('name');
+    $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\String'), 'Got the right wrapper fo the site name property.');
+    $this->assertEqual($property->getType(), 'string', 'Got the right string type for site name data.');
+    $this->assertEqual($property->getValue(), 'Drupal', 'Got the right string value for site name data.');
+
+    $property = $meta->get('page.front');
+    $this->assertTrue(is_a($property, 'Drupal\Core\TypedData\Type\Undefined'), 'Got the right wrapper fo the page.front property.');
+    $this->assertEqual($property->getType(), 'undefined', 'Got the right type for page.front data (undefined).');
+    $this->assertEqual($property->getValue(), 'user', 'Got the right value for page.front data.');
+
+    // Check nested array of properties.
+    $list = $meta->get('page');
+    $this->assertEqual(count($list), 3, 'Got a list with the right number of properties for site page data');
+    $this->assertTrue(isset($list['front']) && isset($list['403']) && isset($list['404']), 'Got a list with the right properties for site page data.');
+    $this->assertEqual($list['front']->getValue(), 'user', 'Got the right value for page.front data from the list.');
+
+    // Now let's try something more complex, with nested objects.
+    $wrapper = config_wrapper('image.style.large');
+    $effects = $wrapper->get('effects');
+    // The function is_array() doesn't work with ArrayAccess, so we use count().
+    $this->assertTrue(count($effects) == 1, 'Got an array with effects for image.style.large data');
+    $ieid = key($effects->getProperties());
+    $effect = $wrapper->get('effects.' . $ieid);
+    $this->assertTrue(count($effect['data']) && $effect['name']->getValue() == 'image.style.effects.scale', 'Got data for the image scale effect from metadata.');
+    $this->assertEqual($effect['data']['width']->getType(), 'integer', 'Got the right type for the scale effect width.');
+    $this->assertEqual($effect['data']['width']->getValue(), 480, 'Got the right value for the scale effect width.' );
+    // Try config key and getting nested properties.
+    $key = 'effects.' . $ieid . '.data';
+    $this->assertEqual($effect['data']->getConfigKey(), $key, 'Got the right configuration key for a nested image styple effect');
+    $this->assertEqual($effect['data'], $wrapper->get($key), 'Got the same object using different keys with dot notation.');
+
+    // Finally update some object using a configuration wrapper.
+    $new_slogan = 'Site slogan for testing configuration metadata';
+    $wrapper = config_wrapper('system.site');
+    $wrapper->set('slogan', $new_slogan);
+    $site_slogan = $wrapper->get('slogan');
+    $this->assertEqual($site_slogan->getValue(), $new_slogan, 'Successfully updated the contained configuration data');
+  }
+
+}
diff --git a/core/modules/image/config/image.style.large.yml b/core/modules/image/config/image.style.large.yml
index 012af17..99e1056 100644
--- a/core/modules/image/config/image.style.large.yml
+++ b/core/modules/image/config/image.style.large.yml
@@ -2,7 +2,7 @@ name: large
 label: Large (480x480)
 effects:
   ddd73aa7-4bd6-4c85-b600-bdf2b1628d1d:
-    name: image_scale
+    name: image.style.effects.scale
     data:
       width: '480'
       height: '480'
diff --git a/core/modules/image/config/image.style.medium.yml b/core/modules/image/config/image.style.medium.yml
index 941ab85..1b5ad6e 100644
--- a/core/modules/image/config/image.style.medium.yml
+++ b/core/modules/image/config/image.style.medium.yml
@@ -2,7 +2,7 @@ name: medium
 label: Medium (220x220)
 effects:
   bddf0d06-42f9-4c75-a700-a33cafa25ea0:
-    name: image_scale
+    name: image.style.effects.scale
     data:
       width: '220'
       height: '220'
diff --git a/core/modules/image/config/image.style.thumbnail.yml b/core/modules/image/config/image.style.thumbnail.yml
index a454200..19f8652 100644
--- a/core/modules/image/config/image.style.thumbnail.yml
+++ b/core/modules/image/config/image.style.thumbnail.yml
@@ -2,7 +2,7 @@ name: thumbnail
 label: Thumbnail (100x100)
 effects:
   1cfec298-8620-4749-b100-ccb6c4500779:
-    name: image_scale
+    name: image.style.effects.scale
     data:
       width: '100'
       height: '100'
diff --git a/core/modules/image/image.effects.inc b/core/modules/image/image.effects.inc
index 35a6a74..d491796 100644
--- a/core/modules/image/image.effects.inc
+++ b/core/modules/image/image.effects.inc
@@ -10,7 +10,7 @@
  */
 function image_image_effect_info() {
   $effects = array(
-    'image_resize' => array(
+    'image.style.effects.resize' => array(
       'label' => t('Resize'),
       'help' => t('Resizing will make images an exact set of dimensions. This may cause images to be stretched or shrunk disproportionately.'),
       'effect callback' => 'image_resize_effect',
@@ -18,7 +18,7 @@ function image_image_effect_info() {
       'form callback' => 'image_resize_form',
       'summary theme' => 'image_resize_summary',
     ),
-    'image_scale' => array(
+    'image.style.effects.scale' => array(
       'label' => t('Scale'),
       'help' => t('Scaling will maintain the aspect-ratio of the original image. If only a single dimension is specified, the other dimension will be calculated.'),
       'effect callback' => 'image_scale_effect',
@@ -26,7 +26,7 @@ function image_image_effect_info() {
       'form callback' => 'image_scale_form',
       'summary theme' => 'image_scale_summary',
     ),
-    'image_scale_and_crop' => array(
+    'image.style.effects.scale_and_crop' => array(
       'label' => t('Scale and crop'),
       'help' => t('Scale and crop will maintain the aspect-ratio of the original image, then crop the larger dimension. This is most useful for creating perfectly square thumbnails without stretching the image.'),
       'effect callback' => 'image_scale_and_crop_effect',
@@ -34,7 +34,7 @@ function image_image_effect_info() {
       'form callback' => 'image_resize_form',
       'summary theme' => 'image_resize_summary',
     ),
-    'image_crop' => array(
+    'image.style.effects.crop' => array(
       'label' => t('Crop'),
       'help' => t('Cropping will remove portions of an image to make it the specified dimensions.'),
       'effect callback' => 'image_crop_effect',
@@ -42,13 +42,13 @@ function image_image_effect_info() {
       'form callback' => 'image_crop_form',
       'summary theme' => 'image_crop_summary',
     ),
-    'image_desaturate' => array(
+    'image.style.effects.desaturate' => array(
       'label' => t('Desaturate'),
       'help' => t('Desaturate converts an image to grayscale.'),
       'effect callback' => 'image_desaturate_effect',
       'dimensions passthrough' => TRUE,
     ),
-    'image_rotate' => array(
+    'image.style.effects.rotate' => array(
       'label' => t('Rotate'),
       'help' => t('Rotating an image may cause the dimensions of an image to increase to fit the diagonal.'),
       'effect callback' => 'image_rotate_effect',
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
index 7437b10..1e614a6 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageAdminStylesTest.php
@@ -70,28 +70,28 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
     $style_label = $this->randomString();
     $style_path = 'admin/config/media/image-styles/edit/' . $style_name;
     $effect_edits = array(
-      'image_resize' => array(
+      'image.style.effects.resize' => array(
         'data[width]' => 100,
         'data[height]' => 101,
       ),
-      'image_scale' => array(
+      'image.style.effects.scale' => array(
         'data[width]' => 110,
         'data[height]' => 111,
         'data[upscale]' => 1,
       ),
-      'image_scale_and_crop' => array(
+      'image.style.effects.scale_and_crop' => array(
         'data[width]' => 120,
         'data[height]' => 121,
       ),
-      'image_crop' => array(
+      'image.style.effects.crop' => array(
         'data[width]' => 130,
         'data[height]' => 131,
         'data[anchor]' => 'center-center',
       ),
-      'image_desaturate' => array(
+      'image.style.effects.desaturate' => array(
         // No options for desaturate.
       ),
-      'image_rotate' => array(
+      'image.style.effects.rotate' => array(
         'data[degrees]' => 5,
         'data[random]' => 1,
         'data[bgcolor]' => '#FFFF00',
@@ -275,7 +275,7 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
     // Add a scale effect.
     $this->drupalGet('admin/config/media/image-styles/add');
     $this->drupalPost(NULL, array('label' => 'Test style effect edit', 'name' => 'test_style_effect_edit'), t('Create new style'));
-    $this->drupalPost(NULL, array('new' => 'image_scale_and_crop'), t('Add'));
+    $this->drupalPost(NULL, array('new' => 'image.style.effects.scale_and_crop'), t('Add'));
     $this->drupalPost(NULL, array('data[width]' => '300', 'data[height]' => '200'), t('Add effect'));
     $this->assertText(t('Scale and crop 300x200'));
 
@@ -290,13 +290,13 @@ class ImageAdminStylesTest extends ImageFieldTestBase {
     // Add another scale effect.
     $this->drupalGet('admin/config/media/image-styles/add');
     $this->drupalPost(NULL, array('label' => 'Test style scale edit scale', 'name' => 'test_style_scale_edit_scale'), t('Create new style'));
-    $this->drupalPost(NULL, array('new' => 'image_scale'), t('Add'));
+    $this->drupalPost(NULL, array('new' => 'image.style.effects.scale'), t('Add'));
     $this->drupalPost(NULL, array('data[width]' => '12', 'data[height]' => '19'), t('Add effect'));
 
     // Edit the scale effect that was just added.
     $this->clickLink(t('edit'));
     $this->drupalPost(NULL, array('data[width]' => '24', 'data[height]' => '19'), t('Update effect'));
-    $this->drupalPost(NULL, array('new' => 'image_scale'), t('Add'));
+    $this->drupalPost(NULL, array('new' => 'image.style.effects.scale'), t('Add'));
 
     // Add another scale effect and make sure both exist.
     $this->drupalPost(NULL, array('data[width]' => '12', 'data[height]' => '19'), t('Add effect'));
diff --git a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
index c2734f0..d49946c 100644
--- a/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
+++ b/core/modules/image/lib/Drupal/image/Tests/ImageDimensionsTest.php
@@ -58,7 +58,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Scale an image that is wider than it is high.
     $effect = array(
-      'name' => 'image_scale',
+      'name' => 'image.style.effects.scale',
       'data' => array(
         'width' => 120,
         'height' => 90,
@@ -80,7 +80,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Rotate 90 degrees anticlockwise.
     $effect = array(
-      'name' => 'image_rotate',
+      'name' => 'image.style.effects.rotate',
       'data' => array(
         'degrees' => -90,
         'random' => FALSE,
@@ -101,7 +101,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Scale an image that is higher than it is wide (rotated by previous effect).
     $effect = array(
-      'name' => 'image_scale',
+      'name' => 'image.style.effects.scale',
       'data' => array(
         'width' => 120,
         'height' => 90,
@@ -123,7 +123,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Test upscale disabled.
     $effect = array(
-      'name' => 'image_scale',
+      'name' => 'image.style.effects.scale',
       'data' => array(
         'width' => 400,
         'height' => 200,
@@ -145,7 +145,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Add a desaturate effect.
     $effect = array(
-      'name' => 'image_desaturate',
+      'name' => 'image.style.effects.desaturate',
       'data' => array(),
       'weight' => 4,
     );
@@ -163,7 +163,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Add a random rotate effect.
     $effect = array(
-      'name' => 'image_rotate',
+      'name' => 'image.style.effects.rotate',
       'data' => array(
         'degrees' => 180,
         'random' => TRUE,
@@ -182,7 +182,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Add a crop effect.
     $effect = array(
-      'name' => 'image_crop',
+      'name' => 'image.style.effects.crop',
       'data' => array(
         'width' => 30,
         'height' => 30,
@@ -204,7 +204,7 @@ class ImageDimensionsTest extends WebTestBase {
 
     // Rotate to a non-multiple of 90 degrees.
     $effect = array(
-      'name' => 'image_rotate',
+      'name' => 'image.style.effects.rotate',
       'data' => array(
         'degrees' => 57,
         'random' => FALSE,
diff --git a/core/modules/image/meta/image.style.%.yml b/core/modules/image/meta/image.style.%.yml
new file mode 100644
index 0000000..d7ac8e9
--- /dev/null
+++ b/core/modules/image/meta/image.style.%.yml
@@ -0,0 +1,11 @@
+name:
+  label: 'Machine name'
+label:
+  label: 'Label'
+  type: 'string'
+effects:
+  label: 'Style effects'
+  type: 'config_nested'
+  config_base: 'image.style.effects.*'
+  config_key: 'name'
+  
\ No newline at end of file
diff --git a/core/modules/image/meta/image.style.effects.%.yml b/core/modules/image/meta/image.style.effects.%.yml
new file mode 100644
index 0000000..618963e
--- /dev/null
+++ b/core/modules/image/meta/image.style.effects.%.yml
@@ -0,0 +1,9 @@
+name:
+  label: 'Style name'
+data:
+  label: 'Data'
+weight:
+  label: 'Weight'
+  type: 'integer'
+ieid:
+  label: 'IEID'
diff --git a/core/modules/image/meta/image.style.effects.scale.yml b/core/modules/image/meta/image.style.effects.scale.yml
new file mode 100644
index 0000000..3fda284
--- /dev/null
+++ b/core/modules/image/meta/image.style.effects.scale.yml
@@ -0,0 +1,10 @@
+data:
+  width:
+    label: 'Width'
+    type: 'integer'
+  height:
+    label: 'Height'
+    type: 'integer'
+  upscale:
+    label: 'Upscale'
+    type: 'boolean'
diff --git a/core/modules/system/meta/system.maintenance.yml b/core/modules/system/meta/system.maintenance.yml
new file mode 100644
index 0000000..0f4cd6d
--- /dev/null
+++ b/core/modules/system/meta/system.maintenance.yml
@@ -0,0 +1,6 @@
+enabled:
+  label: 'Put site into maintenance mode'
+  type: 'boolean'
+message:
+  label: 'Message to display when in maintenance mode'
+  type: 'string'
diff --git a/core/modules/system/meta/system.site.yml b/core/modules/system/meta/system.site.yml
new file mode 100644
index 0000000..f03d92f
--- /dev/null
+++ b/core/modules/system/meta/system.site.yml
@@ -0,0 +1,15 @@
+name:
+  label: 'Site name'
+  type: 'string'
+mail:
+  label: 'Site mail'
+slogan:
+  label: 'Site slogan'
+  type: 'string'
+page:
+  403:
+    label: 'Default 403 (access denied) page'
+  404:
+    label: 'Default 404 (not found) page'
+  front:
+    label: 'Default front page'
\ No newline at end of file
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index bfa91db..2ed75ab 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1947,6 +1947,11 @@ function system_stream_wrappers() {
  */
 function system_data_type_info() {
   return array(
+    'undefined' => array(
+      'label' => t('Undefined'),
+      'class' => '\Drupal\Core\TypedData\Type\Undefined',
+      'primitive type' => Primitive::UNDEFINED,
+    ),
     'boolean' => array(
       'label' => t('Boolean'),
       'class' => '\Drupal\Core\TypedData\Type\Boolean',
