diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Daniel Buckmaster
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
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/hscuid.cabal b/hscuid.cabal
new file mode 100644
--- /dev/null
+++ b/hscuid.cabal
@@ -0,0 +1,43 @@
+-- Initial hscuid.cabal generated by cabal init.  For further 
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                hscuid
+version:             1.0.0
+synopsis:            Collision-resistant IDs
+description:         CUIDs are designed for horizontal scalability and collision
+                     resistance. Read more about them at <https://usecuid.org>.
+homepage:            https://github.com/eightyeight/hscuid
+license:             MIT
+license-file:        LICENSE
+author:              Daniel Buckmaster
+maintainer:          dan.buckmaster@gmail.com
+-- copyright:           
+category:            Web
+build-type:          Simple
+cabal-version:       >=1.8
+
+library
+  hs-source-dirs:      lib
+  exposed-modules:     Web.Cuid
+  build-depends:       base ==4.*,
+                       formatting ==6.*,
+                       time ==1.*,
+                       random ==1.*,
+                       transformers ==0.4.*,
+                       hostname ==1.*,
+                       text ==1.*
+  if os(windows)
+    build-depends:     Win32 ==2.*
+    extra-libraries:   kernel32
+  else
+    build-depends:     unix ==2.*
+  ghc-options:         -Wall
+
+test-suite hscuid-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Test.hs
+  build-depends:       base ==4.*,
+                       containers ==0.5.*,
+                       hscuid
+  ghc-options:         -Wall -rtsopts -O2
diff --git a/lib/Web/Cuid.hs b/lib/Web/Cuid.hs
new file mode 100644
--- /dev/null
+++ b/lib/Web/Cuid.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE CPP, ForeignFunctionInterface #-}
+
+{-|
+Stability: stable
+Portability: portable
+
+You can generate a new CUID inside any IO-enabled monad using this module's
+one exported function:
+
+>>> cuid <- newCuid
+>>> print cuid
+"ciaafthr00000qhpm0jp81gry"
+-}
+module Web.Cuid (
+    Cuid, newCuid
+) where
+
+import Control.Monad (liftM)
+import Control.Monad.IO.Class (MonadIO, liftIO)
+import Data.Char (ord)
+import Data.Monoid (mconcat, (<>))
+import Data.IORef (IORef, newIORef, atomicModifyIORef')
+import Data.String (fromString)
+import Data.Text (Text)
+import Data.Time.Clock.POSIX (getPOSIXTime)
+import Formatting (Format, base, fitRight, sformat, left, (%.))
+import Network.HostName (getHostName)
+import System.IO.Unsafe (unsafePerformIO)
+import System.Random (randomRIO)
+
+#if defined(mingw32_HOST_OS)
+import System.Win32 (ProcessId, failIfZero)
+#else
+import System.Posix.Process (getProcessID)
+#endif
+
+-- | Convenience type so that you don't have to import Text downstream. Note that
+-- this is strict Text.
+type Cuid = Text
+
+-- | Generate a new random CUID.
+newCuid :: MonadIO m => m Cuid
+newCuid = concatM [c, time, count, fingerprint, random, random] where
+    -- The CUID starts with a letter so it's usable in HTML element IDs.
+    c = return (fromString "c")
+
+    -- The second chunk is the timestamp. Note that this means it is possible
+    -- to determine the time a particular CUID was created.
+    time = liftM (sformat number . millis) getPOSIXTime
+
+    -- To avoid collisions on the same machine, add a global counter to each ID.
+    count = liftM (sformat numberPadded) (postIncrement counter)
+
+    -- To avoid collisions between separate machines, generate a 'fingerprint'
+    -- from details which are hopefully unique to this machine - PID and hostname.
+    fingerprint = do
+        pid <- getPid
+        hostname <- getHostName
+        let hostSum = 36 + length hostname + sum (map ord hostname)
+            twoOfNum = sformat (fitRight 2 %. number)
+        return (twoOfNum pid <> twoOfNum hostSum)
+
+    -- And some randomness for good measure. Note that System.Random is not a
+    -- source of crypto-strength randomness.
+    random = liftM (sformat numberPadded) (randomRIO (0, maxValue))
+
+    -- Evaluate IO actions and concatenate their results.
+    concatM actions = liftM mconcat (liftIO $ sequence actions)
+
+    -- POSIX time library gives the result in fractional seconds.
+    millis posix = round (posix * 1000)
+
+-- CUID calls for a globally incrementing counter per machine. This is ugly,
+-- but it satisfies the requirement.
+counter :: IORef Int
+counter = unsafePerformIO (newIORef 0)
+-- Don't want two different counters being created because of inlining.
+-- For more info: https://wiki.haskell.org/Top_level_mutable_state
+{-# NOINLINE counter #-}
+
+-- Increment the counter, and return the value before it was incremented.
+postIncrement :: MonadIO m => IORef Int -> m Int
+postIncrement c = liftIO (atomicModifyIORef' c incrementAndWrap) where
+    incrementAndWrap count = (succ count `mod` maxValue, count)
+
+-- These constants are to do with number formatting.
+formatBase, blockSize, maxValue :: Int
+formatBase = 36
+blockSize = 4
+maxValue = formatBase ^ blockSize
+
+-- Number formatters for converting to the correct base and padding.
+number, numberPadded :: Format Text (Int -> Text)
+(number, numberPadded) = (toBase, toBlockSize %. toBase) where
+    toBlockSize = left blockSize '0'
+    toBase = base formatBase
+
+-- Get the ID of the current process. This function has a platform-specific
+-- implementation. Fun times.
+getPid :: MonadIO m => m Int
+
+#if defined(mingw32_HOST_OS)
+
+foreign import stdcall unsafe "windows.h GetCurrentProcessId"
+    c_GetCurrentProcessId :: IO ProcessId
+
+getCurrentProcessId :: IO ProcessId
+getCurrentProcessId = failIfZero "GetCurrentProcessId" c_GetCurrentProcessId
+
+getPid = liftM fromIntegral (liftIO getCurrentProcessId)
+
+#else
+
+getPid = liftM fromIntegral (liftIO getProcessID)
+
+#endif
diff --git a/test/Test.hs b/test/Test.hs
new file mode 100644
--- /dev/null
+++ b/test/Test.hs
@@ -0,0 +1,28 @@
+module Main where
+
+import Data.Set (empty, insert, size)
+import Control.Monad (foldM)
+import System.Exit (ExitCode(..), exitWith)
+
+import Web.Cuid (newCuid)
+
+main :: IO ()
+main = do
+    result <- runCollisionTest 1200000
+    case result of
+        Nothing -> exitWith ExitSuccess
+        Just n -> do
+            print ("Number of collisions: " ++ show n)
+            exitWith (ExitFailure 1)
+
+runCollisionTest :: Int -> IO (Maybe Int)
+runCollisionTest numberOfCuids = do
+    let accumulate set _i = do
+            cuid <- newCuid
+            return $! insert cuid set
+    -- Generate a set containing a bunch of generated CUIDs.
+    set <- foldM accumulate empty [0..numberOfCuids-1]
+    -- If every element was unique, the set will have the same size as the input.
+    return $ if size set == numberOfCuids
+        then Nothing
+        else Just (numberOfCuids - size set)
