diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2012, Felipe Lessa
+
+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 Felipe Lessa nor the names of other
+      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
+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/README b/README
new file mode 100644
--- /dev/null
+++ b/README
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/src/Yesod/Facebook.hs b/src/Yesod/Facebook.hs
new file mode 100644
--- /dev/null
+++ b/src/Yesod/Facebook.hs
@@ -0,0 +1,121 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Yesod.Facebook
+  ( -- * Running @FacebookT@ actions inside @GHandler@
+    YesodFacebook(..)
+  , runFacebookT
+  , runNoAuthFacebookT
+  , getFbCredentials
+
+    -- * Real-time update notifications
+  , parseRealTimeUpdateNotifications
+  , answerRealTimeUpdateChallenge
+  ) where
+
+import Control.Applicative ((<$>))
+import Crypto.Classes (constTimeEq)
+import Data.ByteString.Char8 () -- IsString
+import qualified Data.Aeson as A
+import qualified Data.ByteString.Lazy.Char8 as L
+import qualified Data.Conduit as C
+import qualified Data.Conduit.List as CL
+import qualified Data.Text.Encoding as TE
+import qualified Facebook as FB
+import qualified Network.Wai as W
+import qualified Network.HTTP.Conduit as HTTP
+import qualified Yesod.Core as Y
+
+
+-- | The 'YesodFacebook' class for foundation datatypes that
+-- support running 'FB.FacebookT' actions.
+class Y.Yesod master => YesodFacebook master where
+  -- | The credentials of your app.
+  fbCredentials :: master -> FB.Credentials
+  -- | HTTP manager used for contacting Facebook (may be the same
+  -- as the one used for @yesod-auth@).
+  fbHttpManager :: master -> HTTP.Manager
+
+
+-- | Returns Facebook's 'FB.Credentials' from inside a
+-- 'Y.GHandler'.  Just a convenience wrapper around
+-- 'fbCredentials'.
+getFbCredentials :: YesodFacebook master =>
+                    Y.GHandler sub master FB.Credentials
+getFbCredentials = fbCredentials <$> Y.getYesod
+
+
+-- | Run a 'FacebookT' action inside a 'Y.GHandler' using your
+-- credentials.
+runFacebookT ::
+     YesodFacebook master =>
+     FB.FacebookT FB.Auth (Y.GHandler sub master) a
+  -> Y.GHandler sub master a
+runFacebookT act = do
+  master <- Y.getYesod
+  let creds   = fbCredentials master
+      manager = fbHttpManager master
+  FB.runFacebookT creds manager act
+
+
+-- | Run a 'FacebookT' action inside a 'Y.GHandler' without using
+-- your credentials.  Usually you won't need to use this function
+-- but it's provided for completeness' sake.
+runNoAuthFacebookT ::
+     YesodFacebook master =>
+     FB.FacebookT FB.NoAuth (Y.GHandler sub master) a
+  -> Y.GHandler sub master a
+runNoAuthFacebookT act = do
+  master <- Y.getYesod
+  let manager = fbHttpManager master
+  FB.runNoAuthFacebookT manager act
+
+
+----------------------------------------------------------------------
+
+
+-- | Same as 'getRealTimeUpdateNotifications' but does the
+-- heavy-lifting for you.  Throws an exception whenever any step
+-- fails (signature header not found, invalid signature, invalid
+-- JSON).
+parseRealTimeUpdateNotifications ::
+  (YesodFacebook master, A.FromJSON a) =>
+  Y.GHandler sub master (FB.RealTimeUpdateNotification a)
+parseRealTimeUpdateNotifications = do
+  let myFail = fail . ("parseRealTimeUpdateNotifications: " ++)
+  -- Get request's signature.
+  waiReq <- Y.waiRequest
+  case lookup "X-Hub-Signature" (W.requestHeaders waiReq) of
+    Nothing  -> myFail "X-Hub-Signature not found."
+    Just sig -> do
+      uncheckedData <- L.fromChunks <$> Y.lift (W.requestBody waiReq C.$$ CL.consume)
+      mcheckedData <- runFacebookT $ FB.verifyRealTimeUpdateNotifications sig uncheckedData
+      case mcheckedData of
+        Nothing -> myFail "Signature is invalid."
+        Just checkedData ->
+          case A.decode checkedData of
+            Nothing  -> myFail "Could not decode data."
+            Just ret -> return ret
+
+
+-- | Answer Facebook's challenge if the 'FB.RealTimeUpdateToken'
+-- matches.
+--
+-- Whenever you modify your subscriptions, Facebook will try to
+-- contact your server with the 'FB.RealTimeUpdateToken' that you
+-- gave on your call to 'FB.modifySubscription'.  This function
+-- will correctly answer Facebook's challenge if the
+-- 'FB.RealTimeUpdateToken' matches, otherwise it will return
+-- 'Y.notFound'.
+answerRealTimeUpdateChallenge ::
+     FB.RealTimeUpdateToken
+  -> Y.GHandler sub master Y.RepPlain
+answerRealTimeUpdateChallenge token = do
+  mhubMode        <- Y.lookupGetParam "hub.mode"
+  mhubChallenge   <- Y.lookupGetParam "hub.challenge"
+  mhubVerifyToken <- Y.lookupGetParam "hub.verify_token"
+  case (mhubMode, mhubChallenge, mhubVerifyToken) of
+    -- FIXME: Is hub.mode always subscribe?  Facebook's docs say
+    -- so, but I don't believe them =).
+    (Just "subscribe", Just hubChallenge, Just hubVerifyToken)
+      | TE.encodeUtf8 hubVerifyToken `constTimeEq` token ->
+          return $ Y.RepPlain (Y.toContent hubChallenge)
+    _ -> Y.notFound
diff --git a/yesod-fb.cabal b/yesod-fb.cabal
new file mode 100644
--- /dev/null
+++ b/yesod-fb.cabal
@@ -0,0 +1,40 @@
+Name:                yesod-fb
+Version:             0.1
+Synopsis:            Useful glue functions between the fb library and Yesod.
+Homepage:            https://github.com/meteficha/yesod-fb
+License:             BSD3
+License-file:        LICENSE
+Author:              Felipe Lessa
+Maintainer:          Felipe Lessa <felipe.lessa@gmail.com>
+Category:            Web
+Build-type:          Simple
+Cabal-version:       >= 1.6
+Extra-source-files:  README
+
+Description:
+  This package contains useful glue functions between the fb
+  package (http://hackage.haskell.org/package/fb) and Yesod.
+  These functions can't be included on the fb package itself
+  because it should not depend on Yesod.
+
+Source-repository head
+  type:     git
+  location: git://github.com/meteficha/yesod-fb.git
+
+Library
+  hs-source-dirs: src
+
+  Build-depends:   base         >= 4.3     && < 5
+                 , yesod-core   == 1.1.*
+                 , fb           >= 0.11.1  && < 0.12
+
+                 , crypto-api
+                 , http-conduit
+                 , wai
+                 , conduit
+                 , aeson
+                 , text
+                 , bytestring
+
+  Exposed-modules: Yesod.Facebook
+  GHC-options: -Wall
