diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
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/seqid.cabal b/seqid.cabal
new file mode 100644
--- /dev/null
+++ b/seqid.cabal
@@ -0,0 +1,19 @@
+name:                seqid
+version:             0.1.0
+synopsis:            Sequence ID production and consumption
+description:         Uniquely identify elements in a sequence.
+License:             BSD3
+License-file:        LICENSE
+author:              Luke Hoersten
+maintainer:          luke@hoersten.org
+category:            Data
+build-type:          Simple
+cabal-version:       >= 1.10
+
+library
+  exposed-modules: Data.SequenceId
+  build-depends: base         >= 4.7 && < 4.8
+               , transformers >= 0.4 && < 0.5
+
+  hs-source-dirs:      src
+  default-language:    Haskell2010
diff --git a/src/Data/SequenceId.hs b/src/Data/SequenceId.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/SequenceId.hs
@@ -0,0 +1,60 @@
+module Data.SequenceId
+       ( checkSeqIdM
+       , checkSeqId
+       , nextSeqIdM
+       , nextSeqId
+       , SequenceError (..)
+       , SequenceIds (..)
+       , SequenceId
+       , SequenceT
+       , evalSequenceT
+       ) where
+
+
+import           Control.Monad.Trans.State (StateT, evalStateT, gets, modify,
+                                            put)
+import           Data.Word                 (Word32)
+
+
+newtype LastSeqId = LastSeqId { unLastSeqId :: SequenceId } deriving Show
+type SequenceT = StateT LastSeqId
+type SequenceId = Word32
+
+
+evalSequenceT :: Monad m => SequenceId -> StateT LastSeqId m b -> m b
+evalSequenceT = flip evalStateT . LastSeqId
+
+
+data SequenceIds =
+    SequenceIds
+    { lastSeqId :: SequenceId
+    , currSeqId :: SequenceId
+    } deriving (Eq, Show)
+
+
+data SequenceError
+    = SequenceIdDropped SequenceIds
+    | SequenceIdDuplicated SequenceIds
+    deriving (Eq, Show)
+
+
+checkSeqIdM :: Monad m => SequenceId -> (SequenceT m) (Maybe SequenceError)
+checkSeqIdM currSeq = do
+    lastSeq <- gets unLastSeqId
+    put $ LastSeqId currSeq
+    return $ checkSeqId lastSeq currSeq
+
+
+checkSeqId :: SequenceId -> SequenceId -> Maybe SequenceError
+checkSeqId lastSeq currSeq
+    | (currSeq - lastSeq) > 1 = Just . SequenceIdDropped    $ SequenceIds lastSeq currSeq
+    | (currSeq - lastSeq) < 1 = Just . SequenceIdDuplicated $ SequenceIds lastSeq currSeq
+    | otherwise = Nothing
+
+
+nextSeqIdM :: Monad m => SequenceT m SequenceId
+nextSeqIdM = modify (LastSeqId . nextSeqId . unLastSeqId) >> gets unLastSeqId
+
+
+nextSeqId :: SequenceId -> SequenceId
+nextSeqId = (+1)
