diff --git a/app/assets/javascripts/discourse/components/discourse-banner.js.es6 b/app/assets/javascripts/discourse/components/discourse-banner.js.es6
index a3f8f07d67c..56374168e6c 100644
--- a/app/assets/javascripts/discourse/components/discourse-banner.js.es6
+++ b/app/assets/javascripts/discourse/components/discourse-banner.js.es6
@@ -5,7 +5,7 @@ export default VisibleComponent.extend({
   visible: function () {
     var bannerKey = this.get("banner.key"),
         dismissedBannerKey = this.get("user.dismissed_banner_key") ||
-                             Discourse.KeyValueStore.get("dismissed_banner_key");
+                             this.keyValueStore.get("dismissed_banner_key");
 
     if (bannerKey) { bannerKey = parseInt(bannerKey, 10); }
     if (dismissedBannerKey) { dismissedBannerKey = parseInt(dismissedBannerKey, 10); }
@@ -19,10 +19,9 @@ export default VisibleComponent.extend({
         this.get("user").dismissBanner(this.get("banner.key"));
       } else {
         this.set("visible", false);
-        Discourse.KeyValueStore.set({ key: "dismissed_banner_key", value: this.get("banner.key") });
+        this.keyValueStore.set({ key: "dismissed_banner_key", value: this.get("banner.key") });
       }
     }
-  },
-
+  }
 
 });
