diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+
+  Pipe/Iteratee Bridge
+  ====================
+
+    Copyright © 2015 Patryk Zadarnowski «pat@jantar.org».
+    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. ➂ The name of any
+  author may not be used to endorse or promote products derived from this
+  software without their specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED “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 AUTHORS 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,3 @@
+#! /usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/pipe-enumerator.cabal b/pipe-enumerator.cabal
new file mode 100644
--- /dev/null
+++ b/pipe-enumerator.cabal
@@ -0,0 +1,43 @@
+name:           pipe-enumerator
+version:        0.1.0.0
+synopsis:       Pipes/iteratees bridge library
+homepage:       https://github.com/zadarnowski/pipe-enumerator
+category:       Control, Pipes
+stability:      alpha
+
+author:         Patryk Zadarnowski
+maintainer:     Patryk Zadarnowski <pat@jantar.org>
+
+copyright:      Copyright (c) 2015 Patryk Zadarnowski
+
+description:
+
+    This library provides a bridge between pipe-based and iteratee-based programs.
+
+cabal-version:  >= 1.18
+build-type:     Simple
+license:        BSD3
+license-file:   LICENSE
+
+source-repository head
+  type:         git
+  location:     https://github.com/zadarnowski/pipe-enumerator.git
+
+source-repository this
+  type:         git
+  location:     https://github.com/zadarnowski/pipe-enumerator.git
+  tag:          0.1.0.0
+
+library
+  hs-source-dirs:   src
+  default-language: Haskell2010
+  ghc-options:      -Wall -fno-warn-unused-do-bind -fno-warn-unused-binds
+
+  exposed-modules:
+    Pipes.Enumerator
+
+  build-depends:
+    base                    >= 4.8 && < 5,
+    enumerator              >= 0.4.20,
+    pipes                   >= 4.1.5,
+    transformers            >= 0.4.2.0
diff --git a/src/Pipes/Enumerator.lhs b/src/Pipes/Enumerator.lhs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Enumerator.lhs
@@ -0,0 +1,122 @@
+> module Pipes.Enumerator (
+>   pipeToEnumerator
+> ) where
+
+> import Control.Monad.Trans.Class
+> import qualified Pipes.Internal as P
+> import qualified Data.Enumerator as E
+
+  Data Type Reference
+  ===================
+
+    data Proxy a' a b' b m r
+        = Request a' (a  -> Proxy a' a b' b m r )
+        | Respond b  (b' -> Proxy a' a b' b m r )
+        | M          (m    (Proxy a' a b' b m r))
+        | Pure    r
+
+    type Pipe a b = Proxy () a () b
+
+    type Enumerator a m b = Step a m b -> Iteratee a m b
+
+    data Step a m b =
+        Continue (Stream a -> Iteratee a m b)
+      | Yield b (Stream a)
+      | Error SomeException
+
+    newtype Iteratee a m b = Iteratee { runIteratee :: m (Step a m b) }
+
+    data Stream a =
+        Chunks [a]
+      | EOF
+
+  Connect a pipe to an iteratee, effectively converting it into an enumerator,
+  generalised slightly to allow distinct input and output types. The chunks of
+  the input stream are fed into the pipe one element at the time, and the pipe's
+  output is fed to the iteratee one element per chunk. Once the input reaches
+  @E.EOF@, the pipe is fed an infinite stream of @Nothing@ until it ends with
+  a pure value. In effect, both the pipe and the iteratee are always consumed
+  fully, with the resulting enumerator returning the results of both.
+
+> pipeToEnumerator :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
+> pipeToEnumerator = advanceIteratee []
+>  where
+
+    State Machine 1
+    ===============
+
+    Advance a single iteratee step, keeping track of any input @cs@ that has been
+    supplied by the upstream enumerator but not yet used. If the target iteratee blocks,
+    we will supply it with input obtained by advancing source pipe; otherwise, we'll
+    simply drain the pipe to completion, in order to retrieve the remainder of the
+    upstream input and the pipe's return value:
+
+>   advanceIteratee :: Monad m => [a] -> P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
+>   advanceIteratee cs p (E.Continue ik)      = advancePipe cs ik p
+>   advanceIteratee cs p s                    = drainPipe cs s p
+
+    Advance a pipe, feeding its response into a blocked iteratee @ik@.
+
+>   advancePipe :: Monad m => [a] -> (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
+>   advancePipe cs ik (P.Request _ pk)        = advancePipeWithChunks ik pk cs
+>   advancePipe cs ik (P.Respond x pk)        = ik (E.Chunks [x]) E.>>== advanceIteratee cs (pk ())
+>   advancePipe cs ik (P.M m)                 = lift m >>= advancePipe cs ik
+>   advancePipe cs ik (P.Pure q)              = ik (E.EOF) E.>>== finish q (E.Chunks cs)
+
+>   advancePipeWithChunks :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
+>   advancePipeWithChunks ik pk (c:cs)        = advancePipe cs ik (pk (Just c))
+>   advancePipeWithChunks ik pk []            = E.continue (advancePipeWithStream ik pk)
+
+>   advancePipeWithStream :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)
+>   advancePipeWithStream ik pk (E.Chunks cs) = advancePipeWithChunks ik pk cs
+>   advancePipeWithStream ik pk (E.EOF)       = advancePipeWithEOF ik pk
+
+>   drainPipe :: Monad m => [a] -> E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
+>   drainPipe cs s (P.Request _ pk)           = drainPipeWithChunks s pk cs
+>   drainPipe cs s (P.Respond _ pk)           = drainPipe cs s (pk ())
+>   drainPipe cs s (P.M m)                    = lift m >>= drainPipe cs s
+>   drainPipe cs s (P.Pure q)                 = finish q (E.Chunks cs) s
+
+>   drainPipeWithChunks :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> [a] -> E.Iteratee a m (q, r)
+>   drainPipeWithChunks s pk (c:cs)           = drainPipe cs s (pk (Just c))
+>   drainPipeWithChunks s pk []               = E.continue (drainPipeWithStream s pk)
+
+>   drainPipeWithStream :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Stream a -> E.Iteratee a m (q, r)
+>   drainPipeWithStream s pk (E.Chunks cs)    = drainPipeWithChunks s pk cs
+>   drainPipeWithStream s pk (E.EOF)          = drainPipeWithEOF s pk
+
+
+    State Machine 2
+    ===============
+
+>   advanceIteratee' :: Monad m => P.Proxy a' (Maybe a) () b m q -> E.Step b m r -> E.Iteratee a m (q, r)
+>   advanceIteratee' p (E.Continue ik)        = advancePipe' ik p
+>   advanceIteratee' p s                      = drainPipe' s p
+
+>   advancePipe' :: Monad m => (E.Stream b -> E.Iteratee b m r) -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
+>   advancePipe' ik (P.Request _ pk)          = advancePipeWithEOF ik pk
+>   advancePipe' ik (P.Respond x pk)          = ik (E.Chunks [x]) E.>>== advanceIteratee' (pk ())
+>   advancePipe' ik (P.M m)                   = lift m >>= advancePipe' ik
+>   advancePipe' ik (P.Pure q)                = ik (E.EOF) E.>>== finish q (E.EOF)
+
+>   advancePipeWithEOF :: Monad m => (E.Stream b -> E.Iteratee b m r) -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
+>   advancePipeWithEOF ik pk                  = advancePipe' ik (pk Nothing)
+
+>   drainPipe' :: Monad m => E.Step b m r -> P.Proxy a' (Maybe a) () b m q -> E.Iteratee a m (q, r)
+>   drainPipe' s (P.Request _ pk)             = drainPipeWithEOF s pk
+>   drainPipe' s (P.Respond _ pk)             = drainPipe' s (pk ())
+>   drainPipe' s (P.M m)                      = lift m >>= drainPipe' s
+>   drainPipe' s (P.Pure q)                   = finish q (E.EOF) s
+
+>   drainPipeWithEOF :: Monad m => E.Step b m r -> (Maybe a -> P.Proxy a' (Maybe a) () b m q) -> E.Iteratee a m (q, r)
+>   drainPipeWithEOF s pk                     = drainPipe' s (pk Nothing)
+
+
+    Final Step
+    ==========
+
+>   finish :: Monad m => q -> E.Stream a -> E.Step b m r -> E.Iteratee a m (q, r)
+>   finish q xs (E.Yield r _)                 = E.yield (q, r) xs
+>   finish _ _  (E.Error e)                   = E.throwError e
+>   finish _ _  (E.Continue _)                = error "divergent iteratee" -- iteratees aren't allowed to continue on EOF
+
