diff --git a/pub.cabal b/pub.cabal
--- a/pub.cabal
+++ b/pub.cabal
@@ -1,5 +1,5 @@
 Name:                   pub
-Version:                1.0.1
+Version:                2.0.0
 Author:                 Parnell Springmeyer <parnell@digitalmentat.com>
 Maintainer:             Parnell Springmeyer <parnell@digitalmentat.com>
 License:                BSD3
@@ -28,7 +28,7 @@
 
 Executable pub
   Default-Language:     Haskell2010
-  HS-Source-Dirs:       src
+  HS-Source-Dirs:       pub
   GHC-Options:          -threaded -Wall -fwarn-tabs -funbox-strict-fields
                         -fno-warn-orphans -fno-warn-unused-do-bind -rtsopts -with-rtsopts=-N
   Main-Is:              Main.hs
@@ -47,6 +47,31 @@
     hedis                     >= 0.6      && < 0.7,
     pipes                     >= 4.1.1    && < 4.2,
     pipes-bytestring          >= 2        && < 3,
+    mtl                       >= 2.1      && < 2.3,
+    system-filepath           >= 0.4.12   && < 0.5,
+    groom                     >= 0.1.2    && < 0.2,
+    time                      >= 1.4      && < 1.5,
+    transformers              >= 0.4      && < 0.5
+
+Executable sub
+  Default-Language:     Haskell2010
+  HS-Source-Dirs:       sub
+  GHC-Options:          -threaded -Wall -fwarn-tabs -funbox-strict-fields
+                        -fno-warn-orphans -fno-warn-unused-do-bind -rtsopts -with-rtsopts=-N
+  Main-Is:              Main.hs
+  Other-Modules:        Sub.Internal
+
+  Build-Depends:
+    base                      >= 4        && < 5,
+    network                   >= 2.4.2.2  && < 2.5,
+    bytestring                >= 0.10.0.2 && < 0.10.1,
+    cmdargs                   >= 0.10.7   && < 0.11,
+    hslogger                  >= 1.2.3    && < 1.3,
+    ConfigFile                >= 1.1.1    && < 1.2,
+    text                      >= 1.1.0.1  && < 1.2,
+    safe                      >= 0.3      && < 0.4,
+    containers                >= 0.5.0.0  && < 0.6,
+    hedis                     >= 0.6      && < 0.7,
     mtl                       >= 2.1      && < 2.3,
     system-filepath           >= 0.4.12   && < 0.5,
     groom                     >= 0.1.2    && < 0.2,
