diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,3 @@
+## 0.2.4
+
+Added hackage-docs program
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+cabal-src is a package intended to help solve the Cabal diamond dependency
+problem.
+
+## The problem
+
+Let's say you have three packages. foo depends on the text package, and can use
+any version of it. bar depends on text as well, but requires version 0.10.\*.
+foobar depends on both of those.
+
+If you upload these three packages to Hackage and install foobar, cabal will
+build foo and bar against the same version of text, and then build foobar
+against them. However, if you install these packages locally, foo will be built
+against the most recent version of text (currently 0.11.something), and then
+foobar will be unbuildable, since its dependencies depend on different versions
+of text.
+
+You can see sample packages demonstrating the issue in the *example* folder.
+
+This is just one example of the diamond dependency issue. When dealing with
+complicated systems such as Yesod, with dozens of packages that are in
+development, the situation becomes much worse.
+
+## Our solution
+
+The important thing to note is that, if the packages are on Hackage, Cabal can
+handle the situation. The reason is that Cabal has enough information to
+calculate the correct versions of all dependencies to be used. So our goal is
+to give Cabal access to information on all dependencies, even those not yet on
+Hackage.
+
+Instead of installing a local package with "cabal install", you can now use
+"cabal-src-install". This program actually calls out to "cabal install", and if
+that build succeeds, will perform the following steps:
+
+1. Create a source tarball via "cabal sdist"
+
+2. Copy this tarball into a special location in your .cabal folder.
+
+3. Update a 00-index.tar file specifically kept for cabal-src.
+
+4. Update your .cabal/config file to recognize the special cabal-src folder as necessary.
+
+If you now install your "foo" and "bar" packages via "cabal-src-install", Cabal
+has full access to their source code. When it comes time to install foobar,
+Cabal can determine that foo can be recompiled with text 0.10 and will do so
+automatically.
+
+## Project status
+
+This software should be considered alpha. We'll likely be using it for all
+Yesod development going forward, so I expect that alpha to be upgraded to beta
+and finally production quality in short order. All feedback is welcome!
+
+## Usage
+
+Simply replace a call to "cabal install" with a call to "cabal-src-install".
+If you would like to only install the source tarball without actually
+installing the binary package, run it with "cabal-src-install --src-only".
+
+## mega-sdist
+
+This package now also includes the mega-sdist util, which handles uploading to
+Hackage from mega repos.
+
+Compares local code against version on Hackage. Accepts the following options:
+
+* __--gittag__: Automatically tag as well.
+* __--test__: Automatically run cabal tests
+
+Uses sources.txt to determine which packages to build.
diff --git a/cabal-src.cabal b/cabal-src.cabal
--- a/cabal-src.cabal
+++ b/cabal-src.cabal
@@ -1,5 +1,5 @@
 Name:                cabal-src
-Version:             0.2.3
+Version:             0.2.4
 Synopsis:            Alternative install procedure to avoid the diamond dependency issue.
 Description:         Please see the README.md file on Github for more information: <https://github.com/yesodweb/cabal-src/blob/master/README.md>.
 License:             BSD3
@@ -10,7 +10,7 @@
 Build-type:          Simple
 Cabal-version:       >=1.6
 homepage:            https://github.com/yesodweb/cabal-src
-
+extra-source-files:  README.md ChangeLog.md
 
 Executable cabal-src-install
   Main-is:             cabal-src-install.hs
@@ -39,6 +39,15 @@
                      , directory
                      , conduit-extra
                      , resourcet
+
+Executable hackage-docs
+  Main-is:             hackage-docs.hs
+  Build-depends:       base
+                     , http-client
+                     , http-client-tls
+                     , temporary
+                     , classy-prelude-conduit
+                     , Cabal
 
 source-repository head
   type:     git
