diff --git a/Distribution/PackDeps.hs b/Distribution/PackDeps.hs
--- a/Distribution/PackDeps.hs
+++ b/Distribution/PackDeps.hs
@@ -21,6 +21,9 @@
     , getReverses
       -- * Helpers
     , diName
+      -- * Internal
+    , PackInfo (..)
+    , DescInfo (..)
     ) where
 
 import System.Directory (getAppUserDataDirectory)
@@ -29,6 +32,7 @@
 import Data.List (foldl', group, sort, isInfixOf, isPrefixOf)
 import Data.Time (UTCTime (UTCTime), addUTCTime)
 import Data.Maybe (mapMaybe, catMaybes)
+import Control.Exception (throw)
 
 import Distribution.Package
 import Distribution.PackageDescription
@@ -78,9 +82,9 @@
 parseNewest :: L.ByteString -> Newest
 parseNewest = foldl' addPackage Map.empty . entriesToList . Tar.read
 
-entriesToList :: Tar.Entries -> [Tar.Entry]
+entriesToList :: Tar.Entries Tar.FormatError -> [Tar.Entry]
 entriesToList Tar.Done = []
-entriesToList (Tar.Fail s) = error s
+entriesToList (Tar.Fail s) = throw s
 entriesToList (Tar.Next e es) = e : entriesToList es
 
 addPackage :: Newest -> Tar.Entry -> Newest
@@ -231,8 +235,8 @@
 
 -- | Find all packages depended upon by the given list of packages.
 deepDeps :: Newest -> [DescInfo] -> [DescInfo]
-deepDeps newest dis =
-    go Set.empty dis
+deepDeps newest dis0 =
+    go Set.empty dis0
   where
     go _ [] = []
     go viewed (di:dis)
@@ -243,9 +247,9 @@
         viewed' = Set.insert name viewed
         newDis = mapMaybe getDI $ diDeps di
         getDI :: Dependency -> Maybe DescInfo
-        getDI (Dependency (PackageName name) _) = do
-            pi <- Map.lookup name newest
-            piDesc pi
+        getDI (Dependency (PackageName name') _) = do
+            pi' <- Map.lookup name' newest
+            piDesc pi'
 
 diName :: DescInfo -> String
 diName =
diff --git a/packdeps.cabal b/packdeps.cabal
--- a/packdeps.cabal
+++ b/packdeps.cabal
@@ -1,5 +1,5 @@
 name:            packdeps
-version:         0.2.0
+version:         0.3.0
 license:         BSD3
 license-file:    LICENSE
 author:          Michael Snoyman <michael@snoyman.com>
@@ -21,7 +21,7 @@
 
 library
     build-depends:   base                      >= 4        && < 5
-                   , tar                       >= 0.3.1    && < 0.4
+                   , tar                       >= 0.4      && < 0.5
                    , split                     >= 0.1.2.3  && < 0.2
                    , bytestring                >= 0.9      && < 0.10
                    , text                      >= 0.7      && < 0.12
@@ -62,6 +62,8 @@
                        , hamlet
                        , yesod-newsfeed
                        , yesod >= 0.10
+                       , cautious-file             >= 1.0      && < 1.1
+                       , aeson                     >= 0.6      && < 0.7
     else
         Buildable: False
 
diff --git a/tools/packdeps-yesod.hs b/tools/packdeps-yesod.hs
--- a/tools/packdeps-yesod.hs
+++ b/tools/packdeps-yesod.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE QuasiQuotes, TypeFamilies, OverloadedStrings, MultiParamTypeClasses #-}
 {-# LANGUAGE TemplateHaskell #-}
+import Prelude hiding (writeFile)
 import Yesod
 import Yesod.AtomFeed
 import Yesod.Feed
@@ -11,15 +12,22 @@
 import Distribution.Package
 import Distribution.Text hiding (Text)
 import Control.Arrow
-import Distribution.Version (withinRange)
+import Distribution.Version (VersionRange, withinRange)
 import qualified Data.Map as Map
 import Data.Text (Text, pack, unpack, append)
 import Text.Hamlet (shamlet)
 import System.Environment (getArgs)
 import Data.List (sort)
+import Data.Version (Version)
+import System.IO.Cautious (writeFileL)
+import qualified Data.ByteString.Lazy as L
+import Data.Aeson
+import Data.Aeson.Types (Parser)
+import Control.Monad (mzero)
+import Control.Applicative ((<$>), (<*>))
 
 data PD = PD Newest Reverses
-mkYesod "PD" [$parseRoutes|
+mkYesod "PD" [parseRoutes|
 /favicon.ico FaviconR GET
 / RootR GET
 /feed FeedR GET
@@ -32,12 +40,14 @@
 /reverse ReverseListR GET
 /reverse/#Text ReverseR GET
 |]
-instance Yesod PD where approot _ = ""
+instance Yesod PD where
+    approot = ApprootStatic ""
 
 getFaviconR :: Handler ()
 getFaviconR = sendFile "image/x-icon" "favicon.ico"
 
-mainCassius = [$cassius|
+mainCassius :: CssUrl (Route PD)
+mainCassius = [cassius|
 body
     font-family: Arial,Helvetica,sans-serif
     width: 600px
@@ -63,9 +73,10 @@
     text-align: center
 |]
 
+getRootR :: Handler RepHtml
 getRootR = defaultLayout $ do
     setTitle "Hackage dependency monitor"
-    addCassius mainCassius
+    addCassius mainCassius :: Widget -- just get rid of a warning by using Widget
     [whamlet|
 <h1>Hackage Dependency Monitor
 <form action="@{FeedR}">
@@ -86,8 +97,12 @@
     <a href="http://docs.yesodweb.com/">Powered by Yesod
 |]
 
+isDeep :: Handler Bool
 isDeep = fmap (== Just "on") $ runInputGet $ iopt textField "deep"
 
+getDeps :: Bool
+        -> String
+        -> Handler ([DescInfo], [((String, Version), ([(String, String)], UTCTime))])
 getDeps deep needle = do
     PD newest _ <- getYesod
     let descs' = filterPackages needle newest
@@ -110,7 +125,8 @@
     let deepR = (FeedR, [("needle", needle), ("deep", "on")])
     defaultLayout $ do
         setTitle $ toHtml title
-        addCassius [$cassius|body
+        addCassius [cassius|
+body
     font-family: Arial,Helvetica,sans-serif
     width: 600px
     margin: 2em auto
@@ -165,14 +181,19 @@
                 <a href="http://hackage.haskell.org/package/#{name}">#{name}
 |]
 
+getFeed2R :: Text -> Handler RepAtomRss
 getFeed2R needle = do
     (_, deps) <- getDeps False $ unpack needle
     feed2Helper needle deps
 
+getFeed2DeepR :: Text -> Handler RepAtomRss
 getFeed2DeepR needle = do
     (_, deps) <- getDeps True $ unpack needle
     feed2Helper needle deps
 
+feed2Helper :: Text
+            -> [((String, Version), ([(String, String)], UTCTime))]
+            -> Handler RepAtomRss
 feed2Helper needle deps = do
     now <- liftIO getCurrentTime
     newsFeed Feed
@@ -185,13 +206,13 @@
         , feedDescription = toHtml $ "Newer dependencies for " ++ unpack needle
         }
   where
-    go' ((name, version), (deps, time)) = FeedEntry
+    go' ((name, version), (deps', time)) = FeedEntry
         { feedEntryLink = Feed3R needle (pack name) (pack $ display version) (pack $ show time)
         , feedEntryUpdated = time
         , feedEntryTitle = pack $ "Outdated dependencies for " ++ name ++ " " ++ display version
         , feedEntryContent = [shamlet|
 <table border="1">
-    $forall d <- deps
+    $forall d <- deps'
         <tr>
             <th>#{fst d}
             <td>#{snd d}
@@ -203,16 +224,79 @@
     redirect
   $ "http://hackage.haskell.org/package/" `append` package
 
+main :: IO ()
 main = do
     args <- getArgs
-    if args == ["--save-newest"]
+    if args == ["--save-reverses"]
         then do
             newest <- loadNewest
-            writeFile "newest" $ show newest
+            writeFileL "reverses" $ encode (newest, getReverses newest)
         else do
-            newest <- read `fmap` readFile "newest"
-            warpDebug 5005 $ PD newest $ getReverses newest
+            putStrLn "Loading reverses..."
+            Just (newest, reverses) <- fmap decode' $ L.readFile "reverses"
+            putStrLn "Done"
+            warpDebug 5005 $ PD newest reverses
 
+instance ToJSON Version where
+    toJSON = toJSONShow
+instance FromJSON Version where
+    parseJSON = parseJSONRead
+
+toJSONShow :: Show a => a -> Value
+toJSONShow = String . pack . show
+
+parseJSONRead :: Read a => Value -> Parser a
+parseJSONRead (String t) =
+    case reads $ unpack t of
+        (v, _):_ -> return v
+        _ -> mzero
+parseJSON _ = mzero
+
+instance ToJSON VersionRange where
+    toJSON = toJSONShow
+instance FromJSON VersionRange where
+    parseJSON = parseJSONRead
+
+instance ToJSON PackInfo where
+    toJSON (PackInfo a b c) = Data.Aeson.object
+        [ "version" .= a
+        , "desc"    .= b
+        , "epoch"   .= c
+        ]
+
+instance FromJSON PackInfo where
+    parseJSON (Object v) = PackInfo
+        <$> v .: "version"
+        <*> v .: "desc"
+        <*> v .: "epoch"
+    parseJSON _ = mzero
+
+instance ToJSON DescInfo where
+    toJSON (DescInfo a b c d) = Data.Aeson.object
+        [ "haystack" .= a
+        , "deps" .= b
+        , "package" .= c
+        , "synopsis" .= d
+        ]
+
+instance FromJSON DescInfo where
+    parseJSON (Object v) = DescInfo
+        <$> v .: "haystack"
+        <*> v .: "deps"
+        <*> v .: "package"
+        <*> v .: "synopsis"
+    parseJSON _ = mzero
+
+instance ToJSON PackageIdentifier where
+    toJSON = toJSONShow
+instance FromJSON PackageIdentifier where
+    parseJSON = parseJSONRead
+
+instance ToJSON Dependency where
+    toJSON = toJSONShow
+instance FromJSON Dependency where
+    parseJSON = parseJSONRead
+
 getSpecificR :: Handler RepHtml
 getSpecificR = do
     packages' <- lookupGetParams "package"
@@ -225,7 +309,8 @@
                 (_, v, WontAccept cd _) -> Just (v, cd)
     defaultLayout $ do
         setTitle $ toHtml title
-        addCassius [$cassius|body
+        addCassius [cassius|
+body
     font-family: Arial,Helvetica,sans-serif
     width: 600px
     margin: 2em auto
@@ -264,6 +349,7 @@
         <p>Invalid package name: #{fst p}
 |]
 
+getSpecificFeedR :: Text -> Handler RepAtomRss
 getSpecificFeedR packages' = do
     PD newest _ <- getYesod
     let descs = mapMaybe (flip getPackage newest) $ words $ unpack packages'
@@ -297,11 +383,11 @@
 
 getReverseListR :: Handler RepHtml
 getReverseListR = do
-    PD _ reverse <- getYesod
+    PD _ reverse' <- getYesod
     defaultLayout $ do
         setTitle "Reverse Dependencies"
         addCassius mainCassius
-        addHamlet [$hamlet|
+        addHamlet [hamlet|
 <h1>Reverse Dependencies
 <p>Please choose a package below to view its reverse dependencies: those packages that depend upon it. This listing will also tell you which packages are incompatible with the current version of the package.
 <table
@@ -309,7 +395,7 @@
         <th>Package
         <th>Total Dependencies
         <th>Total Outdated Dependencies
-    $forall p <- Map.toList reverse
+    $forall p <- Map.toList reverse'
         <tr>
             <td
                 <a href=@{ReverseR $ pack $ fst p}>#{fst p}
@@ -327,8 +413,8 @@
 
 getReverseR :: Text -> Handler RepHtml
 getReverseR dep = do
-    PD _ reverse <- getYesod
-    (version, rels) <- maybe notFound return $ Map.lookup (unpack dep) reverse
+    PD _ reverse' <- getYesod
+    (version, rels) <- maybe notFound return $ Map.lookup (unpack dep) reverse'
     defaultLayout $ do
         setTitle [shamlet|Reverse dependencies for #{dep}|]
         addCassius mainCassius
@@ -340,7 +426,7 @@
         <th>Accepted versions
     $forall rel <- rels
         <tr>
-            $if Map.member (fst rel) reverse
+            $if Map.member (fst rel) reverse'
                 <td><a href=@{ReverseR $ pack $ fst rel}>#{fst rel}
             $else
                 <td>#{fst rel}
