diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2014, foreign-store
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of foreign-store nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/cbits/store.c b/cbits/store.c
new file mode 100644
--- /dev/null
+++ b/cbits/store.c
@@ -0,0 +1,45 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+void **values = NULL;
+size_t size = 0;
+
+uint32_t x_store(void*value) {
+  int no_slot = 1;
+  uint32_t i;
+  uint32_t index;
+  for (i = 0; i < size; i++) {
+    if (values[i] == NULL) {
+      index = i;
+      no_slot = 0;
+      break;
+    }
+  }
+  if (values == NULL || no_slot) {
+    size++;
+    values = realloc(values,size * sizeof(void*));
+    index = size-1;
+  }
+  values[index] = value;
+  return index;
+}
+
+void *x_get(uint32_t index) {
+  return values[index];
+}
+
+uint32_t x_lookup(uint32_t index) {
+  if (values == NULL)
+    return 0;
+  else {
+    if (index < size) {
+      return values[index] == NULL? 0 : 1;
+    } else {
+      return 0;
+    }
+  }
+}
+
+void x_delete(uint32_t index) {
+  values[index] = NULL;
+}
diff --git a/foreign-store.cabal b/foreign-store.cabal
new file mode 100644
--- /dev/null
+++ b/foreign-store.cabal
@@ -0,0 +1,20 @@
+name:                foreign-store
+version:             0.0
+synopsis:            Store a stable pointer in a foreign context to be retrieved later. Persists through GHCi reloads.
+description:         Store a stable pointer in a foreign context to be retrieved later. Persists through GHCi reloads.
+license:             BSD3
+license-file:        LICENSE
+author:              Chris Done
+maintainer:          chrisdone@gmail.com
+copyright:           2014 Chris Done
+category:            Development
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  hs-source-dirs:    src/
+  ghc-options:       -Wall -O2
+  exposed-modules:   Foreign.Store
+  build-depends:     base >= 4 && <5
+  c-sources:         cbits/store.c
+  include-dirs:      cbits
diff --git a/src/Foreign/Store.hs b/src/Foreign/Store.hs
new file mode 100644
--- /dev/null
+++ b/src/Foreign/Store.hs
@@ -0,0 +1,82 @@
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE ForeignFunctionInterface #-}
+
+-- | Store a stable pointer in a foreign context to be retrieved
+-- later. Persists through GHCi reloads.
+
+module Foreign.Store
+  (newStore
+  ,lookupStore
+  ,readStore
+  ,deleteStore
+  ,Store(..))
+  where
+
+import Control.Exception
+import Data.Typeable
+import Data.Word
+import Foreign.Ptr
+import Foreign.StablePtr
+
+data StoreException
+  = StoreNotFound
+  deriving (Show,Eq,Typeable)
+
+instance Exception StoreException
+
+-- | A hideously unsafe store. Only for use if you are suave.
+data Store a =
+  Store Word32
+  deriving (Show,Eq)
+
+-- | Lookup from the store.
+lookupStore :: Word32 -> IO (Maybe (Store a))
+lookupStore i =
+  do r <- x_lookup i
+     if r == 0
+        then return Nothing
+        else return (Just (Store i))
+
+-- | Make a new store. The internal vector of stores grows in
+-- side. When stores are deleted the vector does not shrink, but old
+-- slots are re-used.
+newStore :: a -> IO (Store a)
+newStore a =
+  do sptr <- newStablePtr a
+     i <- x_store sptr
+     return (Store i)
+
+-- | Read from the store. If the store has been deleted, this will
+-- throw an exception.
+readStore :: Store a -> IO a
+readStore (Store i) =
+  do sptr <- x_get i
+     if castStablePtrToPtr sptr == nullPtr
+        then throw StoreNotFound
+        else deRefStablePtr sptr
+
+-- | Frees the stable pointer for GC and frees up the slot in the
+-- store. Deleting an already deleted store is a no-op.
+deleteStore :: Store a -> IO ()
+deleteStore (Store i) = do
+  sptr <- x_get i
+  if castStablePtrToPtr sptr == nullPtr
+     then return ()
+     else do freeStablePtr sptr
+             x_delete i
+
+foreign import ccall
+  "x-helpers.h x_store"
+  x_store :: StablePtr a -> IO Word32
+
+foreign import ccall
+  "x-helpers.h x_get"
+  x_get :: Word32 -> IO (StablePtr a)
+
+foreign import ccall
+  "x-helpers.h x_lookup"
+  x_lookup :: Word32 -> IO Word32
+
+foreign import ccall
+  "x-helpers.h x_delete"
+  x_delete :: Word32 -> IO ()