diff --git a/hackage-docs.hs b/hackage-docs.hs
new file mode 100644
--- /dev/null
+++ b/hackage-docs.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE NoImplicitPrelude, OverloadedStrings #-}
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Parse
+import Distribution.Verbosity
+import Distribution.Package
+import ClassyPrelude.Conduit
+import Distribution.Text (display)
+import Data.Conduit.Process
+import System.IO.Temp
+import Network.HTTP.Client
+import Network.HTTP.Client.MultipartFormData
+import Network.HTTP.Client.TLS
+
+main :: IO ()
+main = withManager tlsManagerSettings $ \man -> do
+    rawCreds <- readFile "/hackage-creds" `catchIO` \_ ->
+        error "This tool requires a file /hackage-creds to be present and readable,\ncontaining your username and password for Hackage"
+    (username, password) <-
+        case words $ decodeUtf8 rawCreds of
+            [x, y] -> return (encodeUtf8 x, encodeUtf8 y)
+            _ -> error "/hackage-creds didn't look like how I wanted"
+    srcsRaw <- readFile "sources.txt" `catchIO` \_ -> return "."
+    forM_ (lines $ decodeUtf8 srcsRaw) (processDir username password man . fpFromText)
+
+terror :: Text -> a
+terror = error . unpack
+
+processDir :: ByteString -> ByteString -> Manager -> FilePath -> IO ()
+processDir username password man dir = do
+    putStrLn $ "Processing " ++ fpToText dir
+    cabals <- runResourceT $ sourceDirectory dir $$ foldMapC
+        (\fp -> if hasExtension fp "cabal"
+                    then asSet $ singletonSet fp
+                    else mempty)
+    cabal <- case setToList cabals of
+        [] -> terror $ "No cabal files found in " ++ fpToText dir
+        [x] -> return x
+        xs -> terror $ "Multiple cabal files found in " ++ fpToText dir ++ ": " ++ tshow xs
+    gpd <- readPackageDescription normal $ fpToString cabal
+    case condLibrary gpd of
+        Nothing -> putStrLn $ concat
+            [ "No library in "
+            , fpToText cabal
+            , ", skipping"
+            ]
+        Just _ -> do
+            let pd = packageDescription gpd
+                PackageIdentifier name' version' = package pd
+                name = pack $ display name'
+                version = pack $ display version'
+            runIn dir "cabal"
+                [ "haddock"
+                , "--hoogle"
+                , "--hyperlink-source"
+                , "--html-location=/package/$pkg-$version/docs"
+                , "--contents-location=/package/$pkg-$version"
+                ]
+            withSystemTempDirectory "build-docs.XXXXXX" $ \tmp -> do
+                let docsdir = concat
+                        [ pack tmp
+                        , "/"
+                        , name
+                        , "-"
+                        , version
+                        , "-docs"
+                        ]
+                    tarball = docsdir ++ ".tar.gz"
+                runIn dir "cp"
+                    [ "-R"
+                    , "dist/doc/html/" ++ name
+                    , docsdir
+                    ]
+                runIn dir "tar"
+                    [ "cvz"
+                    , "-C"
+                    , pack tmp
+                    , "--format=ustar"
+                    , "-f"
+                    , tarball
+                    , concat
+                        [ name
+                        , "-"
+                        , version
+                        , "-docs"
+                        ]
+                    ]
+                let url = unpack $ concat
+                        [ "https://hackage.haskell.org/package/"
+                        , name
+                        , "-"
+                        , version
+                        , "/docs"
+                        ]
+                bs <- readFile $ fpFromText tarball
+                let req = applyBasicAuth username password $ (fromString url)
+                        { method = "PUT"
+                        , requestHeaders =
+                            [ ("Content-Type", "application/x-tar")
+                            , ("Content-Encoding", "gzip")
+                            ]
+                        , requestBody = RequestBodyBS bs
+                        }
+                httpLbs req man >>= print
+
+runIn :: FilePath -> Text -> [Text] -> IO ()
+runIn dir cmd args = do
+    print (dir, cmd, args)
+    withCheckedProcess cp $ \ClosedStream Inherited Inherited -> return ()
+  where
+    cp = (proc (unpack cmd) (map unpack args)) { cwd = Just $ fpToString dir }