diff --git a/app/assets/javascripts/discourse/initializers/logout.js.es6 b/app/assets/javascripts/discourse/initializers/logout.js.es6
index bc3fe49e86f..dc0ff59c81d 100644
--- a/app/assets/javascripts/discourse/initializers/logout.js.es6
+++ b/app/assets/javascripts/discourse/initializers/logout.js.es6
@@ -6,11 +6,12 @@ export default {
   after: "message-bus",
 
   initialize: function (container) {
-    const messageBus = container.lookup('message-bus:main'),
-          siteSettings = container.lookup('site-settings:main');
+    const messageBus = container.lookup('message-bus:main');
+    const siteSettings = container.lookup('site-settings:main');
+    const keyValueStore = container.lookup('key-value-store:main');
 
     if (!messageBus) { return; }
-    const callback = () => logout(siteSettings);
+    const callback = () => logout(siteSettings, keyValueStore);
 
     messageBus.subscribe("/logout", function () {
       bootbox.dialog(I18n.t("logout"), {label: I18n.t("refresh"), callback}, {onEscape: callback, backdrop: 'static'});
diff --git a/app/assets/javascripts/discourse/initializers/message-bus.js.es6 b/app/assets/javascripts/discourse/initializers/message-bus.js.es6
index 4b355ea0547..be9de5c3263 100644
--- a/app/assets/javascripts/discourse/initializers/message-bus.js.es6
+++ b/app/assets/javascripts/discourse/initializers/message-bus.js.es6
@@ -21,7 +21,6 @@ export default {
 
     messageBus.alwaysLongPoll = Discourse.Environment === "development";
     messageBus.start();
-    Discourse.KeyValueStore.init("discourse_", messageBus);
 
     messageBus.callbackInterval = siteSettings.anon_polling_interval;
     messageBus.backgroundCallbackInterval = siteSettings.background_polling_interval;
diff --git a/app/assets/javascripts/discourse/lib/key-value-store.js.es6 b/app/assets/javascripts/discourse/lib/key-value-store.js.es6
new file mode 100644
index 00000000000..112da6de7f0
--- /dev/null
+++ b/app/assets/javascripts/discourse/lib/key-value-store.js.es6
@@ -0,0 +1,49 @@
+// A simple key value store that uses LocalStorage
+let safeLocalStorage;
+
+try {
+  safeLocalStorage = localStorage;
+  if (localStorage["disableLocalStorage"] === "true") {
+    safeLocalStorage = null;
+  }
+} catch(e){
+  // cookies disabled, we don't care
+  safeLocalStorage = null;
+}
+
+
+const KeyValueStore = function(ctx) {
+  this.context = ctx;
+}
+
+KeyValueStore.prototype = {
+  abandonLocal() {
+    if (!safeLocalStorage) { return; }
+
+    let i = safeLocalStorage.length - 1;
+    while (i >= 0) {
+      let k = safeLocalStorage.key(i);
+      if (k.substring(0, this.context.length) === this.context) {
+        safeLocalStorage.removeItem(k);
+      }
+      i--;
+    }
+    return true;
+  },
+
+  remove(key) {
+    return safeLocalStorage.removeItem(this.context + key);
+  },
+
+  set(opts) {
+    if (!safeLocalStorage) { return false; }
+    safeLocalStorage[this.context + opts.key] = opts.value;
+  },
+
+  get(key) {
+    if (!safeLocalStorage) { return null; }
+    return safeLocalStorage[this.context + key];
+  }
+};
+
+export default KeyValueStore;
diff --git a/app/assets/javascripts/discourse/lib/key_value_store.js b/app/assets/javascripts/discourse/lib/key_value_store.js
deleted file mode 100644
index 2ae20f5e6e5..00000000000
--- a/app/assets/javascripts/discourse/lib/key_value_store.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
-  A simple key value store that uses LocalStorage
-
-  @class KeyValueStore
-  @namespace Discourse
-  @module Discourse
-**/
-var safeLocalStorage;
-
-try {
-  safeLocalStorage = localStorage;
-  if (localStorage["disableLocalStorage"] === "true") {
-    safeLocalStorage = null;
-  }
-} catch(e){
- // cookies disabled, we don't care
- safeLocalStorage = null;
-}
-
-Discourse.KeyValueStore = {
-  initialized: false,
-  context: "",
-
-  init: function(ctx) {
-    this.initialized = true;
-    this.context = ctx;
-  },
-
-  abandonLocal: function() {
-    var i, k;
-    if (!(safeLocalStorage && this.initialized)) {
-      return;
-    }
-    i = safeLocalStorage.length - 1;
-    while (i >= 0) {
-      k = safeLocalStorage.key(i);
-      if (k.substring(0, this.context.length) === this.context) {
-        safeLocalStorage.removeItem(k);
-      }
-      i--;
-    }
-    return true;
-  },
-
-  remove: function(key) {
-    return safeLocalStorage.removeItem(this.context + key);
-  },
-
-  set: function(opts) {
-    if (!safeLocalStorage && this.initialized) {
-      return false;
-    }
-    safeLocalStorage[this.context + opts.key] = opts.value;
-  },
-
-  get: function(key) {
-    if (!safeLocalStorage) {
-      return null;
-    }
-    return safeLocalStorage[this.context + key];
-  }
-};
-
diff --git a/app/assets/javascripts/discourse/lib/logout.js.es6 b/app/assets/javascripts/discourse/lib/logout.js.es6
index 8dc0539b898..2fe4078a69f 100644
--- a/app/assets/javascripts/discourse/lib/logout.js.es6
+++ b/app/assets/javascripts/discourse/lib/logout.js.es6
@@ -1,5 +1,5 @@
-export default function logout(siteSettings) {
-  Discourse.KeyValueStore.abandonLocal();
+export default function logout(siteSettings, keyValueStore) {
+  keyValueStore.abandonLocal();
 
   const redirect = siteSettings.logout_redirect;
   if (Ember.isEmpty(redirect)) {
diff --git a/app/assets/javascripts/discourse/models/composer.js.es6 b/app/assets/javascripts/discourse/models/composer.js.es6
index 9317bb183bf..ad4115c8bbf 100644
--- a/app/assets/javascripts/discourse/models/composer.js.es6
+++ b/app/assets/javascripts/discourse/models/composer.js.es6
@@ -281,7 +281,7 @@ const Composer = RestModel.extend({
   }.property('reply'),
 
   _setupComposer: function() {
-    const val = (Discourse.Mobile.mobileView ? false : (Discourse.KeyValueStore.get('composer.showPreview') || 'true'));
+    const val = (Discourse.Mobile.mobileView ? false : (this.keyValueStore.get('composer.showPreview') || 'true'));
     this.set('showPreview', val === 'true');
     this.set('archetypeId', this.site.get('default_archetype'));
   }.on('init'),
@@ -336,7 +336,7 @@ const Composer = RestModel.extend({
 
   togglePreview() {
     this.toggleProperty('showPreview');
-    Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
+    this.keyValueStore.set({ key: 'composer.showPreview', value: this.get('showPreview') });
   },
 
   applyTopicTemplate: function() {
@@ -731,6 +731,7 @@ Composer.reopenClass({
     }
   },
 
+  // TODO: Replace with injection
   create(args) {
     args = args || {};
     args.user = args.user || Discourse.User.current();
diff --git a/app/assets/javascripts/discourse/models/rest.js.es6 b/app/assets/javascripts/discourse/models/rest.js.es6
index 0c595bb7359..60d011bfc31 100644
--- a/app/assets/javascripts/discourse/models/rest.js.es6
+++ b/app/assets/javascripts/discourse/models/rest.js.es6
@@ -78,10 +78,13 @@ RestModel.reopenClass({
 
   create(args) {
     args = args || {};
-    if (!args.store) {
+    if (!args.store || !args.keyValueStore) {
       const container = Discourse.__container__;
       // Ember.warn('Use `store.createRecord` to create records instead of `.create()`');
       args.store = container.lookup('store:main');
+
+      // TODO: Remove this when composer is using the store fully
+      args.keyValueStore = container.lookup('key-value-store:main');
     }
 
     args.__munge = this.munge;
diff --git a/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
index 736f630f7cd..44559b6d2f1 100644
--- a/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
+++ b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6
@@ -1,4 +1,5 @@
 import Session from 'discourse/models/session';
+import KeyValueStore from 'discourse/lib/key-value-store';
 import AppEvents from 'discourse/lib/app-events';
 import Store from 'discourse/models/store';
 import DiscourseURL from 'discourse/lib/url';
@@ -8,7 +9,7 @@ import SearchService from 'discourse/services/search';
 function inject() {
   const app = arguments[0],
         name = arguments[1],
-        singletonName = Ember.String.underscore(name).replace(/_/, '-') + ':main';
+        singletonName = Ember.String.underscore(name).replace(/_/g, '-') + ':main';
 
   Array.prototype.slice.call(arguments, 2).forEach(dest => app.inject(dest, name, singletonName));
 }
@@ -49,5 +50,9 @@ export default {
     injectAll(app, 'messageBus');
 
     app.register('location:discourse-location', DiscourseLocation);
+
+    const keyValueStore = new KeyValueStore("discourse_");
+    app.register('key-value-store:main', keyValueStore, { instantiate: false });
+    injectAll(app, 'keyValueStore');
   }
 };
diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6
index 443d0e9c8b8..3a05163f601 100644
--- a/app/assets/javascripts/discourse/routes/application.js.es6
+++ b/app/assets/javascripts/discourse/routes/application.js.es6
@@ -19,7 +19,7 @@ const ApplicationRoute = Discourse.Route.extend(OpenComposer, {
   actions: {
 
     logout() {
-      this.currentUser.destroySession().then(() => logout(this.siteSettings));
+      this.currentUser.destroySession().then(() => logout(this.siteSettings, this.keyValueStore));
     },
 
     _collectTitleTokens(tokens) {
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js
index 69396e6c98e..25238bf9235 100644
--- a/app/assets/javascripts/main_include.js
+++ b/app/assets/javascripts/main_include.js
@@ -18,6 +18,7 @@
 //= require ./discourse/lib/url
 //= require ./discourse/lib/debounce
 //= require ./discourse/lib/quote
+//= require ./discourse/lib/key-value-store
 //= require ./discourse/helpers/i18n
 //= require ./discourse/helpers/fa-icon
 //= require ./discourse/helpers/register-unbound
diff --git a/test/javascripts/lib/key-value-store-test.js.es6 b/test/javascripts/lib/key-value-store-test.js.es6
index 0ddf9652db0..8f1b0ae4003 100644
--- a/test/javascripts/lib/key-value-store-test.js.es6
+++ b/test/javascripts/lib/key-value-store-test.js.es6
@@ -1,20 +1,18 @@
-var store = Discourse.KeyValueStore;
+import KeyValueStore from "discourse/lib/key-value-store";
 
-module("Discourse.KeyValueStore", {
-  setup: function() {
-    store.init("test");
-  }
-});
+module("lib:key-value-store");
 
-test("it's able to get the result back from the store", function() {
+test("it's able to get the result back from the store", (assert) => {
+  const store = new KeyValueStore("_test");
   store.set({ key: "bob", value: "uncle" });
-  equal(store.get("bob"), "uncle");
+  assert.equal(store.get("bob"), "uncle");
 });
 
-test("is able to nuke the store", function() {
+test("is able to nuke the store", (assert) => {
+  const store = new KeyValueStore("_test");
   store.set({ key: "bob1", value: "uncle" });
   store.abandonLocal();
   localStorage.a = 1;
-  equal(store.get("bob1"), void 0);
-  equal(localStorage.a, "1");
+  assert.equal(store.get("bob1"), void 0);
+  assert.equal(localStorage.a, "1");
 });
diff --git a/test/javascripts/models/composer-test.js.es6 b/test/javascripts/models/composer-test.js.es6
index e6bb0d74d1c..08559b8bca8 100644
--- a/test/javascripts/models/composer-test.js.es6
+++ b/test/javascripts/models/composer-test.js.es6
@@ -1,14 +1,18 @@
 import { blank } from 'helpers/qunit-helpers';
 import { currentUser } from 'helpers/qunit-helpers';
+import KeyValueStore from 'discourse/lib/key-value-store';
 
 module("model:composer");
 
+const keyValueStore = new KeyValueStore("_test_composer");
+
 function createComposer(opts) {
   opts = opts || {};
   opts.user = opts.user || currentUser();
   opts.site = Discourse.Site.current();
   opts.siteSettings = Discourse.SiteSettings;
-  return Discourse.Composer.create(opts);
+  opts.keyValueStore = keyValueStore;
+; return Discourse.Composer.create(opts);
 }
 
 test('replyLength', function() {
@@ -184,9 +188,9 @@ test('showPreview', function() {
   Discourse.Mobile.mobileView = true;
   equal(newComposer().get('showPreview'), false, "Don't show preview in mobile view");
 
-  Discourse.KeyValueStore.set({ key: 'composer.showPreview', value: 'true' });
+  keyValueStore.set({ key: 'composer.showPreview', value: 'true' });
   equal(newComposer().get('showPreview'), false, "Don't show preview in mobile view even if KeyValueStore wants to");
-  Discourse.KeyValueStore.remove('composer.showPreview');
+  keyValueStore.remove('composer.showPreview');
 
   Discourse.Mobile.mobileView = false;
   equal(newComposer().get('showPreview'), true, "Show preview by default in desktop view");