diff --git a/CHANGES.md b/CHANGES.md
new file mode 100644
--- /dev/null
+++ b/CHANGES.md
@@ -0,0 +1,7 @@
+## 0.0.1 (2015-03-20)
+
+ * Push agents "subscribe" via IO Action callbacks.
+
+## 0.0.2 (2015-04-05)
+
+ * Push agents subscribe via a broadcast channel.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2015, Andrew Darqui
+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.
+
+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/LICENSE.Tibell b/LICENSE.Tibell
new file mode 100644
--- /dev/null
+++ b/LICENSE.Tibell
@@ -0,0 +1,30 @@
+Copyright (c) 2014, Johan Tibell
+
+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 Johan Tibell 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/Makefile b/Makefile
new file mode 100644
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+all:
+	cabal sandbox init
+	cabal install
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# Modifications of ekg-statsd by Johan Tibell to provide a generic "push" framework.
+
+You will notice this is almost identical to ekg-statsd. It's just me trying to abstract away the basic functionality found in ekg-statsd, so not to have to duplicate this code anywhere else. Eventually I may try to clean up this "abstraction" and create a PR in ekg-core.
+
+# TODO: Soon
+
+So, the idea of ekg-push is to create a simple framework so that I can create agents such as:
+- ekg-push-redis
+- ekg-push-file
+- ekg-push-statsd
+- ekg-push-uberlog
+
+etc..
+
+Push agents simply 'subscribe' to the push handle returned by forkPush. Once subscribed, agents call consume and handle the Metric.Sample data how they want.
+
+# Installation
+
+You can just type 'make' to install ekg-push into a local sandbox. Or you can use cabal:
+
+```
+cabal install ekg-push
+```
+
+# Getting started
+
+See examples/basic.hs
+
+```
+main :: IO ()
+main = do
+    store <- newStore
+    registerGcMetrics store
+    iters <- createCounter "iterations" store
+    push <- forkPush defaultPushOptions { prefix = "pfx", suffix = "sfx" } store
+
+    ch1 <- subscribe push
+    _ <- forkIO $ forever $ do
+            msg <- consume ch1
+            putStrLn $ "subscription #1: " ++ show  msg
+
+    let loop n = do
+            evaluate $ mean [1..n]
+            Counter.inc iters
+            threadDelay 2000
+            loop n
+
+    loop 1000000
+```
+
+-- adarqui
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/System/Remote/Monitoring/Push.hs b/System/Remote/Monitoring/Push.hs
new file mode 100644
--- /dev/null
+++ b/System/Remote/Monitoring/Push.hs
@@ -0,0 +1,185 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+-- | This library lets you push metric samples to a broadcast channel.
+-- Consumers can then persist the metrics samples as they wish.
+-- ekg-push is based heavily off of the ekg-statsd package which
+-- can be found at: https://github.com/tibbe/ekg-statsd
+--
+-- Example usage:
+--
+-- > main = do
+-- >     store <- newStore
+-- >     push <- forkPush defaultPushOptions store
+-- >     ch <- subscribe push
+-- >     sample <- consume ch
+-- >     putStrLn $ show sample
+--
+-- You probably want to include some of the predefined metrics defined
+-- in the ekg-core package, by calling e.g. the 'registerGcStats'
+-- function defined in that package.
+module System.Remote.Monitoring.Push
+    (
+      Push
+    , PushChan
+    , PushOptions(..)
+    , pushThreadId
+    , forkPush
+    , defaultPushOptions
+    , subscribe
+    , consume
+    ) where
+
+import Control.Concurrent (ThreadId, myThreadId, threadDelay, throwTo)
+import Control.Concurrent.Chan (Chan, newChan, writeChan, readChan, dupChan)
+import qualified Data.HashMap.Strict as M
+import Data.Int (Int64)
+import qualified Data.Text as T
+import Data.Time.Clock.POSIX (getPOSIXTime)
+import qualified System.Metrics as Metrics
+
+#if __GLASGOW_HASKELL__ >= 706
+import Control.Concurrent (forkFinally)
+#else
+import Control.Concurrent (forkIO)
+import Control.Exception (SomeException, mask, try)
+import Prelude hiding (catch)
+#endif
+
+-- | A handle that can be used to control the push sync thread.
+-- Created by 'forkPush'.
+data Push = Push
+    { threadId :: {-# UNPACK #-} !ThreadId
+    , mainCh :: Chan (Metrics.Sample)
+    }
+
+-- | A new PushChan is created on every call to subscribe.
+-- This is essentially a dupChan of our main channel (mainCh).
+data PushChan = PushChan
+    { ch :: PushChanType }
+    
+type PushChanType = Chan Metrics.Sample
+
+-- | The thread ID of the push sync thread. You can stop the sync by
+-- killing this thread (i.e. by throwing it an asynchronous
+-- exception.)
+pushThreadId :: Push -> ThreadId
+pushThreadId = threadId
+
+-- | Options to control how to connect to the push server and how
+-- often to flush metrics. The flush interval should be shorter than
+-- the flush interval push itself uses to flush data to its
+-- backends.
+data PushOptions = PushOptions
+    {
+      -- | Data push interval, in ms.
+      flushInterval :: !Int
+
+      -- | Print debug output to stderr.
+    , debug :: !Bool
+
+      -- | Prefix to add to all metric names.
+    , prefix :: !T.Text
+
+      -- | Suffix to add to all metric names. This is particularly
+      -- useful for sending per host stats by settings this value to:
+      -- @takeWhile (/= \'.\') \<$\> getHostName@, using @getHostName@
+      -- from the @Network.BSD@ module in the network package.
+    , suffix :: !T.Text
+    }
+
+-- | Defaults:
+--
+-- * @flushInterval@ = @1000@
+--
+-- * @debug@ = @False@
+defaultPushOptions :: PushOptions
+defaultPushOptions = PushOptions
+    { flushInterval = 1000
+    , debug         = False
+    , prefix        = ""
+    , suffix        = ""
+    }
+
+-- | Create a thread that periodically flushes the metrics in the
+-- store to push.
+forkPush :: PushOptions  -- ^ Options
+           -> Metrics.Store  -- ^ Metric store
+           -> IO Push      -- ^ Push sync handle
+forkPush opts store = do
+    me <- myThreadId
+    ch <- newChan
+    tid <- forkFinally (loop ch store emptySample opts) $ \ r -> do
+        case r of
+            Left e  -> throwTo me e
+            Right _ -> return ()
+    return $ Push tid ch
+    where
+        emptySample = M.empty
+
+loop :: PushChanType -- ^ ekg-push clients subscribe to this channel
+     -> Metrics.Store   -- ^ Metric Store
+     -> Metrics.Sample  -- ^ Last sampled metrics
+     -> PushOptions  -- ^ Options
+     -> IO ()
+loop ch store lastSample opts = do
+    start <- time
+    sample <- Metrics.sampleAll store
+    let !diff = diffSamples opts lastSample sample
+    writeChan ch diff -- Write the Metrics.Sample to our broadcast channel.
+    end <- time
+    threadDelay (flushInterval opts * 1000 - fromIntegral (end - start))
+    loop ch store sample opts
+
+-- | Subscribe to the push broadcast channel.
+subscribe :: Push -> IO PushChan
+subscribe Push{..} = do
+    ch' <- dupChan mainCh
+    return $ PushChan {
+        ch = ch'
+    }
+
+-- | Consume a Metrics.Sample message from a subscribed channel.
+consume :: PushChan -> IO Metrics.Sample
+consume PushChan{..} = readChan ch
+
+-- | Microseconds since epoch.
+time :: IO Int64
+time = (round . (* 1000000.0) . toDouble) `fmap` getPOSIXTime
+  where toDouble = realToFrac :: Real a => a -> Double
+
+diffSamples :: PushOptions -> Metrics.Sample -> Metrics.Sample -> Metrics.Sample
+diffSamples opts prev curr = M.foldlWithKey' combine M.empty curr
+  where
+    combine m name new = case M.lookup name prev of
+        Just old -> case diffMetric old new of
+            Just val -> M.insert name' val m
+            Nothing  -> m
+        _        -> M.insert name' new m
+        where
+            name' = T.append (prefix opts) (T.append name (suffix opts))
+
+    diffMetric :: Metrics.Value -> Metrics.Value -> Maybe Metrics.Value
+    diffMetric (Metrics.Counter n1) (Metrics.Counter n2)
+        | n1 == n2  = Nothing
+        | otherwise = Just $! Metrics.Counter $ n2 - n1
+    diffMetric (Metrics.Gauge n1) (Metrics.Gauge n2)
+        | n1 == n2  = Nothing
+        | otherwise = Just $ Metrics.Gauge n2
+    diffMetric (Metrics.Label n1) (Metrics.Label n2)
+        | n1 == n2  = Nothing
+        | otherwise = Just $ Metrics.Label n2
+    -- Distributions are assumed to be non-equal.
+    diffMetric _ _  = Nothing
+
+------------------------------------------------------------------------
+-- Backwards compatibility shims
+
+#if __GLASGOW_HASKELL__ < 706
+forkFinally :: IO a -> (Either SomeException a -> IO ()) -> IO ThreadId
+forkFinally action and_then =
+  mask $ \restore ->
+    forkIO $ try (restore action) >>= and_then
+#endif
diff --git a/ekg-push.cabal b/ekg-push.cabal
new file mode 100644
--- /dev/null
+++ b/ekg-push.cabal
@@ -0,0 +1,40 @@
+name:                   ekg-push
+version:                0.0.2
+synopsis:               Small framework to push metric deltas to a broadcast channel using the ekg-core library.
+description:
+  This library lets you push metric samples to a broadcast channel. Consumers can then persist the samples as they wish.
+homepage:               https://github.com/adarqui/ekg-push
+bug-reports:            https://github.com/adarqui/ekg-push/issues
+license:                BSD3
+license-files:          LICENSE, LICENSE.Tibell
+author:                 Andrew Darqui
+maintainer:             andrew.darqui@gmail.com
+category:               System
+build-type:             Simple
+extra-source-files:     CHANGES.md
+cabal-version:          >=1.18
+extra-source-files:     Makefile, README.md
+
+library
+    exposed-modules:     
+                        System.Remote.Monitoring.Push
+    build-depends:
+                        base >= 4.5 && < 4.8,
+                        bytestring < 1.0,
+                        ekg-core == 0.1.0.3,
+                        text < 1.3,
+                        time < 1.5,
+                        unordered-containers < 0.3
+    default-language:   Haskell2010
+    ghc-options:        -Wall
+
+executable basic
+    main-is:            basic.hs
+    hs-source-dirs:     examples
+    build-depends:      base >= 4.5 && <= 4.9, ekg-core == 0.1.0.3, ekg-push == 0.0.2
+    default-language:   Haskell2010
+    ghc-options:        -Wall
+
+source-repository head
+  type:                 git
+  location:             https://github.com/adarqui/ekg-push.git
diff --git a/examples/basic.hs b/examples/basic.hs
new file mode 100644
--- /dev/null
+++ b/examples/basic.hs
@@ -0,0 +1,49 @@
+{-# LANGUAGE OverloadedStrings #-}
+module Main
+    ( main
+    ) where
+
+import Control.Concurrent (threadDelay, forkIO)
+import Control.Exception (evaluate)
+import Control.Monad (forever)
+import Data.List (foldl')
+import System.Metrics (newStore, registerGcMetrics, createCounter)
+import qualified System.Metrics.Counter as Counter (inc)
+import System.Remote.Monitoring.Push (PushOptions (..), forkPush, defaultPushOptions, subscribe, consume)
+
+-- 'sum' is using a non-strict lazy fold and will blow the stack.
+sum' :: Num a => [a] -> a
+sum' = foldl' (+) 0
+
+mean :: Fractional a => [a] -> a
+mean xs = sum' xs / fromIntegral (length xs)
+
+main :: IO ()
+main = do
+    store <- newStore
+    registerGcMetrics store
+    iters <- createCounter "iterations" store
+
+    -- Register our main ekg push channel
+    push <- forkPush defaultPushOptions { prefix = "pfx_", suffix = "_sfx" } store
+
+    -- Create two different subscriptions (ch1 & ch2)
+    ch1 <- subscribe push
+    _ <- forkIO $ forever $ do
+            msg <- consume ch1
+            putStrLn $ "subscription #1: " ++ show  msg
+
+    ch2 <- subscribe push
+    _ <- forkIO $ forever $ do
+            msg <- consume ch2
+            putStrLn $ "subscription #2: " ++ show msg
+
+    let 
+        loop :: Double -> IO ()
+        loop n = do
+            _ <- evaluate $ mean [1..n]
+            Counter.inc iters
+            threadDelay 2000
+            loop n
+
+    loop 1000000
