diff --git a/LICENCE b/LICENCE
new file mode 100644
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,31 @@
+Copyright (c) 2017-2018, Commonwealth Scientific and Industrial Research Organisation
+(CSIRO) ABN 41 687 119 230.
+
+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 QFPL 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/changelog.md b/changelog.md
new file mode 100644
--- /dev/null
+++ b/changelog.md
@@ -0,0 +1,5 @@
+# Revision history for sv-cassava
+
+## 0.1  -- 2018-03-14
+
+* First version. Released on an unsuspecting world.
diff --git a/src/Data/Sv/Cassava.hs b/src/Data/Sv/Cassava.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Sv/Cassava.hs
@@ -0,0 +1,73 @@
+{-|
+Module      : Data.Sv.Cassava
+Copyright   : (C) CSIRO 2017-2018
+License     : BSD3
+Maintainer  : George Wilson <george.wilson@data61.csiro.au>
+Stability   : experimental
+Portability : non-portable
+
+This module provides integration between sv and cassava.
+It lets you plug cassava's (very fast!) parser into sv's decoding layer.
+
+Our benchmarking indicates that parsing is very expensive,
+while decoding is comparatively less performance-sensitive.
+So if performance matters to you, and cassava's parser is sufficient for
+your needs, you might as well use it!
+
+sv's own parser is much slower than cassava's right now, but aims to have
+better error messages and keep more information for you to work with, such as
+spacing, quoting, and newline information.
+-}
+
+module Data.Sv.Cassava (
+  parseDecodeFromCassava
+, parseCassava
+, decodeFromCassava
+) where
+
+import Data.Attoparsec.ByteString (parseOnly)
+import Data.ByteString (ByteString)
+import Data.ByteString.UTF8 as UTF8
+import qualified Data.Csv as Cassava
+import qualified Data.Csv.Parser as Cassava
+import Data.Maybe (mapMaybe)
+import Data.Sv
+import qualified Data.Sv.Decode as D
+import Data.Vector (Vector, (!?))
+import Data.Vector.NonEmpty (NonEmptyVector (NonEmptyVector))
+import qualified Data.Vector as V
+import Text.Escape (Unescaped (Unescaped))
+import Text.Space (unspaced)
+import Text.Quote (Quote (DoubleQuote))
+
+-- | Use an sv 'Decode' to decode from cassava's 'Cassava.Csv' type.
+decodeFromCassava :: Decode' ByteString a -> Cassava.Csv -> DecodeValidation ByteString [a]
+decodeFromCassava d =
+  traverse (D.promote d) . fs2r . V.toList
+    where
+      fs2r :: [Vector Cassava.Field] -> [Record ByteString]
+      fs2r = mapMaybe (fmap (Record . fmap (unspaced . Quoted DoubleQuote . Unescaped)) . vec2nev)
+      vec2nev :: Vector b -> Maybe (NonEmptyVector b)
+      vec2nev v = NonEmptyVector <$> v !? 0 <*> pure (V.drop 1 v)
+
+-- | Parse a 'Cassava.Csv' from a 'ByteString' using cassava's parser
+--
+-- This returns its result in a 'DecodeValidation', so that it's compatible
+-- with the rest of sv.
+parseCassava :: Cassava.DecodeOptions -> ByteString -> DecodeValidation ByteString Cassava.Csv
+parseCassava opts =
+  D.validateEither' (BadParse . UTF8.fromString) . parseOnly (Cassava.csv opts)
+
+-- | Parse a 'Cassava.Csv' from a 'ByteString' using cassava's parser, then
+-- decode it using the given 'Decode'.
+--
+-- This has the benefit of letting you use cassava's parser, which is very fast,
+-- with sv's decoding.
+parseDecodeFromCassava :: Decode' ByteString a -> Headedness -> Cassava.DecodeOptions -> ByteString -> DecodeValidation ByteString [a]
+parseDecodeFromCassava d h opts bs =
+  (chompFirst h <$> parseCassava opts bs) `bindValidation` decodeFromCassava d
+    where
+      -- The csv returned from cassava's parser may include a header row.
+      -- If it does, we want to skip that row.
+      chompFirst Headed   = V.drop 1
+      chompFirst Unheaded = id
diff --git a/sv-cassava.cabal b/sv-cassava.cabal
new file mode 100644
--- /dev/null
+++ b/sv-cassava.cabal
@@ -0,0 +1,58 @@
+-- Initial sv-cassava.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                sv-cassava
+version:             0.1
+synopsis:            Integration to use sv with cassava's parser
+description:
+  This package provides functions allowing you to use cassava's parser
+  together with sv's decoding. You might prefer this to using sv's own
+  parser if speed is a significant concern.
+homepage:            https://github.com/qfpl/sv
+license:             BSD3
+license-file:        LICENCE
+author:              George Wilson
+maintainer:          george@wils.online
+copyright:           Copyright (c) 2017-2018, Commonwealth Scientific and Industrial Research Organisation (CSIRO) ABN 41 687 119 230.
+category:            CSV, Text, Web
+build-type:          Simple
+extra-source-files:  changelog.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:
+    Data.Sv.Cassava
+  build-depends:
+    attoparsec >= 0.12.1.4 && < 0.14
+    , base >=4.8 && <5
+    , bytestring >= 0.9.1.10 && < 0.11
+    , cassava >= 0.4.1 && < 0.6
+    , sv >= 0.1 && < 0.2
+    , utf8-string >= 1 && < 1.1
+    , vector >= 0.10 && < 0.13
+  hs-source-dirs:
+    src
+  default-language:
+    Haskell2010
+
+test-suite hunit
+  type:
+    exitcode-stdio-1.0
+  main-is:
+    tests.hs
+  default-language:
+    Haskell2010
+  build-depends:
+    base >=4.8 && <5
+    , bytestring >= 0.9.1.10 && < 0.11
+    , cassava >= 0.4.1 && < 0.6
+    , sv >= 0.1 && < 0.2
+    , sv-cassava
+    , HUnit >= 1.4 && < 1.7
+    , text >= 1.0 && < 1.3
+    , validation >= 1 && < 1.1
+    , vector >= 0.10 && < 0.13
+  ghc-options:
+    -Wall
+  hs-source-dirs:
+    tests
diff --git a/tests/tests.hs b/tests/tests.hs
new file mode 100644
--- /dev/null
+++ b/tests/tests.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main (main) where
+
+import Control.Monad (when)
+import Data.ByteString (ByteString)
+import qualified Data.Csv as Cassava
+import Data.Sv
+import Data.Sv.Cassava
+import qualified Data.Sv.Decode as D
+import Data.Text (Text)
+import qualified Data.Vector as V
+import System.Exit (exitFailure)
+import Test.HUnit
+
+data TestRow = TestRow Int Double Text deriving (Eq, Show)
+
+testRowDec :: Decode' ByteString TestRow
+testRowDec = TestRow <$> D.int <*> D.double <*> D.utf8
+
+bs1 :: ByteString
+bs1 = mconcat
+  [ "1,5.5,hello\n"
+  , "2,-3.54,\n"
+  , "3,42.0,\"Text in quotes\"\n"
+  ]
+
+bs2 :: ByteString
+bs2 = hdr `mappend` bs1
+  where
+    hdr = "id, number, name\n"
+
+csv1 :: Cassava.Csv
+csv1 = V.fromList . fmap V.fromList $
+  [ ["1","5.5","hello"]
+  , ["2","-3.54", ""]
+  , ["3", "42.0", "Text in quotes"]
+  ]
+
+decoded1 :: [TestRow]
+decoded1 =
+  [ TestRow 1 5.5 "hello"
+  , TestRow 2 (-3.54) ""
+  , TestRow 3 42 "Text in quotes"
+  ]
+
+parseTest :: Test
+parseTest = TestLabel "parse" . TestCase $
+  parseCassava Cassava.defaultDecodeOptions bs1 @?= pure csv1
+
+decodeTest :: Test
+decodeTest = TestLabel "decode" . TestCase $
+  decodeFromCassava testRowDec csv1 @?= pure decoded1
+
+parseDecodeTest :: Test
+parseDecodeTest = TestLabel "parse+decode" . TestList $
+  [ TestLabel "unheaded" . TestCase $
+      parseDecodeFromCassava testRowDec Unheaded Cassava.defaultDecodeOptions bs1 @?=
+        pure decoded1
+  , TestLabel "headed" . TestCase $
+      parseDecodeFromCassava testRowDec Headed Cassava.defaultDecodeOptions bs2 @?=
+        pure decoded1
+  ]
+
+tests :: Test
+tests = TestList [parseTest, decodeTest, parseDecodeTest]
+
+main :: IO ()
+main = do
+  cs <- runTestTT tests
+  when (errors cs > 0 || failures cs > 0)
+    exitFailure
