diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
new file mode 100644
index 0000000..9a6fce5
--- /dev/null
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -0,0 +1,108 @@
+<?php
+
+namespace Drupal\Core;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\HttpKernel;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Controller\ControllerResolver;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\EventListener\RouterListener;
+
+use Drupal\Core\EventSubscriber\HtmlSubscriber;
+use Drupal\Core\EventSubscriber\AccessSubscriber;
+use Drupal\Core\EventSubscriber\PathSubscriber;
+use Drupal\Core\EventSubscriber\LegacyControllerSubscriber;
+
+use Exception;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\DrupalApp.
+ */
+
+/**
+ * The DrupalApp class is the core of Drupal itself.
+ */
+class DrupalKernel implements HttpKernelInterface {
+
+  /**
+   *
+   * @param Request $request
+   *   The request to process.
+   * @return Response
+   *   The response object to return to the requesting user agent.
+   */
+  function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) {
+    try {
+
+      $dispatcher = $this->getDispatcher();
+
+      $matcher = $this->getMatcher($request);
+      $dispatcher->addSubscriber(new RouterListener($matcher));
+      $dispatcher->addSubscriber(new AccessSubscriber());
+      $dispatcher->addSubscriber(new PathSubscriber());
+      $dispatcher->addSubscriber(new LegacyControllerSubscriber());
+
+      $resolver = new ControllerResolver();
+
+      $kernel = new HttpKernel($dispatcher, $resolver);
+      $response = $kernel->handle($request);
+    }
+    catch (Exception $e) {
+      // Some other form of error occured that wasn't handled by another kernel
+      // listener.  That could mean that it's a method/mime-type/error
+      // combination that is not accounted for, or some other type of error.
+      // Either way, treat it as a server-level error and return an HTTP 500.
+      // By default, this will be an HTML-type response because that's a decent
+      // best guess if we don't know otherwise.
+      $response = new Response('A fatal error occurred: ' . $e->getMessage(), 500);
+    }
+
+    return $response;
+  }
+
+  /**
+   * Returns an EventDispatcher for the Kernel to use.
+   *
+   * The EventDispatcher is pre-wired with some event listeners/subscribers.
+   *
+   * @todo Make the listeners that get attached extensible, but without using
+   * hooks.
+   *
+   * @return EventDispatcher
+   */
+  protected function getDispatcher() {
+    $dispatcher = new EventDispatcher();
+
+    // @todo Make this extensible rather than just hard coding some.
+    // @todo Add a subscriber to handle other things, too, like our Ajax
+    // replacement system.
+    $dispatcher->addSubscriber(new HtmlSubscriber());
+
+    return $dispatcher;
+  }
+
+  /**
+   * Returns a UrlMatcher object for the specified request.
+   *
+   * @param Request $request
+   *   The request object for this matcher to use.
+   * @return UrlMatcher
+   */
+  protected function getMatcher(Request $request) {
+    // Resolve a routing context(path, etc) using the routes object to a
+    // Set a routing context to translate.
+    $context = new RequestContext();
+    $context->fromRequest($request);
+    $matcher = new UrlMatcher($context);
+
+    return $matcher;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
new file mode 100644
index 0000000..88ce042
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/AccessSubscriber.php
@@ -0,0 +1,54 @@
+<?php
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\AccessSubscriber
+ */
+
+/**
+ * Access subscriber for controller requests.
+ */
+class AccessSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Verifys that the current user can access the requested path.
+   *
+   * @todo This is a total hack to keep our current access system working. It
+   * should be replaced with something robust and injected at some point.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestAccessCheck(GetResponseEvent $event) {
+
+    $router_item = $event->getRequest()->attributes->get('drupal_menu_item');
+
+    if (!$router_item['access']) {
+      throw new AccessDeniedHttpException($message);
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestAccessCheck', 30);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlSubscriber.php
new file mode 100644
index 0000000..3c2e67f
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlSubscriber.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\HtmlSubscriber;
+ */
+
+/**
+ * Main subscriber for HTML-type HTTP responses.
+ */
+class HtmlSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Determines if we are dealing with an HTML-style response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   * @return boolean
+   *   True if it is an event we should process as HTML, False otherwise.
+   */
+  protected function isHtmlRequestEvent(GetResponseEvent $event) {
+    return in_array('text/html', $event->getRequest()->getAcceptableContentTypes());
+  }
+
+  /**
+   * Processes an AccessDenied exception into an HTTP 403 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onAccessDeniedException(GetResponseEvent $event) {
+    if ($this->isHtmlRequestEvent($event) && $event->getException() instanceof AccessDeniedHttpException) {
+      $event->setResponse(new Response('Access Denied', 403));
+    }
+  }
+
+  /**
+   * Processes a NotFound exception into an HTTP 404 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onNotFoundHttpException(GetResponseEvent $event) {
+    if ($this->isHtmlRequestEvent($event) && $event->getException() instanceof NotFoundHttpException) {
+      $event->setResponse(new Response('Not Found', 404));
+    }
+  }
+
+  /**
+   * Processes a MethodNotAllowed exception into an HTTP 405 response.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onMethodAllowedException(GetResponseEvent $event) {
+    if ($this->isHtmlRequestEvent($event) && $event->getException() instanceof MethodNotAllowedException) {
+      $event->setResponse(new Response('Method Not Allowed', 405));
+    }
+  }
+
+  /**
+   * Processes a successful controller into an HTTP 200 response.
+   *
+   * Some controllers may not return a response object but simply the body of
+   * one.  The VIEW event is called in that case, to allow us to mutate that
+   * body into a Response object.  In particular we assume that the return
+   * from an HTML-type response is a render array from a legacy page callback
+   * and render it.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onView(GetResponseEvent $event) {
+    if ($this->isHtmlRequestEvent($event)) {
+      $page_callback_result = $event->getControllerResult();
+      $event->setResponse(new Response(drupal_render_page($page_callback_result)));
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::EXCEPTION][] = array('onNotFoundHttpException');
+    $events[KernelEvents::EXCEPTION][] = array('onAccessDeniedException');
+    $events[KernelEvents::EXCEPTION][] = array('onMethodAllowedException');
+
+    $events[KernelEvents::VIEW][] = array('onView');
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
new file mode 100644
index 0000000..e85d89f
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/LegacyControllerSubscriber.php
@@ -0,0 +1,64 @@
+<?php
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\LegacyControllerSubscriber
+ */
+
+/**
+ * Access subscriber for controller requests.
+ */
+class LegacyControllerSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Wraps legacy controllers in a closure to handle old-style arguments.
+   *
+   * This is a backward compatibility layer only.  This is a rather ugly way
+   * to piggyback Drupal's existing menu router items onto the Symfony model,
+   * but it works for now.  If we did not do this, any menu router item with
+   * a variable number of arguments would fail to work.  This bypasses Symfony's
+   * controller argument handling entirely and lets the old-style approach work.
+   *
+   * @todo Convert Drupal to use the IETF-draft-RFC style {placeholders}. That
+   * will allow us to use the native Symfony conversion, including out-of-order
+   * argument mapping, name-based mapping, and with another listener
+   * auto-conversion of parameters to full objects.  That may necessitate not
+   * using func_get_args()-based controllers.  That is likely for the best,
+   * as those are quite hard to document anyway.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelControllerLegacy(FilterControllerEvent $event) {
+    $router_item = $event->getRequest()->attributes->get('drupal_menu_item');
+    $controller = $event->getController();
+
+    // This BC logic applies only to functions.  Otherwise, skip it.
+    if (function_exists($controller)) {
+      $new_controller = function() use ($router_item) {
+        return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
+      };
+      $event->setController($new_controller);
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::CONTROLLER][] = array('onKernelControllerLegacy', 30);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
new file mode 100644
index 0000000..1487128
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/PathSubscriber.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Drupal\Core\EventSubscriber;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * @file
+ *
+ * Definition of Drupal\Core\EventSubscriber\AccessSubscriber
+ */
+
+/**
+ * Access subscriber for controller requests.
+ */
+class PathSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Resolve the system path.
+   *
+   * @todo The path system should be objectified to remove the function calls
+   * in this method.
+   *
+   * @todo We're writing back to $_GET['q'] for temporary BC. All instances of
+   * $_GET['q'] should be removed and then this code eliminated.
+   *
+   * @param GetResponseEvent $event
+   *   The Event to process.
+   */
+  public function onKernelRequestPathResolve(GetResponseEvent $event) {
+    $request = $event->getRequest();
+
+    $path = ltrim($request->getPathInfo(), '/');
+
+    // Temporary BC shiv to support automated tests that still rely on old-
+    // style dirty URLs.
+    if (isset($_GET['q'])) {
+      $path = $_GET['q'];
+      $reflection = new \ReflectionObject($request);
+      $property = $reflection->getProperty('pathInfo');
+      $property->setAccessible(TRUE);
+      $property->setValue($request, '/' . $path);
+    }
+
+    if (empty($path)) {
+      // @todo Temporary hack. Fix when configuration is injectable.
+      $path = variable_get('site_frontpage', 'user');
+    }
+    $system_path = drupal_get_normal_path($path);
+
+    $request->attributes->set('system_path', $system_path);
+
+    // @todo Remove this line.
+    // Drupal uses $_GET['q'] directly in over 100 places at present,
+    // including writing back to it at times. Those are all critical bugs,
+    // even by Drupal 7 standards, but as many of the places that it does so
+    // are slated to be rewritten anyway we will save time and include this
+    // temporary hack. Removal of this line is a critical, Drupal-release
+    // blocking bug.
+    $_GET['q'] = $system_path;
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = array('onKernelRequestPathResolve', 100);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/UrlMatcher.php b/core/lib/Drupal/Core/UrlMatcher.php
new file mode 100644
index 0000000..366405a
--- /dev/null
+++ b/core/lib/Drupal/Core/UrlMatcher.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\Core;
+
+use Symfony\Component\Routing\Exception\MethodNotAllowedException;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\UrlMatcher as SymfonyUrlMatcher;
+use Symfony\Component\Routing\RequestContext;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * UrlMatcher matches URL based on a set of routes.
+ */
+class UrlMatcher extends SymfonyUrlMatcher {
+
+  protected $context;
+
+  /**
+   * Constructor.
+   *
+   * @param RequestContext  $context
+   *   The request context object.
+   */
+  public function __construct(RequestContext $context) {
+    $this->context = $context;
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @api
+   */
+  public function match($pathinfo) {
+
+    $this->allow = array();
+
+    // Symfony uses a prefixing / but we don't yet.
+    $dpathinfo = ltrim($pathinfo, '/');
+
+    // Do our fancy frontpage logic.
+    if (empty($dpathinfo)) {
+      $dpathinfo = variable_get('site_frontpage', 'user');
+      $pathinfo = '/' . $dpathinfo;
+    }
+
+    if ($router_item = $this->matchDrupalItem($dpathinfo)) {
+
+      $routes = new RouteCollection();
+      $routes->add(hash('sha256', $router_item['path']), $this->convertDrupalItem($router_item));
+
+      if ($ret = $this->matchCollection($pathinfo, $routes)) {
+        //drupal_set_message('<pre>' . var_export('test', TRUE) . '</pre>');
+        // Stash the router item in the attributes while we're transitioning.
+        $ret['drupal_menu_item'] = $router_item;
+
+        // Most legacy controllers (aka page callbacks) are in a separate file,
+        // so we have to include that.
+        if ($router_item['include_file']) {
+          require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
+        }
+
+        return $ret;
+      }
+    }
+
+    throw 0 < count($this->allow)
+      ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow)))
+      : new ResourceNotFoundException();
+  }
+
+  /**
+   * Get a drupal menu item.
+   *
+   * @todo Make this return multiple possible candidates for the resolver to
+   * consider.
+   *
+   * @param string $path
+   *   The path being looked up by
+   */
+  protected function matchDrupalItem($path) {
+    // For now we can just proxy our procedural method. At some point this will
+    // become more complicated because we'll need to get back candidates for a
+    // path and them resolve them based on things like method and scheme which
+    // we currently can't do.
+    return menu_get_item($path);
+  }
+
+  protected function convertDrupalItem($router_item) {
+    $route = array(
+      '_controller' => $router_item['page_callback']
+    );
+    // Place argument defaults on the route.
+    foreach ($router_item['page_arguments'] as $k => $v) {
+      $route[$k] = $v;
+    }
+    return new Route($router_item['href'], $route);
+  }
+}
diff --git a/index.php b/index.php
index b91fb1e..7609b3e 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,8 @@
 <?php
 
+use Drupal\Core\DrupalKernel;
+use Symfony\Component\HttpFoundation\Request;
+
 /**
  * @file
  * The PHP page that serves all page requests on a Drupal installation.
@@ -16,6 +19,12 @@
  */
 define('DRUPAL_ROOT', getcwd());
 
+// Bootstrap the lowest level of what we need.
 require_once DRUPAL_ROOT . '/core/includes/bootstrap.inc';
 drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
-menu_execute_active_handler();
+
+// A request object from the HTTPFoundation to tell us about the request.
+$request = Request::createFromGlobals();
+
+$kernel = new DrupalKernel();
+$kernel->handle($request)->send();
