diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,56 @@
+Copyright (c) 2012, Janne Hellsten
+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 authors 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.
+
+
+Copyright (c) 2012, Doug Beardsley
+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 authors 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 @@
+import Distribution.Simple
+
+main = defaultMain
diff --git a/resources/auth/devel.cfg b/resources/auth/devel.cfg
new file mode 100644
--- /dev/null
+++ b/resources/auth/devel.cfg
@@ -0,0 +1,21 @@
+# Currently this option is not enforced.  See current auth documentation for
+# more information.
+minPasswordLen = 8
+
+# Name of the cookie to use for remembering the logged in user.
+rememberCookie = "_remember"
+
+# Number of seconds of inactivity before the user is logged out.  If ommitted,
+# the user will remain logged in until the end of the session.
+rememberPeriod = 1209600 # 2 weeks
+
+# Lockout strategy.  The first value is the max number of invalid login
+# attempts before lockout.  The second value is how long the locked lasts.  If
+# ommitted, then incorrect passwords will never result in lockout.
+# lockout = [5, 86400]
+
+# File where the auth encryption key is stored.
+siteKey = "site_key.txt"
+
+# Name of the table where the user data is stored.
+authTable = "snap_auth_user"
diff --git a/resources/db/devel.cfg b/resources/db/devel.cfg
new file mode 100644
--- /dev/null
+++ b/resources/db/devel.cfg
@@ -0,0 +1,13 @@
+db = "test.db"
+
+# Nmuber of distinct connection pools to maintain.  The smallest acceptable
+# value is 1.
+numStripes = 1
+
+# Number of seconds an unused resource is kept open.  The smallest acceptable
+# value is 0.5 seconds.
+idleTime = 5
+
+# Maximum number of resources to keep open per stripe.  The smallest
+# acceptable value is 1.
+maxResourcesPerStripe = 20
diff --git a/snaplet-sqlite-simple.cabal b/snaplet-sqlite-simple.cabal
new file mode 100644
--- /dev/null
+++ b/snaplet-sqlite-simple.cabal
@@ -0,0 +1,54 @@
+name:           snaplet-sqlite-simple
+version:        0.1.0
+synopsis:       sqlite-simple snaplet for the Snap Framework
+description:    This snaplet contains support for using the SQLite
+                database with a Snap Framework application via the
+                sqlite-simple package.  It also includes an
+                authentication backend.
+license:        BSD3
+license-file:   LICENSE
+author:         Janne Hellsten, Doug Beardsley
+maintainer:     Janne Hellsten <jjhellst@gmail.com>
+build-type:     Simple
+cabal-version:  >= 1.6
+homepage:       https://github.com/nurpax/snaplet-sqlite-simple
+category:       Web, Snap
+
+extra-source-files:  LICENSE
+
+data-files:
+  resources/db/devel.cfg
+  resources/auth/devel.cfg
+
+source-repository head
+  type:     git
+  location: https://github.com/nurpax/snaplet-sqlite-simple.git
+
+Library
+  hs-source-dirs: src
+
+  exposed-modules:
+    Snap.Snaplet.SqliteSimple
+    Snap.Snaplet.Auth.Backends.SqliteSimple
+
+  other-modules:
+    Paths_snaplet_sqlite_simple
+
+  build-depends:
+    base                       >= 4       && < 5,
+    bytestring                 >= 0.9.1   && < 0.10,
+    clientsession              >= 0.7.2   && < 0.8,
+    configurator               >= 0.2     && < 0.3,
+    MonadCatchIO-transformers  >= 0.3     && < 0.4,
+    mtl                        >= 2       && < 3,
+    sqlite-simple              >= 0.1     && < 1.0,
+    direct-sqlite              >= 2.0     && < 2.1,
+    resource-pool-catchio      >= 0.2     && < 0.3,
+    snap                       >= 0.9     && < 0.10,
+    text                       >= 0.11    && < 0.12,
+    transformers               >= 0.2     && < 0.4,
+    unordered-containers       >= 0.2     && < 0.3
+
+
+  ghc-options: -Wall -fwarn-tabs -funbox-strict-fields
+               -fno-warn-orphans -fno-warn-unused-do-bind
diff --git a/src/Snap/Snaplet/Auth/Backends/SqliteSimple.hs b/src/Snap/Snaplet/Auth/Backends/SqliteSimple.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/Auth/Backends/SqliteSimple.hs
@@ -0,0 +1,338 @@
+{-# LANGUAGE BangPatterns      #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards   #-}
+
+{-|
+
+This module allows you to use the auth snaplet with your user database
+stored in a SQLite database.  When you run your application with this
+snaplet, a config file will be copied into the the
+@snaplets/sqlite-auth@ directory.  This file contains all of the
+configurable options for the snaplet and allows you to change them
+without recompiling your application.
+
+To use this snaplet in your application enable the session, sqlite,
+and auth snaplets as follows:
+
+> data App = App
+>     { ... -- your own application state here
+>     , _sess :: Snaplet SessionManager
+>     , _db   :: Snaplet Sqlite
+>     , _auth :: Snaplet (AuthManager App)
+>     }
+
+Then in your initializer you'll have something like this:
+
+> d <- nestSnaplet "db" db sqliteInit
+> a <- nestSnaplet "auth" auth $ initSqliteAuth sess d
+
+If you have not already created the database table for users, it will
+automatically be created for you the first time you run your
+application.
+
+-}
+
+module Snap.Snaplet.Auth.Backends.SqliteSimple
+  ( initSqliteAuth
+  ) where
+
+------------------------------------------------------------------------------
+import qualified Data.Configurator as C
+import qualified Data.HashMap.Lazy as HM
+import qualified Data.Text as T
+import           Data.Text (Text)
+import           Data.Maybe
+import           Data.Pool
+import           Database.SQLite3 (SQLData(..))
+import qualified Database.SQLite.Simple as S
+import qualified Database.SQLite.Simple.ToField as S
+import           Database.SQLite.Simple.FromField
+import           Database.SQLite.Simple.FromRow
+import           Database.SQLite.Simple.Types
+import           Snap
+import           Snap.Snaplet.Auth
+import           Snap.Snaplet.SqliteSimple
+import           Snap.Snaplet.Session
+import           Web.ClientSession
+import           Paths_snaplet_sqlite_simple
+
+
+data SqliteAuthManager = SqliteAuthManager
+    { pamTable    :: AuthTable
+    , pamConnPool :: Pool S.Connection
+    }
+
+
+------------------------------------------------------------------------------
+-- | Initializer for the sqlite backend to the auth snaplet.
+--
+initSqliteAuth
+  :: Lens b (Snaplet SessionManager)  -- ^ Lens to the session snaplet
+  -> Snaplet Sqlite  -- ^ The sqlite snaplet
+  -> SnapletInit b (AuthManager b)
+initSqliteAuth sess db = makeSnaplet "sqlite-auth" desc datadir $ do
+    config <- getSnapletUserConfig
+    authTable <- liftIO $ C.lookupDefault "snap_auth_user" config "authTable"
+    authSettings <- authSettingsFromConfig
+    key <- liftIO $ getKey (asSiteKey authSettings)
+    let tableDesc = defAuthTable { tblName = authTable }
+    let manager = SqliteAuthManager tableDesc $
+                                      sqlitePool $ getL snapletValue db
+    liftIO $ createTableIfMissing manager
+    rng <- liftIO mkRNG
+    return $ AuthManager
+      { backend = manager
+      , session = sess
+      , activeUser = Nothing
+      , minPasswdLen = asMinPasswdLen authSettings
+      , rememberCookieName = asRememberCookieName authSettings
+      , rememberPeriod = asRememberPeriod authSettings
+      , siteKey = key
+      , lockout = asLockout authSettings
+      , randomNumberGenerator = rng
+      }
+  where
+    desc = "An Sqlite backend for user authentication"
+    datadir = Just $ liftM (++"/resources/auth") getDataDir
+
+
+------------------------------------------------------------------------------
+-- | Create the user table if it doesn't exist.
+createTableIfMissing :: SqliteAuthManager -> IO ()
+createTableIfMissing SqliteAuthManager{..} = do
+    withResource pamConnPool $ \conn -> do
+        res <- S.query conn
+               (Query "SELECT name FROM sqlite_master WHERE type='table' AND name=?")
+               (Only (tblName pamTable))
+        when (null (res :: [Only T.Text])) $
+          S.execute_ conn (Query q) >> return ()
+    return ()
+  where
+    q = T.concat
+          [ "CREATE TABLE "
+          , tblName pamTable
+          , " ("
+          , T.intercalate "," (map (fDesc . ($pamTable) . (fst)) colDef)
+          , ")"
+          ]
+
+buildUid :: Int -> UserId
+buildUid = UserId . T.pack . show
+
+
+instance FromField UserId where
+    fromField f = buildUid <$> fromField f
+
+instance FromField Password where
+    fromField f = Encrypted <$> fromField f
+
+instance FromRow AuthUser where
+    fromRow =
+        AuthUser
+        <$> _userId
+        <*> _userLogin
+        <*> _userPassword
+        <*> _userActivatedAt
+        <*> _userSuspendedAt
+        <*> _userRememberToken
+        <*> _userLoginCount
+        <*> _userFailedLoginCount
+        <*> _userLockedOutUntil
+        <*> _userCurrentLoginAt
+        <*> _userLastLoginAt
+        <*> _userCurrentLoginIp
+        <*> _userLastLoginIp
+        <*> _userCreatedAt
+        <*> _userUpdatedAt
+        <*> _userRoles
+        <*> _userMeta
+      where
+        !_userId               = field
+        !_userLogin            = field
+        !_userPassword         = field
+        !_userActivatedAt      = field
+        !_userSuspendedAt      = field
+        !_userRememberToken    = field
+        !_userLoginCount       = field
+        !_userFailedLoginCount = field
+        !_userLockedOutUntil   = field
+        !_userCurrentLoginAt   = field
+        !_userLastLoginAt      = field
+        !_userCurrentLoginIp   = field
+        !_userLastLoginIp      = field
+        !_userCreatedAt        = field
+        !_userUpdatedAt        = field
+        !_userRoles            = pure []
+        !_userMeta             = pure HM.empty
+
+
+querySingle :: (ToRow q, FromRow a)
+            => Pool S.Connection -> Query -> q -> IO (Maybe a)
+querySingle pool q ps = withResource pool $ \conn -> return . listToMaybe =<<
+    S.query conn q ps
+
+authExecute :: ToRow q
+            => Pool S.Connection -> Query -> q -> IO ()
+authExecute pool q ps = do
+    withResource pool $ \conn -> S.execute conn q ps
+    return ()
+
+instance S.ToField Password where
+    toField (ClearText bs) = S.toField bs
+    toField (Encrypted bs) = S.toField bs
+
+
+-- | Datatype containing the names of the columns for the authentication table.
+data AuthTable
+  =  AuthTable
+  {  tblName             :: Text
+  ,  colId               :: (Text, Text)
+  ,  colLogin            :: (Text, Text)
+  ,  colPassword         :: (Text, Text)
+  ,  colActivatedAt      :: (Text, Text)
+  ,  colSuspendedAt      :: (Text, Text)
+  ,  colRememberToken    :: (Text, Text)
+  ,  colLoginCount       :: (Text, Text)
+  ,  colFailedLoginCount :: (Text, Text)
+  ,  colLockedOutUntil   :: (Text, Text)
+  ,  colCurrentLoginAt   :: (Text, Text)
+  ,  colLastLoginAt      :: (Text, Text)
+  ,  colCurrentLoginIp   :: (Text, Text)
+  ,  colLastLoginIp      :: (Text, Text)
+  ,  colCreatedAt        :: (Text, Text)
+  ,  colUpdatedAt        :: (Text, Text)
+  ,  rolesTable          :: Text
+  }
+
+-- | Default authentication table layout
+defAuthTable :: AuthTable
+defAuthTable
+  =  AuthTable
+  {  tblName             = "snap_auth_user"
+  ,  colId               = ("uid", "INTEGER PRIMARY KEY")
+  ,  colLogin            = ("login", "text UNIQUE NOT NULL")
+  ,  colPassword         = ("password", "text")
+  ,  colActivatedAt      = ("activated_at", "timestamp")
+  ,  colSuspendedAt      = ("suspended_at", "timestamp")
+  ,  colRememberToken    = ("remember_token", "text")
+  ,  colLoginCount       = ("login_count", "INTEGER NOT NULL")
+  ,  colFailedLoginCount = ("failed_login_count", "INTEGER NOT NULL")
+  ,  colLockedOutUntil   = ("locked_out_until", "timestamp")
+  ,  colCurrentLoginAt   = ("current_login_at", "timestamp")
+  ,  colLastLoginAt      = ("last_login_at", "timestamp")
+  ,  colCurrentLoginIp   = ("current_login_ip", "text")
+  ,  colLastLoginIp      = ("last_login_ip", "text")
+  ,  colCreatedAt        = ("created_at", "timestamp")
+  ,  colUpdatedAt        = ("updated_at", "timestamp")
+  ,  rolesTable          = "user_roles"
+  }
+
+fDesc :: (Text, Text) -> Text
+fDesc f = fst f `T.append` " " `T.append` snd f
+
+-- | List of deconstructors so it's easier to extract column names from an
+-- 'AuthTable'.
+colDef :: [(AuthTable -> (Text, Text), AuthUser -> SQLData)]
+colDef =
+  [ (colId              , S.toField . fmap unUid . userId)
+  , (colLogin           , S.toField . userLogin)
+  , (colPassword        , S.toField . userPassword)
+  , (colActivatedAt     , S.toField . userActivatedAt)
+  , (colSuspendedAt     , S.toField . userSuspendedAt)
+  , (colRememberToken   , S.toField . userRememberToken)
+  , (colLoginCount      , S.toField . userLoginCount)
+  , (colFailedLoginCount, S.toField . userFailedLoginCount)
+  , (colLockedOutUntil  , S.toField . userLockedOutUntil)
+  , (colCurrentLoginAt  , S.toField . userCurrentLoginAt)
+  , (colLastLoginAt     , S.toField . userLastLoginAt)
+  , (colCurrentLoginIp  , S.toField . userCurrentLoginIp)
+  , (colLastLoginIp     , S.toField . userLastLoginIp)
+  , (colCreatedAt       , S.toField . userCreatedAt)
+  , (colUpdatedAt       , S.toField . userUpdatedAt)
+  ]
+
+saveQuery :: AuthTable -> AuthUser -> (Text, [SQLData])
+saveQuery at u@AuthUser{..} = maybe insertQuery updateQuery userId
+  where
+    insertQuery =  (T.concat [ "INSERT INTO "
+                             , tblName at
+                             , " ("
+                             , T.intercalate "," cols
+                             , ") VALUES ("
+                             , T.intercalate "," vals
+                             , ")"
+                             ]
+                   , params)
+    qval f  = fst (f at) `T.append` " = ?"
+    updateQuery uid =
+        (T.concat [ "UPDATE "
+                  , tblName at
+                  , " SET "
+                  , T.intercalate "," (map (qval . fst) $ tail colDef)
+                  , " WHERE "
+                  , fst (colId at)
+                  , " = ?"
+                  ]
+        , params ++ [S.toField $ unUid uid])
+    cols = map (fst . ($at) . fst) $ tail colDef
+    vals = map (const "?") cols
+    params = map (($u) . snd) $ tail colDef
+
+
+------------------------------------------------------------------------------
+-- |
+instance IAuthBackend SqliteAuthManager where
+    save SqliteAuthManager{..} u@AuthUser{..} = do
+        let (qstr, params) = saveQuery pamTable u
+        let q = Query qstr
+        withResource pamConnPool $ \conn -> do
+            S.execute conn q params
+            let q2 = Query $ T.concat
+                     [ "select * from "
+                     , tblName pamTable
+                     , " where "
+                     , fst (colLogin pamTable)
+                     , " = ?"
+                     ]
+            res <- S.query conn q2 [userLogin]
+            return $ fromMaybe u $ listToMaybe res
+
+    lookupByUserId SqliteAuthManager{..} uid = do
+        let q = Query $ T.concat
+                [ "select * from "
+                , tblName pamTable
+                , " where "
+                , fst (colId pamTable)
+                , " = ?"
+                ]
+        querySingle pamConnPool q [unUid uid]
+
+    lookupByLogin SqliteAuthManager{..} login = do
+        let q = Query $ T.concat
+                [ "select * from "
+                , tblName pamTable
+                , " where "
+                , fst (colLogin pamTable)
+                , " = ?"
+                ]
+        querySingle pamConnPool q [login]
+
+    lookupByRememberToken SqliteAuthManager{..} token = do
+        let q = Query $ T.concat
+                [ "select * from "
+                , tblName pamTable
+                , " where "
+                , fst (colRememberToken pamTable)
+                , " = ?"
+                ]
+        querySingle pamConnPool q [token]
+
+    destroy SqliteAuthManager{..} AuthUser{..} = do
+        let q = Query $ T.concat
+                [ "delete from "
+                , tblName pamTable
+                , " where "
+                , fst (colLogin pamTable)
+                , " = ?"
+                ]
+        authExecute pamConnPool q [userLogin]
diff --git a/src/Snap/Snaplet/SqliteSimple.hs b/src/Snap/Snaplet/SqliteSimple.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/SqliteSimple.hs
@@ -0,0 +1,213 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE FlexibleContexts  #-}
+{-# LANGUAGE FlexibleInstances #-}
+
+{-|
+
+This snaplet makes it simple to use a SQLite database from your Snap
+application and is based on the sqlite-simple library
+(<http://hackage.haskell.org/package/sqlite-simple>).  Now, adding a
+database to your web app takes just two simple steps.
+
+First, include this snaplet in your application's state.
+
+> data App = App
+>     { ... -- Other state needed in your app
+>     , _db :: Snaplet Sqlite
+>     }
+
+Next, call the sqliteInit from your application's initializer.
+
+> appInit = makeSnaplet ... $ do
+>     ...
+>     d <- nestSnaplet "db" db sqliteInit
+>     return $ App ... d
+
+Now you can use any of the sqlite-simple wrapper functions defined in
+this module anywhere in your application handlers.  For instance:
+
+> postHandler :: Handler App App ()
+> postHandler = do
+>     posts <- with db $ query_ "select * from blog_post"
+>     ...
+
+Optionally, if you find yourself doing many database queries, you can
+eliminate some of the boilerplate by defining a HasSqlite instance for
+your application.
+
+> instance HasSqlite (Handler b App) where
+>   getSqliteState = with db get
+
+With this code, our postHandler example no longer requires the 'with'
+function:
+
+> postHandler :: Handler App App ()
+> postHandler = do
+>     posts <- query_ "select * from blog_post"
+>     ...
+
+The first time you run an application with the sqlite-simple snaplet,
+a configuration file @devel.cfg@ is created in the
+@snaplets/sqlite-simple@ directory underneath your project root.  It
+specifies how to connect to your Sqlite database.  Edit this file and
+modify the values appropriately and you'll be off and running.
+
+If you want to have out-of-the-box authentication, look at the
+documentation for the "Snap.Snaplet.Auth.Backends.Sqlite" module.
+
+-}
+
+module Snap.Snaplet.SqliteSimple (
+  -- * The Snaplet
+    Sqlite(..)
+  , HasSqlite(..)
+  , sqliteInit
+
+  -- * Wrappers and re-exports
+  , query
+  , query_
+  , execute
+  , execute_
+
+  -- Re-exported from sqlite-simple
+  , S.Connection
+  , S.Query
+  , S.Only(..)
+  , S.FormatError(..)
+  , S.ResultError(..)
+  , (S.:.)(..)
+  , ToRow(..)
+  , FromRow(..)
+
+  , field
+
+  ) where
+
+import           Prelude hiding (catch)
+
+import           Control.Monad.CatchIO hiding (Handler)
+import           Control.Monad.IO.Class
+import           Control.Monad.State
+import           Control.Monad.Trans.Reader
+import           Control.Monad.Trans.Writer
+import qualified Data.Configurator as C
+import           Data.List
+import           Data.Maybe
+import           Data.Pool
+import           Database.SQLite.Simple.ToRow
+import           Database.SQLite.Simple.FromRow
+import qualified Database.SQLite.Simple as S
+import           Snap
+import           Paths_snaplet_sqlite_simple
+
+
+
+------------------------------------------------------------------------------
+-- | The state for the sqlite-simple snaplet. To use it in your app
+-- include this in your application state and use 'sqliteInit' to initialize it.
+data Sqlite = Sqlite
+    { sqlitePool :: Pool S.Connection
+    -- ^ Function for retrieving the connection pool
+    }
+
+
+------------------------------------------------------------------------------
+-- | Instantiate this typeclass on 'Handler b YourAppState' so this snaplet
+-- can find the connection source.  If you need to have multiple instances of
+-- the sqlite snaplet in your application, then don't provide this instance
+-- and leverage the default instance by using \"@with dbLens@\" in front of calls
+-- to snaplet-sqlite-simple functions.
+class (MonadCatchIO m) => HasSqlite m where
+    getSqliteState :: m Sqlite
+
+
+------------------------------------------------------------------------------
+-- | Default instance
+instance HasSqlite (Handler b Sqlite) where
+    getSqliteState = get
+
+
+------------------------------------------------------------------------------
+-- | A convenience instance to make it easier to use this snaplet in the
+-- Initializer monad like this:
+--
+-- > d <- nestSnaplet "db" db pgsInit
+-- > count <- liftIO $ runReaderT (execute "INSERT ..." params) d
+instance (MonadCatchIO m) => HasSqlite (ReaderT (Snaplet Sqlite) m) where
+    getSqliteState = asks (getL snapletValue)
+
+
+------------------------------------------------------------------------------
+-- | A convenience instance to make it easier to use functions written for
+-- this snaplet in non-snaplet contexts.
+instance (MonadCatchIO m) => HasSqlite (ReaderT Sqlite m) where
+    getSqliteState = ask
+
+
+------------------------------------------------------------------------------
+-- | Convenience function allowing easy collection of config file errors.
+logErr :: MonadIO m
+       => t -> IO (Maybe a) -> WriterT [t] m (Maybe a)
+logErr err m = do
+    res <- liftIO m
+    when (isNothing res) (tell [err])
+    return res
+
+
+------------------------------------------------------------------------------
+-- | Initialize the snaplet
+sqliteInit :: SnapletInit b Sqlite
+sqliteInit = makeSnaplet "sqlite-simple" description datadir $ do
+    config <- getSnapletUserConfig
+    (mci,errs) <- runWriterT $ do
+        db <- logErr "Must specify db filename" $ C.lookup config "db"
+        return $ db
+    let ci = fromMaybe (error $ intercalate "\n" errs) mci
+
+    stripes <- liftIO $ C.lookupDefault 1 config "numStripes"
+    idle <- liftIO $ C.lookupDefault 5 config "idleTime"
+    resources <- liftIO $ C.lookupDefault 20 config "maxResourcesPerStripe"
+    pool <- liftIO $ createPool (S.open ci) S.close stripes
+                                (realToFrac (idle :: Double)) resources
+    return $ Sqlite pool
+  where
+    description = "Sqlite abstraction"
+    datadir = Just $ liftM (++"/resources/db") getDataDir
+
+
+------------------------------------------------------------------------------
+-- | Convenience function for executing a function that needs a database
+-- connection.
+withSqlite :: (HasSqlite m)
+       => (S.Connection -> IO b) -> m b
+withSqlite f = do
+    s <- getSqliteState
+    let pool = sqlitePool s
+    liftIO $ withResource pool f
+
+
+------------------------------------------------------------------------------
+-- | See 'P.query'
+query :: (HasSqlite m, ToRow q, FromRow r)
+      => S.Query -> q -> m [r]
+query q params = withSqlite (\c -> S.query c q params)
+
+
+------------------------------------------------------------------------------
+-- | See 'P.query_'
+query_ :: (HasSqlite m, FromRow r) => S.Query -> m [r]
+query_ q = withSqlite (\c -> S.query_ c q)
+
+
+------------------------------------------------------------------------------
+-- |
+execute :: (HasSqlite m, ToRow q, MonadCatchIO m)
+        => S.Query -> q -> m ()
+execute template qs = withSqlite (\c -> S.execute c template qs)
+
+
+------------------------------------------------------------------------------
+-- |
+execute_ :: (HasSqlite m, MonadCatchIO m)
+         => S.Query -> m ()
+execute_ template = withSqlite (\c -> S.execute_ c template)