diff --git a/pub/Main.hs b/pub/Main.hs
new file mode 100644
--- /dev/null
+++ b/pub/Main.hs
@@ -0,0 +1,42 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Main
+-- Copyright   :  (C) 2014 Parnell Springmeyer
+-- License     :  AllRightsReserved
+-- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
+-- Stability   :  stable
+--
+-- This is the entry point for the `pub` executable. Main sets up
+-- the cli options configuration and parses configuration file options.
+----------------------------------------------------------------------------
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import           Data.Version           (showVersion)
+import           Paths_pub              (version)
+import           System.Console.CmdArgs
+
+import           Pub
+import           Pub.Internal
+
+-- | Command line argument configuration.
+--
+-- Help messages, switches, options, and the human-readable version
+-- number is configured here.
+programArgs :: PArgs
+programArgs = PArgs
+    { chan  = def &= argPos 0 &= typ "CHANNEL"
+    , host  = def &= name "h" &= typ "STRING" &= help "Redis host (default `localhost`)"
+    , port  = def &= name "p" &= help "Redis port (default `6379`)"
+    , db    = def &= name "d" &= help "Redis database (default `0`)"
+    } &=
+    verbosity &=
+    help    "Pipe stdin to a pub/sub channel." &=
+    summary ("pub v" ++ showVersion version) &=
+    noAtExpand &=
+    details ["Given an input on stdin, pipe to a redis pub/sub channel."]
+
+main :: IO ()
+main = cmdArgs programArgs >>= handleOpts >>= pipePublish
diff --git a/pub/Pub/Internal.hs b/pub/Pub/Internal.hs
new file mode 100644
--- /dev/null
+++ b/pub/Pub/Internal.hs
@@ -0,0 +1,66 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Pub.Internal
+-- Copyright   :  (C) 2014 Parnell Springmeyer
+-- License     :  AllRightsReserved
+-- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
+-- Stability   :  stable
+--
+-- Internal functions and types for the `Main` program. Mostly
+-- responsible for defining the `cmdargs` data type, parsing
+-- configuration, and setting the log level.
+----------------------------------------------------------------------------
+
+{-# LANGUAGE DataKinds          #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Pub.Internal where
+
+import           Control.Applicative
+import qualified Data.ByteString.Char8   as C8
+import           Database.Redis          as R
+import           Network.Socket.Internal as NSI (PortNumber)
+import           System.Console.CmdArgs
+import           System.Log.Logger
+import           Text.Groom              (groom)
+
+-- | Data type for program CLI options.
+data PArgs = PArgs
+    { chan :: String
+    , host :: Maybe String
+    , port :: Maybe Integer
+    , db   :: Maybe Integer
+    } deriving (Data, Typeable, Show, Eq)
+
+-- | Data type for settings once merged from CLI.
+data Settings = Settings
+    { loglevel  :: !Priority
+    , channel   :: C8.ByteString
+    , redisHost :: Maybe HostName
+    , redisPort :: Maybe PortID
+    , redisDB   :: Maybe Integer
+    } deriving (Show, Eq)
+
+-- | Given options, set the logging level, and create the settings
+-- record.
+handleOpts :: PArgs -> IO Settings
+handleOpts cliopts = do
+    whenNormal $ updateGlobalLogger "Console" (setLevel ERROR)
+    whenLoud   $ updateGlobalLogger "Console" (setLevel DEBUG)
+
+    let c    = C8.pack $ chan cliopts
+        p    = (fromInteger <$> (port cliopts)) :: Maybe NSI.PortNumber
+        conf = Settings { loglevel  = INFO
+                        , channel   = c
+                        , redisHost = host cliopts
+                        , redisPort = R.PortNumber <$> p
+                        , redisDB   = db cliopts
+                        }
+
+    debugM "Console" "Configuration parsed"
+    debugM "Console" $ groom conf
+
+    return conf
diff --git a/src/Main.hs b/src/Main.hs
deleted file mode 100644
--- a/src/Main.hs
+++ /dev/null
@@ -1,42 +0,0 @@
------------------------------------------------------------------------------
--- |
--- Module      :  Main
--- Copyright   :  (C) 2014 Parnell Springmeyer
--- License     :  AllRightsReserved
--- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
--- Stability   :  stable
---
--- This is the entry point for the `pub` executable. Main sets up
--- the cli options configuration and parses configuration file options.
-----------------------------------------------------------------------------
-
-{-# LANGUAGE OverloadedStrings #-}
-
-module Main where
-
-import           Data.Version           (showVersion)
-import           Paths_pub              (version)
-import           System.Console.CmdArgs
-
-import           Pub
-import           Pub.Internal
-
--- | Command line argument configuration.
---
--- Help messages, switches, options, and the human-readable version
--- number is configured here.
-programArgs :: PArgs
-programArgs = PArgs
-    { chan  = def &= argPos 0 &= typ "CHANNEL"
-    , host  = def &= name "h" &= typ "STRING" &= help "Redis host (default `localhost`)"
-    , port  = def &= name "p" &= help "Redis port (default `6379`)"
-    , db    = def &= name "d" &= help "Redis database (default `0`)"
-    } &=
-    verbosity &=
-    help    "Pipe stdin to a pub/sub channel." &=
-    summary ("pub v" ++ showVersion version) &=
-    noAtExpand &=
-    details ["Given an input on stdin, pipe to a redis pub/sub channel."]
-
-main :: IO ()
-main = cmdArgs programArgs >>= handleOpts >>= pipePublish
diff --git a/src/Pub/Internal.hs b/src/Pub/Internal.hs
deleted file mode 100644
--- a/src/Pub/Internal.hs
+++ /dev/null
@@ -1,66 +0,0 @@
------------------------------------------------------------------------------
--- |
--- Module      :  Pub.Internal
--- Copyright   :  (C) 2014 Parnell Springmeyer
--- License     :  AllRightsReserved
--- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
--- Stability   :  stable
---
--- Internal functions and types for the `Main` program. Mostly
--- responsible for defining the `cmdargs` data type, parsing
--- configuration, and setting the log level.
-----------------------------------------------------------------------------
-
-{-# LANGUAGE DataKinds          #-}
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE OverloadedStrings  #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-
-module Pub.Internal where
-
-import           Control.Applicative
-import qualified Data.ByteString.Char8   as C8
-import           Database.Redis          as R
-import           Network.Socket.Internal as NSI (PortNumber)
-import           System.Console.CmdArgs
-import           System.Log.Logger
-import           Text.Groom              (groom)
-
--- | Data type for program CLI options.
-data PArgs = PArgs
-    { chan :: String
-    , host :: Maybe String
-    , port :: Maybe Integer
-    , db   :: Maybe Integer
-    } deriving (Data, Typeable, Show, Eq)
-
--- | Data type for settings once merged from CLI.
-data Settings = Settings
-    { loglevel  :: !Priority
-    , channel   :: C8.ByteString
-    , redisHost :: Maybe HostName
-    , redisPort :: Maybe PortID
-    , redisDB   :: Maybe Integer
-    } deriving (Show, Eq)
-
--- | Given options, set the logging level, and create the settings
--- record.
-handleOpts :: PArgs -> IO Settings
-handleOpts cliopts = do
-    whenNormal $ updateGlobalLogger "Console" (setLevel ERROR)
-    whenLoud   $ updateGlobalLogger "Console" (setLevel DEBUG)
-
-    let c    = C8.pack $ chan cliopts
-        p    = (fromInteger <$> (port cliopts)) :: Maybe NSI.PortNumber
-        conf = Settings { loglevel  = INFO
-                        , channel   = c
-                        , redisHost = host cliopts
-                        , redisPort = R.PortNumber <$> p
-                        , redisDB   = db cliopts
-                        }
-
-    debugM "Console" "Configuration parsed"
-    debugM "Console" $ groom conf
-
-    return conf
diff --git a/sub/Main.hs b/sub/Main.hs
new file mode 100644
--- /dev/null
+++ b/sub/Main.hs
@@ -0,0 +1,42 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Main
+-- Copyright   :  (C) 2014 Parnell Springmeyer
+-- License     :  AllRightsReserved
+-- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
+-- Stability   :  stable
+--
+-- This is the entry point for the `sub` executable. Main sets up
+-- the cli options configuration and parses configuration file options.
+----------------------------------------------------------------------------
+
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+import           Data.Version           (showVersion)
+import           Paths_pub              (version)
+import           System.Console.CmdArgs
+
+import           Sub
+import           Sub.Internal
+
+-- | Command line argument configuration.
+--
+-- Help messages, switches, options, and the human-readable version
+-- number is configured here.
+programArgs :: PArgs
+programArgs = PArgs
+    { chan  = def &= argPos 0 &= typ "CHANNEL"
+    , host  = def &= name "h" &= typ "STRING" &= help "Redis host (default `localhost`)"
+    , port  = def &= name "p" &= help "Redis port (default `6379`)"
+    , db    = def &= name "d" &= help "Redis database (default `0`)"
+    } &=
+    verbosity &=
+    help    "Pipe from a pub/sub channel to stdout." &=
+    summary ("sub v" ++ showVersion version) &=
+    noAtExpand &=
+    details ["Given a redis pub/sub channel, pipe to stdout."]
+
+main :: IO ()
+main = cmdArgs programArgs >>= handleOpts >>= pipePublish
diff --git a/sub/Sub/Internal.hs b/sub/Sub/Internal.hs
new file mode 100644
--- /dev/null
+++ b/sub/Sub/Internal.hs
@@ -0,0 +1,66 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Sub.Internal
+-- Copyright   :  (C) 2014 Parnell Springmeyer
+-- License     :  AllRightsReserved
+-- Maintainer  :  Parnell Springmeyer <parnell@digitalmentat.com>
+-- Stability   :  stable
+--
+-- Internal functions and types for the `Main` program. Mostly
+-- responsible for defining the `cmdargs` data type, parsing
+-- configuration, and setting the log level.
+----------------------------------------------------------------------------
+
+{-# LANGUAGE DataKinds          #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE OverloadedStrings  #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+module Sub.Internal where
+
+import           Control.Applicative
+import qualified Data.ByteString.Char8   as C8
+import           Database.Redis          as R
+import           Network.Socket.Internal as NSI (PortNumber)
+import           System.Console.CmdArgs
+import           System.Log.Logger
+import           Text.Groom              (groom)
+
+-- | Data type for program CLI options.
+data PArgs = PArgs
+    { chan :: String
+    , host :: Maybe String
+    , port :: Maybe Integer
+    , db   :: Maybe Integer
+    } deriving (Data, Typeable, Show, Eq)
+
+-- | Data type for settings once merged from CLI.
+data Settings = Settings
+    { loglevel  :: !Priority
+    , channel   :: C8.ByteString
+    , redisHost :: Maybe HostName
+    , redisPort :: Maybe PortID
+    , redisDB   :: Maybe Integer
+    } deriving (Show, Eq)
+
+-- | Given options, set the logging level, and create the settings
+-- record.
+handleOpts :: PArgs -> IO Settings
+handleOpts cliopts = do
+    whenNormal $ updateGlobalLogger "Console" (setLevel ERROR)
+    whenLoud   $ updateGlobalLogger "Console" (setLevel DEBUG)
+
+    let c    = C8.pack $ chan cliopts
+        p    = (fromInteger <$> (port cliopts)) :: Maybe NSI.PortNumber
+        conf = Settings { loglevel  = INFO
+                        , channel   = c
+                        , redisHost = host cliopts
+                        , redisPort = R.PortNumber <$> p
+                        , redisDB   = db cliopts
+                        }
+
+    debugM "Console" "Configuration parsed"
+    debugM "Console" $ groom conf
+
+    return conf
