diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, Silk
+
+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 Silk 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-schema.cabal b/json-schema.cabal
new file mode 100644
--- /dev/null
+++ b/json-schema.cabal
@@ -0,0 +1,31 @@
+Name:                json-schema
+Version:             0.4
+Synopsis:            Types and type classes for defining JSON schemas.
+Description:         Types and type classes for defining JSON schemas.
+License:             BSD3
+License-file:        LICENSE
+Author:              Silk
+Maintainer:          code@silk.co
+Category:            Data
+Build-type:          Simple
+Cabal-version:       >=1.6
+
+Library
+  Hs-Source-Dirs:    src
+  Exposed-Modules:   Data.JSON.Schema
+                   , Data.JSON.Schema.Combinators
+                   , Data.JSON.Schema.Generic
+                   , Data.JSON.Schema.Types
+  Build-Depends:     base == 4.*
+                   , aeson >= 0.6 && < 0.8
+                   , containers >= 0.3 && < 0.6
+                   , generic-aeson == 0.1.*
+                   , generic-deriving == 1.6.*
+                   , tagged >= 0.2 && < 0.8
+                   -- No bounds, since we only use the Text type.
+                   , text
+  Ghc-Options:       -Wall
+
+Source-repository head
+  Type:              Git
+  Location:          https://github.com/silkapp/json-schema.git
diff --git a/src/Data/JSON/Schema.hs b/src/Data/JSON/Schema.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JSON/Schema.hs
@@ -0,0 +1,9 @@
+module Data.JSON.Schema
+  ( module Data.JSON.Schema.Generic
+  , module Data.JSON.Schema.Types
+  , Proxy (..)
+  ) where
+
+import Data.JSON.Schema.Generic
+import Data.JSON.Schema.Types
+import Data.Proxy
diff --git a/src/Data/JSON/Schema/Combinators.hs b/src/Data/JSON/Schema/Combinators.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JSON/Schema/Combinators.hs
@@ -0,0 +1,58 @@
+-- | Combinators for creating JSON schemas.
+module Data.JSON.Schema.Combinators where
+
+import Data.JSON.Schema.Types
+
+-- | A schema combinator.
+type SchemaC = Schema -> Schema
+
+-- | Optionality operator for schemas.
+infixl 3 <|>
+(<|>) :: Schema -> Schema -> Schema
+(Choice s1) <|> (Choice s2) = Choice $ s1 ++ s2
+(Choice s1) <|> v           = Choice $ s1 ++ [v]
+v           <|> (Choice s2) = Choice $ v : s2
+v1          <|> v2          = Choice $ [v1, v2]
+
+-- | Tupling.
+infixl 4 <+>
+(<+>) :: Schema -> Schema -> Schema
+(Tuple t1) <+> (Tuple t2) = Tuple $ t1 ++ t2
+(Tuple t1) <+> v          = Tuple $ t1 ++ [v]
+v          <+> (Tuple t2) = Tuple $ v : t2
+v1         <+> v2         = Tuple $ [v1, v2]
+
+-- | If passed two objects, merges the fields. Otherwise creates a
+-- tuple.
+merge :: Schema -> Schema -> Schema
+merge (Object f1) (Object f2) = Object $ f1 ++ f2
+merge v1          v2          = v1 <+> v2
+
+-- | Create an object with a single field.
+field :: String -> Bool -> Schema -> Schema
+field k r v = Object [Field k r v]
+
+-- | An unbounded string.
+value :: Schema
+value = Value 0 (-1)
+
+-- | An unbounded number.
+number :: Schema
+number = Number 0 (-1)
+
+-- | An unbounded array with non-unique values.
+array :: Schema -> Schema
+array = Array 0 (-1) False
+
+-- | Add a field to an object, or tuple if passed a non-object.
+addField :: String -> Bool -> Schema -> SchemaC
+addField k r v = merge $ field k r v
+
+-- | Add multiple fields to an object, or tuple if passed a
+-- non-object.
+addFields :: [(String, Bool, Schema)] -> SchemaC
+addFields = flip $ foldr (\(k, r, v) -> addField k r v)
+
+-- | An empty object.
+empty :: Schema
+empty = Object []
diff --git a/src/Data/JSON/Schema/Generic.hs b/src/Data/JSON/Schema/Generic.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JSON/Schema/Generic.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE TypeOperators
+           , TupleSections
+           , ScopedTypeVariables
+           , FlexibleContexts
+           , FlexibleInstances
+           , OverlappingInstances
+           , TypeFamilies
+           #-}
+-- | Generic derivation of schemas. The schemas generated match the
+-- JSON generated by type 'generic-aeson' package. See that package
+-- for documentation on the format and examples of it.
+module Data.JSON.Schema.Generic (gSchema) where
+
+import Control.Applicative hiding (empty, (<|>))
+import Data.Char
+import Data.JSON.Schema.Combinators
+import Data.JSON.Schema.Types
+import Data.Maybe
+import Data.Proxy
+import GHC.Generics
+import Generics.Deriving.ConNames
+import Generics.Generic.IsEnum
+
+class GJSONSCHEMA f where
+  gSchema' :: Bool -> Bool -> Proxy (f a) -> Schema
+
+-- Recursive positions disabled for now, it causes infintite data structures. This is a problem to be solved!
+{-
+instance GJSONSCHEMA I where
+  gSchema' _ f = f . fmap unI
+-}
+
+instance JSONSchema c => GJSONSCHEMA (K1 i c) where
+  gSchema' _ _ = schema . fmap unK1
+
+instance GJSONSCHEMA (K1 i String) where
+  gSchema' _ _ _ = Value 0 (-1)
+
+instance GJSONSCHEMA U1 where
+  gSchema' _ _ _ = empty
+
+instance (GJSONSCHEMA f, GJSONSCHEMA g) => GJSONSCHEMA (f :+: g) where
+  gSchema' mc enm p =
+        gSchema' mc enm (gL <$> p)
+    <|> gSchema' mc enm (gR <$> p)
+    where
+      gL :: (f :+: g) r -> f r
+      gL _ = undefined
+      gR :: (f :+: g) r -> g r
+      gR _ = undefined
+
+gFst :: (f :*: g) r -> f r
+gFst (f :*: _) = f
+
+gSnd :: (f :*: g) r -> g r
+gSnd (_ :*: g) = g
+
+instance (GJSONSCHEMA f, GJSONSCHEMA g) => GJSONSCHEMA (f :*: g) where
+  gSchema' mc enm p = gSchema' mc enm (gFst <$> p) `merge` gSchema' mc enm (gSnd <$> p)
+
+instance (Constructor c, GJSONSCHEMA f) => GJSONSCHEMA (M1 C c f) where
+  gSchema' _  True = const $ Value 0 (-1)
+  gSchema' mc enm = wrap . gSchema' mc enm . fmap unM1
+    where
+      wrap = if mc
+             then field (firstLetterToLower $ conName (undefined :: M1 C c f p)) True
+             else id
+
+instance GJSONSCHEMA f => GJSONSCHEMA (M1 D c f) where
+  gSchema' mc enm = gSchema' mc enm . fmap unM1
+
+firstLetterToLower :: String -> String
+firstLetterToLower ""     = ""
+firstLetterToLower (l:ls) = toLower l : ls
+
+instance (Selector c, JSONSchema a) => GJSONSCHEMA (M1 S c (K1 i (Maybe a))) where
+  gSchema' _ _ = field (selName (undefined :: M1 S c f p)) False . schema . fmap (fromJust . unK1 . unM1)
+
+instance Selector c => GJSONSCHEMA (M1 S c (K1 i (Maybe String))) where
+  gSchema' _ _ _ = field (selName (undefined :: M1 S c f p)) False $ Value 0 (-1)
+
+instance (Selector c, GJSONSCHEMA f) => GJSONSCHEMA (M1 S c f) where
+  gSchema' mc enm = field (selName (undefined :: M1 S c f p)) True . gSchema' mc enm . fmap unM1
+
+multipleConstructors :: (Generic a, ConNames (Rep a)) => Proxy a -> Bool
+multipleConstructors = (> 1) . length . conNames . pv
+  where pv :: Proxy a -> a
+        pv _ = undefined
+
+-- | Derive a JSON schema for types with an instance of 'Generic'.
+gSchema :: (Generic a, GJSONSCHEMA (Rep a), ConNames (Rep a), GIsEnum (Rep a)) => Proxy a -> Schema
+gSchema p = gSchema' (multipleConstructors p) (isEnum p) (fmap from p)
diff --git a/src/Data/JSON/Schema/Types.hs b/src/Data/JSON/Schema/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/JSON/Schema/Types.hs
@@ -0,0 +1,62 @@
+{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
+-- | Types for defining JSON schemas.
+module Data.JSON.Schema.Types where
+
+import Data.Maybe
+import Data.Proxy
+import Data.Text (Text)
+import Data.Word (Word32)
+
+-- | A schema is any JSON value.
+type Schema = Value
+
+-- | A schema for a JSON value.
+data Value =
+    Choice [Value] -- ^ A choice of multiple values, e.g. for sum types.
+  | Object [Field] -- ^ A JSON object.
+  | Array Int Int Bool Value -- ^ An array. The integers represent the
+                             -- lower and upper bound of the array
+                             -- size. The value -1 indicates no bound.
+                             -- The boolean denotes whether items have
+                             -- to unique.
+  | Tuple [Value]   -- ^ A fixed-length tuple of different values.
+  | Value Int Int   -- ^ A string. The integers denote the lower and
+                    -- upper bound of the length of the string. The
+                    -- value -1 indicates no bound.
+  | Boolean
+  | Number Int Int  -- ^ A number. The integers denote the lower and
+                    -- upper bound on the value. The value -1
+                    -- indicates no bound.
+  | Null
+  deriving (Eq, Show)
+
+-- | A field in an object.
+data Field = Field { key :: String, required :: Bool, content :: Schema } deriving (Eq, Show)
+
+-- | Class representing JSON schemas
+class JSONSchema a where
+  schema :: Proxy a -> Schema
+
+instance JSONSchema () where
+  schema _ = Null
+
+instance JSONSchema Int where
+  schema _ = Number 0 (-1)
+
+instance JSONSchema Integer where
+  schema _ = Number 0 (-1)
+
+instance JSONSchema Word32 where
+  schema _ = Number 0 4294967295
+
+instance JSONSchema Bool where
+  schema _ = Boolean
+
+instance JSONSchema Text where
+  schema _ = Value 0 (-1)
+
+instance JSONSchema a => JSONSchema (Maybe a) where
+  schema p = Choice [Object [Field "Just" True $ schema $ fmap fromJust p], Object [Field "Nothing" True Null]]
+
+instance JSONSchema a => JSONSchema [a] where
+  schema = Array 0 (-1) False . schema . fmap head
