diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Author name here (c) 2022
+
+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 Author name here 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# jsonl-conduit
+
+[![Build Status](https://travis-ci.org/unfoldml/jsonl-conduit.png)](https://travis-ci.org/unfoldml/jsonl-conduit)
+
+TODO Description.
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/jsonl-conduit.cabal b/jsonl-conduit.cabal
new file mode 100644
--- /dev/null
+++ b/jsonl-conduit.cabal
@@ -0,0 +1,47 @@
+name:                jsonl-conduit
+version:             0.1.0.0
+synopsis:            Conduit interface to JSONL-encoded data
+description:         Streaming interface to JSONL-encoded files and bytestrings
+homepage:            https://github.com/unfoldml/jsonl-conduit
+license:             BSD3
+license-file:        LICENSE
+author:              Marco Zocca
+maintainer:          UnfoldML AB
+copyright:           (c) 2022 UnfoldML AB, Marco Zocca
+category:            JSON, Web, Text
+build-type:          Simple
+extra-source-files:  README.md
+                     records
+cabal-version:       >=1.10
+tested-with:         GHC == 9.0.2
+
+library
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  hs-source-dirs:      src
+  exposed-modules:     JSONL.Conduit
+  build-depends:       base >= 4.7 && < 5
+                     , aeson
+                     , bytestring
+                     , conduit
+                     , jsonl >= 0.2
+                     -- debug
+                     , hspec
+
+test-suite spec
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , aeson
+                     , bytestring
+                     , conduit
+                     , jsonl-conduit
+                     , hspec
+
+
+source-repository head
+  type:     git
+  location: https://github.com/unfoldml/jsonl-conduit
diff --git a/records b/records
new file mode 100644
--- /dev/null
+++ b/records
@@ -0,0 +1,4 @@
+["Gilbert","2013",24,true]
+["Alexa","2013",29,true]
+["May","2012B",14,false]
+["Deloise","2012A",19,true]
diff --git a/src/JSONL/Conduit.hs b/src/JSONL/Conduit.hs
new file mode 100644
--- /dev/null
+++ b/src/JSONL/Conduit.hs
@@ -0,0 +1,110 @@
+{-# LANGUAGE TupleSections #-}
+{-# options_ghc -Wno-unused-imports #-}
+module JSONL.Conduit (
+  -- * Encode
+  jsonToLBSC
+  -- ** I/O
+  , sinkFileC
+  -- * Decode
+  , jsonFromLBSC
+  -- ** I/O
+  , sourceFileC
+  ) where
+
+import Data.Void (Void)
+
+import Control.Monad.IO.Class (MonadIO(..))
+
+  -- aeson
+import Data.Aeson (ToJSON(..), FromJSON(..), eitherDecode' )
+-- bytestring
+import qualified Data.ByteString as BS (ByteString, null)
+import qualified Data.ByteString.Builder as BBS (toLazyByteString, Builder)
+import qualified Data.ByteString.Internal as BS (c2w)
+import qualified Data.ByteString.Char8 as BS8 (span, drop, putStrLn, putStr)
+import qualified Data.ByteString.Lazy as LBS (ByteString, drop, span, toStrict, fromStrict)
+-- conduit
+import qualified Conduit as C (ConduitT, runConduit, sourceFile, sinkFile, await, yield, mapC, unfoldC, foldMapC, foldlC, printC)
+import Conduit ( (.|) , MonadResource)
+-- jsonl
+import JSONL (jsonLine)
+
+-- | Render a stream of JSON-encodable objects into a `LBS.ByteString`
+jsonToLBSC :: (ToJSON a, Monad m) => C.ConduitT a o m LBS.ByteString
+jsonToLBSC = BBS.toLazyByteString <$> jsonToBuilderC
+
+-- | Render a stream of JSON-encodable objects into a `BSB.Builder`
+jsonToBuilderC :: (ToJSON a, Monad m) => C.ConduitT a o m BBS.Builder
+jsonToBuilderC = C.foldMapC jsonLine
+
+-- | Render a stream of JSON-encodable objects into a JSONL file
+sinkFileC :: (ToJSON a, MonadResource m) =>
+             FilePath -- ^ path of JSONL file to be created
+          -> C.ConduitT a o m ()
+sinkFileC fpath = C.mapC (LBS.toStrict . BBS.toLazyByteString . jsonLine) .|
+                  C.sinkFile fpath
+
+-- | Read a JSONL file and stream the decoded records
+--
+-- NB : ignores any parsing errors and returns 
+sourceFileC :: (MonadResource m, FromJSON a) =>
+               FilePath -- ^ path of JSONL file to be read
+            -> C.ConduitT () a m ()
+sourceFileC fpath = C.sourceFile fpath .|
+                    parseChunk
+
+parseChunk :: (Monad m, FromJSON a) => C.ConduitT BS.ByteString a m ()
+parseChunk = go mempty
+  where
+    go acc =
+      if not (BS.null acc) -- buffer is non empty
+      then
+        case chopDecode acc of
+          Left _ -> pure ()
+          Right (y, srest) -> do
+            C.yield y
+            go srest
+      else do
+        mc <- C.await -- get data from upstream
+        case mc of
+          Nothing -> pure ()
+          Just x -> do
+            let
+              acc' = acc <> x
+            case chopDecode acc' of
+              Left _ -> pure ()
+              Right (y, srest) -> do
+                C.yield y
+                go srest
+
+chopDecode :: FromJSON a =>
+              BS.ByteString -> Either String (a, BS.ByteString)
+chopDecode acc = (, srest) <$> eitherDecode' (LBS.fromStrict s)
+  where
+    (s, srest) = chopBS8 acc
+
+
+
+-- | Source a `LBS.ByteString` for JSONL records
+jsonFromLBSC :: (FromJSON a, Monad m) => LBS.ByteString -> C.ConduitT Void a m ()
+jsonFromLBSC = C.unfoldC mk
+  where
+    mk lbs = case eitherDecode' s of
+      Right x -> Just (x, srest)
+      Left _ -> Nothing
+      where
+        (s, srest) = chopLBS lbs -- LBS.span (== BS.c2w '\n') lbs
+
+
+-- * utilities
+
+-- | 'span' for a strict bytestring encoding a JSONL record
+chopBS8 :: BS.ByteString -> (BS.ByteString, BS.ByteString)
+chopBS8 lbs = (s, BS8.drop 1 srest)
+  where (s, srest) = BS8.span (/= '\n') lbs
+
+-- | 'span' for a lazy bytestring encoding a JSONL record
+chopLBS :: LBS.ByteString -> (LBS.ByteString, LBS.ByteString)
+chopLBS lbs = (s, LBS.drop 1 srest)
+  where (s, srest) = LBS.span (/= BS.c2w '\n') lbs
+
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
