diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, Kamil Ciemniewski
+
+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 Kamil Ciemniewski 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/snaplet-tasks.cabal b/snaplet-tasks.cabal
new file mode 100644
--- /dev/null
+++ b/snaplet-tasks.cabal
@@ -0,0 +1,78 @@
+-- snaplet-tasks.cabal auto-generated by cabal init. For additional
+-- options, see
+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.
+-- The name of the package.
+Name:                snaplet-tasks
+
+-- The package version. See the Haskell package versioning policy
+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for
+-- standards guiding when and how versions should be incremented.
+Version:             0.1
+
+-- A short (one-line) description of the package.
+Synopsis:            Snaplet for Snap Framework enabling developers to administrative tasks akin to Rake tasks from Ruby On Rails framework.
+
+-- A longer description of the package.
+-- Description:         
+
+-- The license under which the package is released.
+License:             BSD3
+
+-- The file containing the license text.
+License-file:        LICENSE
+
+-- The package author(s).
+Author:              Kamil Ciemniewski
+
+-- An email address to which users can send suggestions, bug reports,
+-- and patches.
+Maintainer:          ciemniewski.kamil@gmail.com
+
+-- A copyright notice.
+-- Copyright:           
+
+Category:            Web
+
+Build-type:          Simple
+
+-- Extra files to be distributed with the package, such as examples or
+-- a README.
+-- Extra-source-files:  
+
+-- Constraint on the version of Cabal needed to build this package.
+cabal-version: >= 1.6
+
+Library
+  hs-source-dirs: src
+
+  library
+  exposed-modules:  Snap.Snaplet.Tasks,
+                    Snap.Snaplet.Tasks.Utils
+
+  other-modules:    Snap.Snaplet.Tasks.Internal,
+                    Snap.Snaplet.Tasks.Types
+  
+  -- Packages needed in order to build this package.
+  Build-depends:
+    base >= 4 && < 5,
+    bytestring >= 0.9.1 && < 0.10,
+    network == 2.3.0.2,
+    MissingH == 1.1.1.0,
+    curl == 1.3.7,
+    data-hash == 0.1.0.0,
+    snap == 0.7.*,
+    snap-core   == 0.7.*,
+    mtl >= 2 && < 3,
+    containers == 0.4.0.0,
+    haskell98 == 1.1.0.1
+  
+  -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
+  -- Build-tools:    
+  
+  extensions: 
+      OverloadedStrings
+    , FlexibleInstances
+    , TypeSynonymInstances
+    , MultiParamTypeClasses 
+              
+  
diff --git a/src/Snap/Snaplet/Tasks.hs b/src/Snap/Snaplet/Tasks.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/Tasks.hs
@@ -0,0 +1,46 @@
+{-# LANGUAGE OverloadedStrings #-}
+-- | This module contains definition of a Tasks Snaplet for 
+--   Snap >= 0.7.*. It allows Snap app developers to create command 
+--   line tasks akin to "rake tasks" found in Ruby On Rails framework.
+--   
+--   Essenstially, this snaplet let's your other snaplet's to have 
+--   their administrative tasks that You'd call from command line 
+--   to - let's say create indexes in Your DB or screen scrape some 
+--   useful data from some service and save it in DB.
+--
+--   Every task is in fact just a handler for route. Those routes 
+--   are hashes for routes so that 'somens:other:cool:task' becomes
+--   a valid route in app.
+--   To create such task in one of Your snaplets (maybe in your app
+--   snaplet) - define route for it using handy 'task' function
+--   that this module reexports.
+--
+--   Running tasks is fairly simple: 
+--   yourapp T snaplet:super:cool arg1=v1 arg2=v2 [-p 1000]
+--   This means that your task command always follows 'T'
+--   If you're running Your app at default port You can specify
+--   different port at the end by using standard -p argument.
+
+module Snap.Snaplet.Tasks( tasksInit,
+                           TasksSnaplet,
+                           module Snap.Snaplet.Tasks.Utils ) where
+
+import           Snap.Snaplet
+import           Snap.Snaplet.Tasks.Internal
+import           Snap.Snaplet.Tasks.Utils
+
+import           Control.Monad.Reader
+import           Control.Concurrent
+
+data TasksSnaplet = TasksSnaplet
+
+-- | This method spawns a new thread that waits till
+--   server is ready and then fires given task.
+tasksInit :: SnapletInit b TasksSnaplet
+tasksInit = makeSnaplet "tasks" "Snap Tasks" Nothing $ do
+  liftIO $ do
+    mtask <- taskFromCommandLine
+    case mtask of
+      Nothing   -> return ()
+      Just task -> (forkIO $ runTask task) >> return ()
+  return $ TasksSnaplet
diff --git a/src/Snap/Snaplet/Tasks/Internal.hs b/src/Snap/Snaplet/Tasks/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/Tasks/Internal.hs
@@ -0,0 +1,111 @@
+module Snap.Snaplet.Tasks.Internal where
+
+import           System( getArgs )
+import           System.Exit
+
+import           Snap.Snaplet.Tasks.Types
+
+import           Control.Concurrent
+
+import qualified Data.ByteString.Char8 as B
+import           Data.List (intercalate)
+import qualified Data.Map as Map
+import qualified Data.List.Utils as U
+import           Data.Hash
+
+import           Network.Curl
+
+-- | This function reads options from command line
+--   and if it sees any options after "T" it
+--   tries to compile them into task.
+taskFromCommandLine :: IO (Maybe Task)
+taskFromCommandLine = do
+  taskArgs <- return . tail . dropWhile ((/=) "T") =<< getArgs
+  case null taskArgs of
+    True  -> return Nothing
+    False -> return $ Just $ taskForArgs taskArgs
+  where
+    taskForArgs :: [String] -> Task
+    taskForArgs args = Task
+      (namespace heading)
+      (name      heading)
+      (taskargs  restargs)
+      portFromArgs
+      where
+        heading :: [String]
+        heading = U.split ":" $ head args
+
+        portFromArgs :: String
+        portFromArgs =
+          case dropWhile ((/=) "-p") args of
+            [] -> "8000"
+            xs -> head $ tail xs
+
+        restargs :: [(B.ByteString, B.ByteString)]
+        restargs = map toKeyValue $ takeWhile ((/=) "-p") $ tail args
+ 
+        toKeyValue :: String -> (B.ByteString, B.ByteString)
+        toKeyValue s = (sp !! 0, sp !! 1)
+         where
+          sp = map B.pack $ U.split "=" s
+
+        namespace :: [String] -> String
+        namespace = head
+
+        name :: [String] -> String
+        name = (intercalate "/") . tail
+
+        taskargs :: [(B.ByteString, B.ByteString)] -> Map.Map B.ByteString B.ByteString
+        taskargs = Map.fromList
+
+-- | This function lives in different thread than main app thread
+--   and for given task it waits until app is ready to serve requests 
+--   and then it fires request described in that task.
+--   
+--   For example if you had a task with namespace 'supercool' and
+--   name 'task' and params id=1234 number=12345 url=google.com
+--   then it'd fire PUT request for url: 
+--   'localhost:[port]/supercool/task?id=1234&number=12345&url=google.com'
+--   If 404 occurs it yells at user that such task does not exist.
+--   For task name 'task:cool' url would start like:
+--   'localhost/supercool/task/cool?id....' 
+runTask :: Task -> IO ()
+runTask task = waitForApp >> requestTask
+  where
+    waitForApp = do
+      (code, _) <- curlGetString "http://localhost" 
+                     [CurlPort $ read $ taskPort task]
+      case code of
+        CurlOK -> return ()
+        _      -> threadDelay 100 >> waitForApp
+
+    requestTask = do
+      resp <- curlGetResponse taskUrl 
+                        [CurlPort $ read $ taskPort task]
+      case respCurlCode resp of
+        CurlOK -> putStrLn   "Task ended successfully"
+        _      -> 
+          case respStatus resp of
+            404 -> putStrLn   "No such task in application"
+            _   -> putStrLn $ "Task request ended with HTTP code " ++
+                                (show $ respStatus resp)
+
+    taskUrl :: URLString
+    taskUrl = "http://localhost/" ++ (taskNamespace task) ++
+               "/" ++ nameForTask ++ argsToUrl
+
+    nameForTask :: String
+    nameForTask = hashString $ taskName task
+
+    argsToUrl :: String
+    argsToUrl = 
+      case Map.null $ taskArgs task of
+        True  -> ""
+        False -> "?" ++ (intercalate "&" $ map toArg $ Map.toList $ taskArgs task)
+    
+    toArg :: (B.ByteString, B.ByteString) -> String
+    toArg (k, v) = (B.unpack k) ++ "=" ++ (B.unpack v)
+
+-- | This function takes a string and returns a hash for it
+hashString :: String -> String
+hashString s = show $ asWord64 $ foldl combine (hashInt $ length s) $ map hash s
diff --git a/src/Snap/Snaplet/Tasks/Types.hs b/src/Snap/Snaplet/Tasks/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/Tasks/Types.hs
@@ -0,0 +1,21 @@
+module Snap.Snaplet.Tasks.Types where
+
+import           Data.ByteString
+import qualified Data.Map as Map
+
+-- | Formalized task given at command line by 
+--   user. 
+--   If you'd have "supercool" snaplet that would
+--   register one of it's task through "/sometask"
+--   route then at command line user would have to 
+--   run Snap App with: 'snapapp T supercool:sometask'
+--   Params for that task would be given at command line
+--   like this: 'snapapp T supercool:sometask param1=v1 param2=v2'
+--   This means that everything after T at command line is
+--   treated as task description.
+data Task = Task {
+  taskNamespace :: String,
+  taskName      :: String,
+  taskArgs      :: Map.Map ByteString ByteString,
+  taskPort      :: String
+} deriving (Show)
diff --git a/src/Snap/Snaplet/Tasks/Utils.hs b/src/Snap/Snaplet/Tasks/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Snaplet/Tasks/Utils.hs
@@ -0,0 +1,28 @@
+module Snap.Snaplet.Tasks.Utils(task) where
+
+import           Snap.Core
+import           Snap.Snaplet
+import           Snap.Snaplet.Tasks.Internal
+
+import qualified Data.ByteString.Char8 as B
+import qualified Data.List.Utils as U
+import           Data.List (intercalate)
+
+-- | Helper function for creating tasks in Snaplets routes
+--   that are available through Tasks Snaplet interface
+task :: String -> (Handler b v ()) -> (B.ByteString, Handler b v ())
+task name handler = (B.pack $ hashString canonizedName, taskify handler)
+  where
+    canonizedName = intercalate "/" $ U.split ":" name
+
+taskify :: Handler b v () -> Handler b v ()
+taskify handler = do
+   --now here we *must* check if our apps IP
+   --is the same as request's IP and if it is
+   --continue or else pass
+  setTimeout $ 60*60*24*31 -- approx 31 days
+  localIp   <- return . rqLocalAddr  =<< getRequest
+  requestIp <- return . rqRemoteAddr =<< getRequest
+  case localIp == requestIp of
+    True  -> handler
+    False -> pass
