diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016, Ben Gamari
+
+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 Ben Gamari 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/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,20 @@
+import Control.Monad (void)
+import Pipes
+import Pipes.Lzma
+import qualified Pipes.Prelude as PP
+import qualified Data.ByteString as BS
+
+import Test.QuickCheck
+import Test.QuickCheck.Monadic
+
+roundTrip :: [BS.ByteString] -> Property
+roundTrip bss = monadicIO $ do
+    bss' <- run $ PP.toListM $ void $ decompress $ compress $ each bss
+    monitor $ counterexample $ show bss'
+    assert (BS.concat bss' == BS.concat bss)
+
+main :: IO ()
+main = quickCheck roundTrip
+
+instance Arbitrary BS.ByteString where
+    arbitrary = BS.pack <$> arbitrary
diff --git a/Unxz.hs b/Unxz.hs
new file mode 100644
--- /dev/null
+++ b/Unxz.hs
@@ -0,0 +1,9 @@
+import Pipes
+import Control.Monad (void)
+import qualified Pipes.ByteString as PBS
+import qualified Pipes.Lzma as Lzma
+
+main :: IO ()
+main = do
+    Pipes.runEffect $ void (Lzma.decompress PBS.stdin) >-> PBS.stdout
+    return ()
diff --git a/pipes-lzma.cabal b/pipes-lzma.cabal
new file mode 100644
--- /dev/null
+++ b/pipes-lzma.cabal
@@ -0,0 +1,46 @@
+name:                pipes-lzma
+version:             0.1.0.0
+synopsis:            LZMA compressors and decompressors for the Pipes package
+description:
+    This package provides a @pipes@ interface to the LZMA compression algorithm
+    used by the @.xz@ file format.
+homepage:            http://github.com/bgamari/pipes-lzma
+license:             BSD3
+license-file:        LICENSE
+author:              Ben Gamari
+maintainer:          ben@smart-cactus.org
+copyright:           (c) 2016 Ben Gamari
+category:            Codec
+build-type:          Simple
+cabal-version:       >=1.10
+
+source-repository head
+  type:                git
+  location:            git://github.com/bgamari/pipes-lzma
+
+library
+  exposed-modules:     Pipes.Lzma
+  build-depends:       base >=4.6 && <4.10,
+                       bytestring >=0.10 && <0.11,
+                       pipes >=4.0 && <4.3,
+                       lzma >=0.0.0.1 && <0.1
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+executable pipes-lzma-tests
+  main-is:             Test.hs
+  build-depends:       base >=4.6 && <4.10,
+                       bytestring,
+                       pipes,
+                       pipes-bytestring,
+                       pipes-lzma,
+                       QuickCheck
+  default-language:    Haskell2010
+
+executable pipes-lzma-unxz
+  main-is:             Unxz.hs
+  build-depends:       base >=4.6 && <4.10,
+                       pipes,
+                       pipes-bytestring,
+                       pipes-lzma
+  default-language:    Haskell2010
diff --git a/src/Pipes/Lzma.hs b/src/Pipes/Lzma.hs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Lzma.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Pipes.Lzma
+    ( -- * Simple interface
+      compress
+    , decompress
+
+      -- * Compression
+    , compressWith
+      -- ** Parameters
+    , Lzma.defaultCompressParams
+    , Lzma.CompressParams
+    , Lzma.compressLevel
+    , Lzma.CompressionLevel(..)
+    , Lzma.compressLevelExtreme
+    , Lzma.IntegrityCheck(..)
+    , Lzma.compressIntegrityCheck
+
+      -- * Decompression
+    , decompressWith
+      -- ** Parameters
+    , Lzma.defaultDecompressParams
+    , Lzma.DecompressParams
+    , Lzma.decompressTellNoCheck
+    , Lzma.decompressTellUnsupportedCheck
+    , Lzma.decompressTellAnyCheck
+    , Lzma.decompressConcatenated
+    , Lzma.decompressAutoDecoder
+    , Lzma.decompressMemLimit
+    ) where
+
+import Pipes
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Codec.Compression.Lzma as Lzma
+
+-- | Decompress a 'ByteString'
+decompress :: forall m r. MonadIO m
+           => Producer ByteString m r
+           -> Producer ByteString m (Producer ByteString m r)
+decompress = decompressWith Lzma.defaultDecompressParams
+
+-- | Decompress a 'ByteString'.
+decompressWith :: forall m r. MonadIO m
+               => Lzma.DecompressParams
+               -> Producer ByteString m r
+               -> Producer ByteString m (Producer ByteString m r)
+decompressWith params prod0 = liftIO (Lzma.decompressIO params) >>= go prod0
+  where
+    go :: Producer ByteString m r
+       -> Lzma.DecompressStream IO
+       -> Producer ByteString m (Producer ByteString m r)
+    go prod s@(Lzma.DecompressInputRequired more) = do
+        mx <- lift $ next prod
+        case mx of
+          Right (x, prod')
+            | BS.null x    -> go prod' s
+            | otherwise    -> liftIO (more x) >>= go prod'
+          Left r           -> liftIO (more mempty) >>= go (return r)
+    go prod (Lzma.DecompressOutputAvailable output cont) = do
+        yield output
+        liftIO cont >>= go prod
+    go prod (Lzma.DecompressStreamEnd leftover) =
+        return (yield leftover >> prod)
+    go prod (Lzma.DecompressStreamError Lzma.LzmaRetOK) =
+        return prod
+    go _prod (Lzma.DecompressStreamError err) =
+        fail $ "Pipes.Lzma.decompress: Error "++show err
+
+-- | Compress a 'ByteString'
+compress :: forall m r. MonadIO m
+         => Producer ByteString m r
+         -> Producer ByteString m r
+compress = compressWith Lzma.defaultCompressParams
+
+-- | Compress a 'ByteString'
+compressWith :: forall m r. MonadIO m
+             => Lzma.CompressParams
+             -> Producer ByteString m r
+             -> Producer ByteString m r
+compressWith params prod0 = liftIO (Lzma.compressIO params) >>= go prod0
+  where
+    go :: Producer ByteString m r
+       -> Lzma.CompressStream IO
+       -> Producer ByteString m r
+    go prod s@(Lzma.CompressInputRequired _flush more) = do
+        mx <- lift $ next prod
+        case mx of
+          Right (x, prod')
+            | BS.null x    -> go prod' s
+            | otherwise    -> liftIO (more x) >>= go prod'
+          Left r           -> liftIO (more mempty) >>= go (return r)
+    go prod (Lzma.CompressOutputAvailable output cont) = do
+        yield output
+        liftIO cont >>= go prod
+    go prod Lzma.CompressStreamEnd =
+        prod
