diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+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.
+
+  * The names of its contributors may not 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 OWNER 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,6 @@
+module Main ( main ) where
+
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/hackage-db.cabal b/hackage-db.cabal
new file mode 100644
--- /dev/null
+++ b/hackage-db.cabal
@@ -0,0 +1,51 @@
+Name:                   hackage-db
+Version:                1.0
+Copyright:              Peter Simons
+License:                BSD3
+License-File:           LICENSE
+Author:                 Peter Simons <simons@cryp.to>
+Maintainer:             Peter Simons <simons@cryp.to>
+Homepage:               http://github.com/peti/hackage-db
+Category:               Distribution
+Synopsis:               provide access to the Hackage database via Data.Map
+Cabal-Version:          >= 1.8
+Build-Type:             Simple
+Tested-With:            GHC == 6.12.3, GHC == 7.0.4, GHC == 7.2.1
+Description:
+   This module provides simple access to the Hackage database by means
+   of @Data.Map@. Suppose you wanted to implement a utility that queries
+   the set of available versions for a given package, the following
+   program would do the trick:
+   .
+   > import qualified Distribution.Hackage.DB as DB
+   > import Distribution.Text ( display )
+   > import System.Environment ( getArgs )
+   >
+   > main :: IO ()
+   > main = do
+   >   pkgs <- getArgs
+   >   db <- DB.readHackage
+   >   let getVersions name = maybe [] DB.keys (DB.lookup name db)
+   >   mapM_ (putStrLn . unwords . map display . getVersions) pkgs
+   .
+   When run, it would produce the following output:
+   .
+   > ./a.out containers deepseq cabal-install
+   > 0.1.0.0 0.1.0.1 0.2.0.0 0.2.0.1 0.3.0.0 0.4.0.0
+   > 1.0.0.0 1.1.0.0 1.1.0.1 1.1.0.2
+   > 0.4.0 0.5.0 0.5.1 0.5.2 0.6.0 0.6.2 0.6.4 0.8.0 0.8.2 0.10.0 0.10.2
+   .
+   Note that once the database has been parsed, it can be accessed
+   quickly, but the inital cost of reading @00-index.tar@ is fairly
+   high.
+
+Source-Repository head
+  Type:                 git
+  Location:             git://github.com/peti/hackage-db.git
+
+Library
+  Build-Depends:        base >= 3 && < 5, tar, Cabal, containers,
+                        directory, filepath, bytestring
+  hs-source-dirs:       src
+  Ghc-Options:          -Wall
+  Exposed-Modules:      Distribution.Hackage.DB
diff --git a/src/Distribution/Hackage/DB.hs b/src/Distribution/Hackage/DB.hs
new file mode 100644
--- /dev/null
+++ b/src/Distribution/Hackage/DB.hs
@@ -0,0 +1,80 @@
+{- |
+   Module      :  Distribution.Hackage.DB
+   License     :  BSD3
+
+   Maintainer  :  simons@cryp.to
+   Stability   :  provisional
+   Portability :  portable
+
+   This module provides simple access to the Hackage database by means
+   of 'Map'. Note that once the database has been parsed, it can be
+   accessed quickly, but the inital cost of reading @00-index.tar@ is
+   fairly high.
+ -}
+
+module Distribution.Hackage.DB
+  ( Hackage, readHackage, readHackage', parseHackage
+  , module Data.Map
+  , module Data.Version
+  , module Distribution.Package
+  , module Distribution.PackageDescription
+  )
+  where
+
+import qualified Codec.Archive.Tar as Tar
+import Data.ByteString.Lazy.Char8 ( ByteString )
+import qualified Data.ByteString.Lazy.Char8 as BS
+import Data.Map
+import Data.Maybe
+import Data.Version
+import System.Directory
+import System.FilePath
+import Distribution.Package
+import Distribution.Text
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
+
+-- | A 'Map' representation of the Hackage database. For sake of
+-- simplicity, we use 'String' rather than 'PackageName' to represent
+-- the name of a package.
+
+type Hackage = Map String (Map Version GenericPackageDescription)
+
+-- | Read the Hackage database from
+-- @$HOME\/.cabal\/packages\/hackage.haskell.org\/00-index.tar@ and
+-- return a 'Map' that provides fast access to its contents. That @tar@
+-- file is typically created by running the command @\"cabal update\"@.
+
+readHackage :: IO Hackage
+readHackage = do
+  homedir <- getHomeDirectory
+  readHackage' (joinPath [ homedir, ".cabal", "packages", "hackage.haskell.org", "00-index.tar" ])
+
+-- | Read the Hackage database from the given 'FilePath' and return a
+-- 'Hackage' map that provides fast access to its contents.
+
+readHackage' :: FilePath -> IO Hackage
+readHackage' = fmap parseHackage . BS.readFile
+
+-- | Parse the contents of Hackage's @00-index.tar@ into a 'Hackage' map.
+
+parseHackage :: ByteString -> Hackage
+parseHackage = Tar.foldEntries addEntry empty error . Tar.read
+  where
+    addEntry :: Tar.Entry -> Hackage -> Hackage
+    addEntry e db = case splitDirectories (Tar.entryPath e) of
+                        path@[name,vers,_] -> case Tar.entryContent e of
+                                                Tar.NormalFile buf _ -> add name vers buf db
+                                                _                    -> error ("unexpected content of " ++ show path)
+                        _                  -> db
+
+    add :: String -> String -> ByteString -> Hackage -> Hackage
+    add name version pkg = insertWith union name (singleton (pVersion version) (pPackage name pkg))
+
+    pPackage :: String -> ByteString -> GenericPackageDescription
+    pPackage name buf = case parsePackageDescription (BS.unpack buf) of
+                          ParseOk _ a     -> a
+                          ParseFailed err -> error ("cannot parse cabal file " ++ show name ++ ": " ++ show err)
+
+    pVersion :: String -> Version
+    pVersion str = fromMaybe (error $ "cannot parse version " ++ show str) (simpleParse str)
