diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2014, jay groven
+
+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 jay groven 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/objectid.cabal b/objectid.cabal
new file mode 100644
--- /dev/null
+++ b/objectid.cabal
@@ -0,0 +1,46 @@
+-- Initial objectid.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                objectid
+version:             0.1.0.2
+synopsis:            Rather unique identifier for things that need to be stored
+-- description:         
+homepage:            https://github.com/tsuraan/objectid
+license:             BSD3
+license-file:        LICENSE
+author:              jay groven
+maintainer:          tsuraan@gmail.com
+-- copyright:           
+category:            Data
+build-type:          Simple
+-- extra-source-files:  
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Data.ObjectID
+  -- other-modules:       
+  -- other-extensions:    
+  build-depends:       base >=4.6 && <4.8
+                     , blaze-builder
+                     , bytestring >= 0.10.0.0
+                     , cereal
+                     , cryptohash >= 0.5.1
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+
+test-suite test-all
+  type:                exitcode-stdio-1.0
+  main-is:             TestAll.hs
+  build-depends:       base >= 4.6 && < 4.8
+                     , bytestring
+                     , bytestring-arbitrary
+                     , cereal
+                     , objectid
+                     , QuickCheck >= 2.1
+                     , tasty
+                     , tasty-quickcheck
+  hs-source-dirs:      tests
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+
diff --git a/src/Data/ObjectID.hs b/src/Data/ObjectID.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/ObjectID.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE FlexibleInstances #-}
+-- |
+-- Module    : Data.ObjectID
+-- Copyright : Jeremy Groven
+-- License   : BSD3
+--
+-- Opaque type to uniquely identify things. Objects to be identified must
+-- implement the 'uniqueByteString' of 'IsObject', ideally in some way that
+-- different objects have different bytestrings, while two instances of the
+-- same object will have the same bytestring.
+
+module Data.ObjectID
+( ObjectID
+, calculateSerialize
+, calculateBuilder
+, calculateByteString
+) where
+
+import Crypto.Hash.Skein256 ( hash )
+import Blaze.ByteString.Builder ( Builder, toByteString )
+import Data.ByteString      ( ByteString )
+import Data.Serialize       ( Serialize(..), encode, decode )
+import Data.Serialize.Get   ( getWord64be )
+import Data.Serialize.Put   ( putWord64be )
+import Data.Word            ( Word64 )
+
+-- |Opaque type used to uniquely identify objects
+data ObjectID = ObjectID !Word64 !Word64 !Word64 !Word64
+                deriving ( Show, Eq, Ord )
+
+instance Serialize ObjectID where
+  put (ObjectID a b c d) = do
+      putWord64be a
+      putWord64be b
+      putWord64be c
+      putWord64be d
+
+  get = do
+    a <- getWord64be
+    b <- getWord64be
+    c <- getWord64be
+    d <- getWord64be
+    return $ ObjectID a b c d
+
+-- |Get the 'ObjectID' from a 'Serialize' instance
+calculateSerialize :: Serialize a => a -> ObjectID
+calculateSerialize = calculateByteString . encode
+
+calculateBuilder :: Builder -> ObjectID
+calculateBuilder = calculateByteString . toByteString
+
+calculateByteString :: ByteString -> ObjectID
+calculateByteString bs =
+  let hsh = hash 256 bs
+      Right oid = decode hsh
+  in oid
+
diff --git a/tests/TestAll.hs b/tests/TestAll.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestAll.hs
@@ -0,0 +1,71 @@
+module Main
+( main
+) where
+
+import Data.ObjectID
+
+import qualified Data.ByteString as ByteString
+import Data.ByteString.Arbitrary
+import Data.Serialize        ( encode, decode )
+import Data.Word             ( Word64 )
+import Test.Tasty
+import Test.Tasty.QuickCheck ( testProperty )
+
+main :: IO ()
+main = defaultMain $
+  testGroup "ObjectID"
+  [ testProperty "ByteString"     bytestring
+  , testProperty "Integer"        integer
+  , testProperty "Double"         double
+  , testProperty "Word"           word
+  , testProperty "Neg ByteString" neg_bytestring
+  , testProperty "Neg Integer"    neg_integer
+  , testProperty "Neg Double"     neg_double
+  , testProperty "Neg Word"       neg_word
+  ]
+  where
+
+  bytestring :: ArbByteString -> Bool
+  bytestring (ABS bs) =
+    let oid = calculateByteString bs
+        ser = encode oid
+        Right des = decode ser
+    in oid == des
+
+  integer :: Integer -> Bool
+  integer = seri
+
+  double :: Double -> Bool
+  double = seri
+
+  word :: Word64 -> Bool
+  word = seri
+
+  seri s =
+    let oid = calculateSerialize s
+        ser = encode oid
+        Right des = decode ser
+    in oid == des
+
+  neg_bytestring :: ArbByteString -> Bool
+  neg_bytestring (ABS bs) =
+    let oid = calculateSerialize bs
+        ser = ByteString.reverse $ encode oid
+        Right des = decode ser
+    in oid /= des
+
+  neg_integer :: Integer -> Bool
+  neg_integer = neg_seri
+
+  neg_double :: Double -> Bool
+  neg_double = neg_seri
+
+  neg_word :: Word64 -> Bool
+  neg_word = neg_seri
+
+  neg_seri s =
+    let oid = calculateSerialize s
+        ser = ByteString.reverse $ encode oid
+        Right des = decode ser
+    in oid /= des
+
