diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+Copyright (c) 2009, Will Donnelly
+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 the software 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 THE COPYRIGHT HOLDER OR CONTRIBUTORS 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,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/System/IO/Storage.hs b/System/IO/Storage.hs
new file mode 100644
--- /dev/null
+++ b/System/IO/Storage.hs
@@ -0,0 +1,99 @@
+{- |
+Conceptually, this library provides a way to arbitrarily extend the
+global state represented by the IO monad. Viewed another way, this
+library provides a basic facility for setting and retrieving values
+from global variables.
+
+The interface takes the form of a very basic key-value store, with
+multiple different stores made available through the 'withStore'
+function. Stores are referenced by arbitrary strings, and keys
+within those stores are treated likewise. The 'putValue', 'getValue',
+and 'delValue' functions allow you to store, retrieve, and delete
+data from the store.
+
+Internally, data is stored within an IORef which is created using the
+'unsafePerformIO hack', but this is hidden within the library so that
+it can easily be modified if and when a more 'proper' solution is
+implemented.
+-}
+module System.IO.Storage
+  ( withStore
+  , putValue
+  , getValue
+  , getDefaultValue
+  , delValue
+  ) where
+
+import Data.IORef        ( IORef, newIORef, modifyIORef, readIORef )
+import Data.List as L    ( lookup, deleteFirstsBy )
+import Data.Map as M     ( Map, empty, lookup, insert, delete )
+import Data.Dynamic      ( Dynamic, toDyn, fromDyn, fromDynamic )
+import Data.Typeable     ( Typeable )
+import Data.Function     ( on )
+import Control.Exception ( bracket )
+import System.IO.Unsafe  ( unsafePerformIO )
+
+type ValueStore = M.Map String Dynamic
+
+-- | This is the magic bit that makes the data-stores global to the
+--   entire program. Sure, it cheats a little, but who doesn't?
+globalPeg :: IORef [(String, IORef ValueStore)]
+{-# NOINLINE globalPeg #-}
+globalPeg = unsafePerformIO (newIORef [])
+
+-- | Create a named key-value store, and then execute the given
+--   IO action within its extent. Calls to 'withStore' can be
+--   nested, and calling it again with the name of a data-store
+--   that has already been initialized will cause the original
+--   to be shadowed for the duration of the call to 'withStore'.
+withStore :: String -> IO a -> IO a
+withStore storeName action = do
+    store <- newIORef M.empty
+    let emptyStore = (storeName, store)
+    let create = modifyIORef globalPeg (emptyStore:)
+    let delete = modifyIORef globalPeg deleteStore
+    bracket create (const delete) (const action)
+  where deleteStore xs = deleteFirstsBy ((==) `on` fst) xs dummyStore
+        dummyStore = [(storeName, undefined)]
+
+getPrimitive :: String -> String -> IO (Maybe Dynamic)
+getPrimitive storeName key = do
+    storeList <- readIORef globalPeg
+    case storeName `L.lookup` storeList of
+         Nothing -> return Nothing
+         Just st -> do map <- readIORef st
+                       return $ key `M.lookup` map
+
+-- | Get a value from the given data-store, if it exists. If it
+--   doesn't exist, obviously, 'Nothing' will be returned.
+getValue :: Typeable a => String -> String -> IO (Maybe a)
+getValue storeName key = do
+    value <- getPrimitive storeName key
+    case value of
+         Nothing -> return $ Nothing
+         Just dy -> return $ fromDynamic dy
+
+-- | Get a value from the given store, with a default if it
+--   doesn't exist.
+getDefaultValue :: Typeable a => String -> String -> a -> IO a
+getDefaultValue storeName key val = do
+    value <- getPrimitive storeName key
+    case value of
+         Nothing -> return $ val
+         Just dy -> return $ fromDyn dy val
+
+-- | Put a value into the given data-store.
+putValue :: Typeable a => String -> String -> a -> IO ()
+putValue storeName key value = do
+    storeList <- readIORef globalPeg
+    case storeName `L.lookup` storeList of
+         Nothing -> return ()
+         Just st -> modifyIORef st . M.insert key . toDyn $ value
+
+-- | Delete a value from the given data-store.
+delValue :: String -> String -> IO ()
+delValue storeName key = do
+    storeList <- readIORef globalPeg
+    case storeName `L.lookup` storeList of
+         Nothing -> return ()
+         Just st -> modifyIORef st . M.delete $ key
diff --git a/io-storage.cabal b/io-storage.cabal
new file mode 100644
--- /dev/null
+++ b/io-storage.cabal
@@ -0,0 +1,30 @@
+name:          io-storage
+version:       0.3
+category:      Database, Data, System
+synopsis:      A key-value store in the IO monad.
+
+description:   This library allows an application to extend the 'global state'
+               hidden inside the IO monad with semi-arbitrary data. Data is
+               required to be 'Typeable'. The library provides an essentially
+               unbounded number of key-value stores indexed by strings, with
+               each key within the stores also being a string.
+
+homepage:      http://github.com/willdonnelly/io-storage
+bug-reports:   http://github.com/willdonnelly/io-storage/issues
+stability:     alpha
+author:        Will Donnelly
+maintainer:    Will Donnelly <will.donnelly@gmail.com>
+copyright:     (c) 2009 Will Donnelly
+license:       BSD3
+license-file:  LICENSE
+
+build-type:    Simple
+cabal-version: >= 1.6
+
+library
+  exposed-modules: System.IO.Storage
+  build-depends:   base >= 4 && < 5, containers
+
+source-repository head
+  type:      git
+  location:  git://github.com/willdonnelly/io-storage.git
