diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2013, Oliver Charles
+
+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 Oliver Charles 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/json-assertions.cabal b/json-assertions.cabal
new file mode 100644
--- /dev/null
+++ b/json-assertions.cabal
@@ -0,0 +1,26 @@
+name: json-assertions
+version: 1.0.0
+synopsis: Test that your (Aeson) JSON encoding matches your expectations
+homepage: http://github.com/ocharles/json-assertions.git
+license: BSD3
+license-file: LICENSE
+author: Oliver Charles
+maintainer: ollie@ocharles.org.uk
+copyright: Oliver Charles (c) 2013
+category: Testing
+build-type: Simple
+cabal-version: >=1.10
+
+library
+  exposed-modules:     Test.JSON.Assertions
+  build-depends:
+    aeson >= 0.6,
+    base >= 4.5 && < 5,
+    indexed >= 0.1,
+    indexed-free >= 0.3,
+    lens >= 3.10,
+    lens-aeson >= 0.1.2,
+    text >= 0.11.3.1
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options: -O2 -Wall
diff --git a/src/Test/JSON/Assertions.hs b/src/Test/JSON/Assertions.hs
new file mode 100644
--- /dev/null
+++ b/src/Test/JSON/Assertions.hs
@@ -0,0 +1,175 @@
+{-# LANGUAGE GADTs #-}
+
+{-|
+@json-assertions@ is a library for validating that your JSON encoding matches 
+what you are actually expecting. It does this by providing you with a DSL to 
+traverse a JSON document at the same time as you traverse the value that was 
+encoded. As you traverse the JSON document, you are building up assertions (by 
+asserting that you expect certain keys and array indices to exist), and you can 
+also add your own assertions to check the contents of object properties.
+
+'JSONTest' is an indexed monad, so you will need to enable @RebindableSyntax@ 
+and bring indexed monadic bind into scope:
+
+> {-# LANGUAGE RebindableSyntax #-}
+> import Prelude hiding (Monad(..))
+> import Control.Monad.Indexed ((>>>=), ireturn)
+> import Test.JSON.Assertions
+> import Data.Aeson
+>
+> return :: a -> JSONTest i i a
+> return = ireturn
+>
+> (>>=) :: m i j a -> (a -> m j k b) -> m i k b
+> (>>=) = (>>>=)
+
+You can now write tests as an action in the 'JSONTest' monad. The first index 
+is the type of the object you wish to encode, and the second parameter is the 
+type that the test ends in. For example, consider the following:
+
+> data Person = Person { personName :: String }
+> instance ToJSON Person where
+>   toJSON p = object [ "name" .= personName p ]
+
+We can write a test to check that the JSON encoding of a @Person@'s name is
+correct:
+
+> personTest :: JSONTest Person String String
+> personTest = do
+>   expectedName <- key "name"
+>   assertEq expectedName
+
+For more information, you may wish to read <http://ocharles.org.uk/blog/posts/2013-11-24-using-indexed-free-monads-to-quickcheck-json.html>.
+
+-}
+
+module Test.JSON.Assertions
+    ( -- * Tests and Traversals
+      key
+    , nth
+    , assertEq
+    , stop
+    , jsonTest
+
+      -- * Test Interpreters
+    , testJSON
+
+    , JSONTest
+    ) where
+
+import Control.Monad.Indexed (IxFunctor(..), (>>>=))
+import Control.MonadPlus.Indexed.Free (IxFree(..))
+import Data.Monoid (First)
+
+import qualified Control.Lens as Lens
+import qualified Control.Lens.Aeson as Aeson
+import qualified Data.Aeson as Aeson
+import qualified Data.Text as Text
+
+--------------------------------------------------------------------------------
+data JSONF i j a where
+  Key :: String -> (i -> j) -> (j -> a) -> JSONF i j a
+  Index :: Int -> (i -> j) -> (j -> a) -> JSONF i j a
+  Assert :: (Aeson.Value -> Either String ()) -> a -> JSONF i i a
+  Stop :: JSONF i () a
+
+instance IxFunctor JSONF where
+  imap g (Key keyS f k) = Key keyS f (g . k)
+  imap g (Index n f k) = Index n f (g . k)
+  imap f (Assert p k) = Assert p (f k)
+  imap _ Stop = Stop
+
+
+--------------------------------------------------------------------------------
+type JSONTest = IxFree JSONF
+
+
+--------------------------------------------------------------------------------
+-- | Traverse into the value underneath a specific key in the JSON structure. 
+-- The return value is the value inside the Haskell value - that is, the result 
+-- applying the associated morphism.
+key :: String   -- ^ JSON Key
+    -> (i -> j) -- ^ An associated morphism into a substructure of the test environment
+    -> JSONTest i j j
+key k f = Free (Key k f Pure)
+
+
+--------------------------------------------------------------------------------
+-- | Traverse the specific index of a JSON array.
+-- The return value is the value inside the Haskell value - that is, the result 
+-- applying the associated morphism.
+nth :: Int      -- ^ JSON array index
+    -> (i -> j) -- ^ An associated morphism into a substructure of the test environment
+    -> JSONTest i j j
+nth i f = Free (Index i f Pure)
+
+
+--------------------------------------------------------------------------------
+-- | Assert that the current JSON value is exactly equal to the result of
+-- calling 'Aeson.toJSON' on a value.
+assertEq :: Aeson.ToJSON a => a -> JSONTest i i ()
+assertEq expected =
+  let expectedJSON = Aeson.toJSON expected
+      p actual | actual == expectedJSON = Right ()
+               | otherwise = Left $ unlines
+                               [ "Expected: " ++ show expectedJSON
+                               , "     Got: " ++ show actual
+                               ]
+  in Free (Assert p (Pure ()))
+
+
+--------------------------------------------------------------------------------
+-- | Using 'stop' discards the indices in the monad, which can help when you
+-- need to 'isum' multiple tests that end in different states.
+stop :: JSONTest a () r
+stop = Free Stop
+
+
+--------------------------------------------------------------------------------
+-- | Finalize a 'JSONTest' by calling 'stop' at the end.
+jsonTest :: JSONTest i j a -> JSONTest i () a
+jsonTest = (>>>= const stop)
+
+--------------------------------------------------------------------------------
+-- | Run a 'JSONTest' against a Haskell value that can be encoded to JSON. 
+-- Returns a list of strings describing the failed assertions, or the empty list
+-- if all assertions were satisfied.
+testJSON :: Aeson.ToJSON i => JSONTest i j a -> i -> [String]
+testJSON tests env = go tests (Aeson.toJSON env) env "subject"
+ 
+ where
+
+  go :: JSONTest i j a -> Aeson.Value -> i -> String -> [String]
+ 
+  go (Pure _) _ _ _ = []
+ 
+  go (Free (Key keyS f k)) actual expected descr =
+    tryLens (Aeson.key (Text.pack keyS)) f actual expected k $
+      descr ++ "[\"" ++ keyS ++ "\"]"
+
+  go (Free (Index n f k)) actual expected descr =
+    tryLens (Aeson.nth n) f actual expected k $
+      descr ++ " failed to match any targets"
+
+  go (Free (Assert p k)) actual expected descr =
+    either
+      (return . ((descr ++ " failed assertion\n") ++))
+      (const $ go k actual expected descr)
+      (p actual)
+
+  go (Free Stop) _ _ _ = []
+
+  go (Plus steps) actual expected descr =
+    concatMap (\s -> go s actual expected descr) steps
+
+  tryLens :: Lens.Getting (First Aeson.Value) Aeson.Value Aeson.Value
+          -> (i -> j) -> Aeson.Value
+          -> i -> (j -> JSONTest j k a)
+          -> String
+          -> [String]
+
+  tryLens l f actual expected k path =
+    case Lens.preview l actual of
+      Nothing -> [path ++ " failed to match any targets"]
+      Just matched ->
+        go (k (f expected)) matched (f expected) path
