diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2019, Obsidian Systems LLC
+
+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 Obsidian Systems LLC 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/aeson-gadt-th.cabal b/aeson-gadt-th.cabal
new file mode 100644
--- /dev/null
+++ b/aeson-gadt-th.cabal
@@ -0,0 +1,25 @@
+cabal-version: >=1.10
+name: aeson-gadt-th
+version: 0.1.0.0
+synopsis: Derivation of Aeson instances for GADTs
+category: JSON
+description: Template Haskell for generating ToJSON and FromJSON instances for GADTs
+license: BSD3
+license-file: LICENSE
+author: Obsidian Systems LLC
+maintainer: maintainer@obsidian.systems
+copyright: 2019 Obsidian Systems LLC
+build-type: Simple
+
+library
+  exposed-modules: Data.Aeson.GADT.TH
+  build-depends: base >= 4.8 && < 4.13
+               , aeson
+               , dependent-sum
+               , template-haskell
+  hs-source-dirs: src
+  default-language: Haskell2010
+
+source-repository head
+  type:     git
+  location: git://github.com/obsidiansystems/aeson-gadt-th.git
diff --git a/src/Data/Aeson/GADT/TH.hs b/src/Data/Aeson/GADT/TH.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Aeson/GADT/TH.hs
@@ -0,0 +1,142 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PatternGuards #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE StandaloneDeriving #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
+
+module Data.Aeson.GADT.TH where
+
+import Control.Monad
+import Data.Aeson
+import Data.Dependent.Sum
+import Data.Functor.Classes
+import Data.Some (Some (..))
+import Language.Haskell.TH
+
+-- | Derive 'ToJSON' and 'FromJSON' instances for the name GADT
+deriveJSONGADT :: Name -> DecsQ
+deriveJSONGADT n = do
+  tj <- deriveToJSONGADT n
+  fj <- deriveFromJSONGADT n
+  return (tj ++ fj)
+
+decCons :: Dec -> [Con]
+decCons = \case
+  DataD _ _ _ _ cs _ -> cs
+  NewtypeD _ _ _ _ c _ -> [c]
+  _ -> error "undefined"
+
+conName :: Con -> Name
+conName c = case c of
+  NormalC n _ -> n
+  RecC n _ -> n
+  InfixC _ n _ -> n
+  ForallC _ _ c' -> conName c'
+  GadtC [n] _ _ -> n
+  RecGadtC [n] _ _ -> n
+  _ -> error "conName: GADT constructors with multiple names not yet supported"
+
+conArity :: Con -> Int
+conArity c = case c of
+  NormalC _ ts -> length ts
+  RecC _ ts -> length ts
+  InfixC _ _ _ -> 2
+  ForallC _ _ c' -> conArity c'
+  GadtC _ ts _ -> length ts
+  RecGadtC _ ts _ -> length ts
+
+{-# DEPRECATED deriveGADTInstances "Use deriveJSONGADT instead" #-}
+deriveGADTInstances :: Name -> DecsQ
+deriveGADTInstances = deriveJSONGADT
+
+deriveToJSONGADT :: Name -> DecsQ
+deriveToJSONGADT n = do
+  x <- reify n
+  let cons = case x of
+       TyConI d -> decCons d
+       _ -> error "undefined"
+  [d|
+    instance ToJSON ($(conT n) a) where
+      toJSON r = $(caseE [|r|] $ map conMatchesToJSON cons)
+    |]
+
+deriveFromJSONGADT :: Name -> DecsQ
+deriveFromJSONGADT n = do
+  x <- reify n
+  let cons = case x of
+       TyConI d -> decCons d
+       _ -> error "undefined"
+  let wild = match wildP (normalB [|fail "deriveFromJSONGADT: Supposedly-complete GADT pattern match fell through in generated code. This shouldn't happen."|]) []
+  [d|
+    instance FromJSON (Some $(conT n)) where
+      parseJSON v = do
+        (tag', v') <- parseJSON v
+        $(caseE [|tag' :: String|] $ map (conMatchesParseJSON [|v'|]) cons ++ [wild])
+    |]
+
+-- | Generate all required matches (and some redundant ones...) for `eqTagged`
+-- for some constructor
+conMatchesEqTagged :: Con -> [MatchQ]
+conMatchesEqTagged c = case c of
+    ForallC _ _ c' -> conMatchesEqTagged c'
+    GadtC _ tys _ -> forTypes (map snd tys)
+    _ -> error "conMatchesEqTagged: Unmatched constructor type"
+  where
+    name = conName c
+    forTypes ts =
+      [ do
+          as <- mapM (\_ -> newName "a") ts
+          bs <- mapM (\_ -> newName "b") ts
+          x <- newName "x"
+          y <- newName "y"
+          let compareTagFields = foldr (\(a, b) e -> [| $(varE a) == $(varE b) && $(e) |]) [| True |] (zip as bs)
+          match
+            (tupP [conP name (map varP as), conP name (map varP bs)])
+            (normalB (lamE [varP x, varP y] [| $(compareTagFields) && eq1 $(varE x) $(varE y) |] ))
+            []
+      , match
+          (tupP [conP name (map (const wildP) ts), wildP])
+          (normalB [| \ _ _ -> False |])
+          []
+      ]
+
+-- | Implementation of 'toJSON'
+conMatchesToJSON :: Con -> MatchQ
+conMatchesToJSON c = do
+  let name = conName c
+      base = nameBase name
+      toJSONExp e = [| toJSON $(e) |]
+  vars <- replicateM (conArity c) (newName "x")
+  let body = toJSONExp $ tupE [ [| base :: String |] , tupE $ map (toJSONExp . varE) vars ]
+  match (conP name (map varP vars)) (normalB body) []
+
+
+-- | Implementation of 'parseJSON'
+conMatchesParseJSON :: ExpQ -> Con -> MatchQ
+conMatchesParseJSON e c = do
+  let name = conName c
+      match' = match (litP (StringL (nameBase name)))
+  vars <- replicateM (conArity c) (newName "x")
+  let forTypes _ = do
+        let pat = tupP (map varP vars)
+            conApp = foldl appE (conE name) (map varE vars)
+            body = doE [ bindS pat [| parseJSON $e |]
+                       , noBindS [| return (This $conApp) |]
+                       ]
+        match' (normalB body) []
+  case c of
+    ForallC _ _ c' -> conMatchesParseJSON e c'
+    GadtC _ tys _ -> forTypes (map snd tys)
+    NormalC _ tys -> forTypes (map snd tys)
+    _ -> error "conMatchesParseJSON: Unmatched constructor type"
