diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for wai-reflex
+
+## 0.1.0.0  -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Dave Laing
+
+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 Dave Laing 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/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/exe/Main.hs b/exe/Main.hs
new file mode 100644
--- /dev/null
+++ b/exe/Main.hs
@@ -0,0 +1,20 @@
+{-|
+Copyright   : (c) Dave Laing, 2017-2019
+License     : BSD3
+Maintainer  : dave.laing.80@gmail.com
+Stability   : experimental
+Portability : non-portable
+-}
+{-# LANGUAGE OverloadedStrings #-}
+module Main (main) where
+
+import Network.Wai (responseLBS)
+import Network.HTTP.Types.Status (status200)
+
+import Reflex.Backend.Warp (runAppForever)
+
+main :: IO ()
+main =
+  runAppForever 8080 $ \eReq -> do
+    let eRes = responseLBS status200 [] "Hi" <$ eReq
+    pure eRes
diff --git a/reflex-backend-wai.cabal b/reflex-backend-wai.cabal
new file mode 100644
--- /dev/null
+++ b/reflex-backend-wai.cabal
@@ -0,0 +1,62 @@
+name:                reflex-backend-wai
+version:             0.1.0.0
+synopsis:            Reflex interface to `wai`
+description:
+  Reflex interface to `wai`.
+  .
+  A minimal example:
+  .
+  > {-# LANGUAGE OverloadedStrings #-}
+  > module Main where
+  >  
+  > import Network.Wai (responseLBS)
+  > import Network.HTTP.Types.Status (status200)
+  >  
+  > import Reflex.Backend.Warp (runAppForever)
+  >  
+  > main :: IO ()
+  > main =
+  >   runAppForever 8080 $ \eReq -> do
+  >     let eRes = responseLBS status200 [] "Hi" <$ eReq
+  >     pure eRes
+
+license:             BSD3
+license-file:        LICENSE
+author:              Dave Laing
+maintainer:          dave.laing.80@gmail.com
+-- copyright:           
+category:            FRP, Web
+build-type:          Simple
+extra-source-files:  ChangeLog.md
+cabal-version:       >=1.10
+tested-with:         GHC==8.0.2
+                   , GHC==8.2.2
+                   , GHC==8.4.3
+                   , GHC==8.6.4
+
+library
+  exposed-modules:     Reflex.Backend.Wai
+                     , Reflex.Backend.Warp
+  build-depends:       base       >=4.9    && < 4.13
+                     , containers >= 0.5   && < 0.7
+                     , mtl        >= 2.2.1 && < 2.3
+                     , reflex     >= 0.5   && < 0.6
+                     , reflex-basic-host >= 0.1 && < 0.2
+                     , stm        >= 2.4.4 && < 2.6
+                     , wai        >= 3.2.1 && < 3.3
+                     , warp >= 3.2.13 && < 3.3
+  hs-source-dirs:      src
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+executable example
+  main-is:             Main.hs
+  build-depends:       base              >=4.9     && < 4.13
+                     , http-types        >= 0.9.1  && < 0.13
+                     , reflex            >= 0.5    && < 0.6
+                     , wai               >= 3.2.1  && < 3.3
+                     , reflex-backend-wai
+  hs-source-dirs:      exe
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+  
diff --git a/src/Reflex/Backend/Wai.hs b/src/Reflex/Backend/Wai.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Backend/Wai.hs
@@ -0,0 +1,132 @@
+{-|
+Copyright   : (c) Dave Laing, 2017-2019
+License     : BSD3
+Maintainer  : dave.laing.80@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Low level operations for integrating reflex networks with WAI
+'Application's.
+
+If you just want to serve a reflex network then have a look
+at "Reflex.Backend.Warp".
+
+-}
+
+{-# LANGUAGE FlexibleContexts #-}
+module Reflex.Backend.Wai
+  ( WaiSource(..), newWaiSource
+  , waiApplicationGuest
+  , waiApplicationHost
+  , liftWaiApplication, liftWaiApplicationTagged
+  )
+where
+
+import Network.Wai
+import Network.Wai.Internal (ResponseReceived(..))
+
+import Control.Monad (void, forever)
+
+import Control.Monad.Trans (MonadIO, liftIO)
+
+import Data.Map (Map)
+import qualified Data.Map as Map
+
+import Control.Concurrent (forkIO)
+import Control.Monad.STM
+import Control.Concurrent.STM
+
+import Reflex hiding (Request, Response)
+
+-- | The source of the WAI application data.
+--
+-- Requests generated by the web server are stored here, and read
+-- by the reflex network.
+--
+-- Responses generated by the reflex network are stored here, and
+-- read (and subsequently returned) by the web server.
+data WaiSource
+  = WaiSource
+  { wsRequest :: TMVar Request
+  , wsResponse :: TMVar Response
+  }
+
+-- | Initialise a 'WaiSource'
+newWaiSource :: MonadIO m => m WaiSource
+newWaiSource =
+  liftIO $ WaiSource <$> newEmptyTMVarIO <*> newEmptyTMVarIO
+
+-- | Build an 'Application' that deposits a 'Request' into the
+-- 'WaiSource', then reads a 'Response' from the 'WaiSource', then
+-- responds with it.
+waiApplicationHost :: WaiSource -> Application
+waiApplicationHost (WaiSource wReq wRes) req response = do
+  atomically $ putTMVar wReq req
+  res <- atomically $ takeTMVar wRes
+  response res
+
+-- | Build a reflex network that pumps 'Request's and 'Response's to
+-- and from the 'WaiSource'
+waiApplicationGuest ::
+  ( Reflex t
+  , MonadIO m
+  , PerformEvent t m
+  , MonadIO (Performable m)
+  , TriggerEvent t m
+  ) =>
+  WaiSource ->
+  (Event t Request -> m (Event t Response)) ->
+  m ()
+waiApplicationGuest (WaiSource wReq wRes) network = do
+  (eReq, onReq) <- newTriggerEvent
+  eRes <- network eReq
+
+  performEvent_ $ liftIO . atomically . putTMVar wRes <$> eRes
+
+  void . liftIO . forkIO . forever $ do
+    req <- atomically $ takeTMVar wReq
+    onReq req
+
+  pure ()
+
+-- | Given a WAI 'Application' and a 'Request' event, create an
+-- 'Event' that yield a 'Response' by running the 'Application'.
+--
+-- The output 'Event' will fire some time after the input 'Event'.
+liftWaiApplication ::
+  ( Reflex t
+  , PerformEvent t m
+  , MonadIO (Performable m)
+  , TriggerEvent t m
+  ) =>
+  Application ->
+  Event t Request ->
+  m (Event t Response)
+liftWaiApplication app eReq = do
+  (eRes, onRes) <- newTriggerEvent
+
+  let go res = ResponseReceived <$ onRes res
+
+  performEvent_ $ (\req -> void . liftIO $ app req go) <$> eReq
+
+  pure eRes
+
+-- | Similar to 'liftWaiApplication', but the 'Request' event should yield
+-- a tag which is then attached to the 'Response'.
+liftWaiApplicationTagged ::
+  ( Reflex t
+  , PerformEvent t m
+  , MonadIO (Performable m)
+  , TriggerEvent t m
+  ) =>
+  Application ->
+  Event t (tag, Request) ->
+  m (Event t (Map tag Response))
+liftWaiApplicationTagged app eReq = do
+  (eRes, onRes) <- newTriggerEvent
+
+  let go t res = ResponseReceived <$ onRes (Map.singleton t res)
+
+  performEvent_ $ (\(t, req) -> void . liftIO . app req $ go t) <$> eReq
+
+  pure eRes
diff --git a/src/Reflex/Backend/Warp.hs b/src/Reflex/Backend/Warp.hs
new file mode 100644
--- /dev/null
+++ b/src/Reflex/Backend/Warp.hs
@@ -0,0 +1,36 @@
+{-|
+Copyright   : (c) Dave Laing, 2017-2019
+License     : BSD3
+Maintainer  : dave.laing.80@gmail.com
+Stability   : experimental
+Portability : non-portable
+
+Serving reflex networks using @warp@.
+
+-}
+
+{-# language FlexibleContexts, TypeFamilies #-}
+{-# language RankNTypes #-}
+module Reflex.Backend.Warp where
+
+import Reflex hiding (Request, Response)
+import Reflex.Host.Basic (BasicGuestConstraints, BasicGuest, basicHostForever)
+import Reflex.Backend.Wai
+
+import Control.Concurrent (forkIO)
+import Control.Monad (void)
+import Network.Wai (Request, Response)
+import Network.Wai.Handler.Warp (Port, run)
+
+-- | Serve a reflex network using warp.
+runAppForever
+  :: Port -- ^ Server port
+  -> (forall t m.
+      BasicGuestConstraints t m =>
+      Event t Request ->
+      BasicGuest t m (Event t Response)) -- ^ Reflex network
+  -> IO ()
+runAppForever port network = do
+  waiSource <- newWaiSource
+  void . forkIO $ basicHostForever (waiApplicationGuest waiSource network)
+  void . run port $ waiApplicationHost waiSource
