diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,5 @@
+# Revision history for machines-bitestring
+
+## 0.1.0.0
+
+* initial release
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2017, Claudio Zanasi
+
+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 Claudio Zanasi 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/machines-bytestring.cabal b/machines-bytestring.cabal
new file mode 100644
--- /dev/null
+++ b/machines-bytestring.cabal
@@ -0,0 +1,25 @@
+name:                machines-bytestring
+version:             0.1.0.0
+cabal-version:       >=1.10
+build-type:          Simple
+synopsis:            ByteString support for machines
+description:         @machines@ utilities to work with @ByteString@
+license:             BSD3
+license-file:        LICENSE
+author:              Claudio Zanasi
+maintainer:          claudio.zan@outlook.com
+copyright:           2017 Claudio Zanasi
+category:            Control, Machines
+extra-source-files:  ChangeLog.md
+Source-Repository head
+    Type: git
+    Location: https://github.com/zclod/machines-bytestring
+
+library
+  exposed-modules:     Data.Machine.ByteString
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  GHC-Options:         -Wall -O2
+  build-depends:       base       >= 4    && < 5,
+                       bytestring >= 0.10 && < 0.11,
+                       machines   >= 0.6  && < 0.7
diff --git a/src/Data/Machine/ByteString.hs b/src/Data/Machine/ByteString.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Machine/ByteString.hs
@@ -0,0 +1,162 @@
+{-# LANGUAGE Rank2Types #-}
+module Data.Machine.ByteString (
+  -- * Sources
+  fromLazy,
+  fromStrict,
+  fromHandle,
+  hGet,
+  hGetSome,
+  hGetNonBlocking,
+  -- * Sinks
+  stdin,
+  stdout,
+  stderr,
+  toHandle,
+  interact,
+  -- * Processes
+  mapping,
+  filtered,
+  taking,
+  takingWhile,
+  scan,
+  -- Stream Transformation
+  pack,
+  unpack
+  ) where
+
+import           Data.Word (Word8)
+import           Data.Machine hiding (filtered, mapping, taking, takingWhile, scan)
+import           Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import           Data.ByteString.Unsafe (unsafeTake)
+import           Data.ByteString.Lazy.Builder.Extras (defaultChunkSize)
+import qualified Data.ByteString.Lazy as BL
+import qualified System.IO as IO
+import           Control.Monad.IO.Class (MonadIO(..))
+import           Prelude hiding (interact)
+
+-- | Transform a lazy ByteString into a 'Source' of strict 'ByteString'.
+fromLazy :: BL.ByteString -> Source ByteString
+fromLazy bs = construct $ BL.foldrChunks (\e a -> yield e >> a) (pure ()) bs
+
+-- | Use a strict ByteString as Source
+fromStrict :: ByteString -> Source ByteString
+fromStrict bs = construct $ yield bs
+
+-- | Use an 'Handle' to create a 'Source' of strict 'ByteString'.
+fromHandle :: MonadIO m => IO.Handle -> SourceT m ByteString
+fromHandle = hGetSome defaultChunkSize
+
+-- | Read chunks of strict 'ByteString' of fixed size.
+hGet :: MonadIO m => Int -> IO.Handle -> SourceT m ByteString
+hGet size h = repeatedly $ do
+  bs <- liftIO (B.hGet h size)
+  if B.null bs
+     then pure ()
+     else yield bs
+
+-- | Like 'hGet' but may be returned smaller chunks if there are not enough
+-- data immediately available.
+hGetSome :: MonadIO m => Int -> IO.Handle -> SourceT m ByteString
+hGetSome size h = repeatedly $ do
+  bs <- liftIO (B.hGetSome h size)
+  if B.null bs
+     then pure ()
+     else yield bs
+
+-- | Similar to 'hGet' but it will never block waiting for data.
+hGetNonBlocking :: MonadIO m => Int -> IO.Handle -> SourceT m ByteString
+hGetNonBlocking size h = repeatedly $ do
+  eof <- liftIO (IO.hIsEOF h)
+  if eof
+     then pure ()
+     else liftIO (B.hGetNonBlocking h size)>>= yield
+
+-- | Source that stream bytes from 'stdin'
+stdin :: MonadIO m => SourceT m ByteString
+stdin = fromHandle IO.stdin
+
+-- | Create a 'Machine' that stream its input to the given 'Handle'
+toHandle :: MonadIO m => IO.Handle -> MachineT m (Is ByteString) ()
+toHandle h = autoM (liftIO . B.hPut h)
+
+-- | Stream data to 'stdout'
+stdout :: MonadIO m => MachineT m (Is ByteString) ()
+stdout = toHandle IO.stdout
+
+-- | Stream data to 'stderr'
+stderr :: MonadIO m => MachineT m (Is ByteString) ()
+stderr = toHandle IO.stderr
+
+-- | Use a @Process ByteString ByteString@ to transform the input from
+-- 'stdin' and output to 'stdout'
+interact :: Process ByteString ByteString -> IO ()
+interact p = runT_ $ stdin ~> p ~> stdout
+
+-- | Transform every byte of a 'ByteString'
+mapping :: (Word8 -> Word8) -> Process ByteString ByteString
+mapping f = auto $ B.map f
+
+-- | Allow to pass only the bytes that satisfy the predicate
+filtered :: (Word8 -> Bool) -> Process ByteString ByteString
+filtered p = auto $ B.filter p
+
+-- | Stream only the first n bytes
+taking :: Integral n => n -> Process ByteString ByteString
+taking n0 = construct $ go n0
+  where
+    go n
+        | n <= 0 = pure ()
+        | otherwise = do
+          bs <- await
+          let len = fromIntegral (B.length bs)
+          if len > n
+             then yield (unsafeTake (fromIntegral n) bs)
+             else yield bs >> go (n - len)
+
+-- | Stream bytes until they fail the predicate
+takingWhile :: (Word8 -> Bool) -> Process ByteString ByteString
+takingWhile p = construct go
+  where
+    go = do
+      bs <- await
+      let (prefix, suffix) = B.span p bs
+      if B.null suffix
+         then yield bs >> go
+         else yield prefix
+
+-- Drop the first n bytes
+-- dropping :: Integral n => n -> Process ByteString ByteString
+-- dropping n = loop n
+--   where
+--     loop cnt
+--       | cnt <= 0  = echo
+--       | otherwise = do
+--                       bs <- await
+--                       let l = B.length bs
+--                       if l <= cnt
+--                          then loop (cnt - l)
+--                          else pure . B.drop l >>= yield >> loop (cnt - l)
+
+
+-- | Strict left scan over the bytes
+scan :: (Word8 -> Word8 -> Word8) -> Word8 -> Process ByteString ByteString
+scan step begin = construct $ do
+    yield (B.singleton begin)
+    go begin
+  where
+    go w8 = do
+        bs <- await
+        let bs' = B.scanl step w8 bs
+            w8' = B.last bs'
+        yield (B.tail bs')
+        go w8'
+
+-- | Convert a stream of strict 'ByteString' into a stream of Bytes
+unpack :: Process ByteString Word8
+unpack = auto B.unpack ~> asParts
+
+-- | Convert a stream of Bytes into a stream of strict 'ByteString' of
+-- size equal to 'defaultChunkSize'
+pack :: Process Word8 ByteString
+pack = buffered defaultChunkSize ~> auto B.pack
