diff --git a/Control/Monad/Squishy.hs b/Control/Monad/Squishy.hs
new file mode 100644
--- /dev/null
+++ b/Control/Monad/Squishy.hs
@@ -0,0 +1,83 @@
+{-# LANGUAGE RankNTypes                 #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+module Control.Monad.Squishy 
+   ( Squishy , runSquishy
+   , Identifier
+   , Distinct , distinguish , conflate , identify
+   , Ref , newRef , readRef , writeRef , modifyRef
+   ) where
+
+import Control.Monad.ST.Lazy
+import Data.STRef.Lazy
+import Control.Monad.State
+import Control.Monad.Reader
+import Control.Applicative
+
+-- | The Squishy monad is a monad with mutable references and optional reference
+--   identity
+newtype Squishy s a =
+   Squishy { getSquishy :: StateT ID (ST s) a }
+   deriving ( Functor, Applicative, Monad )
+
+-- | Runs a Squishy computation, returning a pure value
+runSquishy :: forall a. (forall s. Squishy s a) -> a
+runSquishy x = runST $ evalStateT (getSquishy x) initialID
+
+-- | A unique identifier. Only possible to create while making a Distinct value.
+newtype Identifier s = Identifier ID deriving ( Eq, Ord )
+
+-- | Data with faked reference equality; the interface provided guarantees that
+--   every Distinct value has a unique Identifier.
+data Distinct s a =
+   Distinct a (Identifier s)
+
+instance Eq (Distinct s a) where
+   a == b = identify a == identify b
+
+instance Ord (Distinct s a) where
+   compare a b = compare (identify a) (identify b)
+
+-- | The only way to create a Distinct value is to generate a new identifier for
+--   it in the Squishy monad.
+distinguish :: a -> Squishy s (Distinct s a)
+distinguish a = Distinct a <$> newIdentifier
+   where
+      newIdentifier = Squishy $ do
+         x <- get
+         put (succ x)
+         return (Identifier x)
+
+-- | Extracts the value stored in a Distinct
+conflate :: Distinct s a -> a
+conflate (Distinct a _) = a
+
+-- | Extracts the unique identifier for a Distinct
+identify :: Distinct s a -> Identifier s
+identify (Distinct _ i) = i
+
+-- The mutable ref API from Data.STRef, lifted through to the Squishy monad
+
+-- | A mutable reference in the @Squishy@ monad.
+newtype Ref s a = Ref (STRef s a)
+
+-- | Make a new reference.
+newRef :: a -> Squishy s (Ref s a)
+newRef a = Squishy $ lift (Ref <$> newSTRef a)
+
+-- | Read the value of a reference.
+readRef :: Ref s a -> Squishy s a
+readRef (Ref r) = Squishy $ lift (readSTRef r)
+
+-- | Write a new value to a reference.
+writeRef :: Ref s a -> a -> Squishy s ()
+writeRef (Ref r) a = Squishy $ lift (writeSTRef r a)
+
+-- | Use the provided function to modify the contained value in a reference.
+modifyRef :: Ref s a -> (a -> a) -> Squishy s ()
+modifyRef (Ref r) a = Squishy $ lift (modifySTRef r a)
+
+-- This type is not exposed, it's used internally to implement unique identifiers
+type ID = Int
+initialID :: ID
+initialID = minBound
diff --git a/Data/Kiln.hs b/Data/Kiln.hs
new file mode 100644
--- /dev/null
+++ b/Data/Kiln.hs
@@ -0,0 +1,76 @@
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Data.Kiln
+   ( Clay , newClay , readClay , modifyClay , writeClay , identifyClay
+   , kiln , kilnWith , runKilningWith , runKilning
+   , module X
+   ) where
+
+import Control.Monad.Squishy as X
+import Data.Fix              as X
+import Data.Traversable      as X
+
+import Control.Monad
+import Control.Monad.State
+import Control.Monad.IfElse
+import Control.Applicative
+import Control.Arrow
+
+import           Data.Map ( Map )
+import qualified Data.Map as M
+
+-- | A @Clay s f@ is a recursive structure with a uniquely-identified mutable
+--   reference at each node. It cannot escape the @Squishy@ monad, by means of
+--   the same mechanism as used in @Control.Monad.ST@.
+data Clay s f = Clay { getClay :: Distinct s (Ref s (f (Clay s f))) }
+
+-- | Take a container @f (Clay s f)@, and wrap it in a new mutable reference and
+--   distinct tag, returning a new @Clay s f@.
+newClay :: f (Clay s f) -> Squishy s (Clay s f)
+newClay = (Clay <$>) . (distinguish =<<) . newRef
+
+-- | Takes a @Clay s f@ and exposes the first level of @f (Clay s f)@ inside it.
+readClay :: Clay s f -> Squishy s (f (Clay s f))
+readClay = readRef . conflate . getClay
+
+-- | Use the provided function to destructively update the value at this node of
+--   a @Clay s f@.
+modifyClay :: Clay s f -> (f (Clay s f) -> f (Clay s f)) -> Squishy s ()
+modifyClay = modifyRef . conflate . getClay
+
+-- | Set a piece of @Clay@ to a particular value.
+writeClay :: Clay s f -> f (Clay s f) -> Squishy s ()
+writeClay = writeRef . conflate . getClay
+
+-- | Get the unique @Identifier@ for a piece of @Clay@.
+identifyClay :: Clay s f -> Identifier s
+identifyClay = identify . getClay
+
+-- | Given a @Clay s f@, use a natural transformation @(forall a. f a -> g a)@
+--   to convert it into the fixed-point of a the functor @g@ by eliminating the
+--   indirection of the mutable references and using the distinct tags on the
+--   structure's parts to tie knots where there are cycles in the original graph
+--   of references. TThe result is an immutable cyclic lazy data structure.
+kilnWith :: forall s f g. (Traversable f) => (forall a. f a -> g a) -> Clay s f -> Squishy s (Fix g)
+kilnWith transform = flip evalStateT M.empty . kiln'
+   where
+      kiln' :: Clay s f -> StateT (Map (Identifier s) (Fix g)) (Squishy s) (Fix g)
+      kiln' clay =
+         aifM (M.lookup (identifyClay clay) <$> get) return $ do
+            baked <- (Fix . transform <$>) . traverse kiln' =<< lift (readClay clay)
+            modify (M.insert (identifyClay clay) baked) >> return baked
+
+-- | Freeze a Clay using the identity transformation, so that a Clay s f turns into a Fix f.
+kiln :: (Traversable f) => Clay s f -> Squishy s (Fix f)
+kiln = kilnWith id
+
+-- | Given a @Squishy@ monad action which returns a @Clay s f@, run the action
+--   and use the provided natural transformation during baking (see 'kilnWith').
+runKilningWith :: (Traversable f) => (forall a. f a -> g a) -> (forall s. Squishy s (Clay s f)) -> Fix g
+runKilningWith transform action = runSquishy (action >>= kilnWith transform)
+
+-- | Given a @Squishy@ monad action which returns a @Clay s f@, run the action
+--   and kiln the result.
+runKilning :: (Traversable f) => (forall s. Squishy s (Clay s f)) -> Fix f
+runKilning = runKilningWith id
diff --git a/Data/Kiln/Examples.hs b/Data/Kiln/Examples.hs
new file mode 100644
--- /dev/null
+++ b/Data/Kiln/Examples.hs
@@ -0,0 +1,77 @@
+module Data.Kiln.Examples where
+
+import Data.Kiln
+
+import Control.Arrow
+import Data.List
+import Data.Functor.Compose
+import Data.Traversable
+import Data.Foldable
+import Control.Applicative
+
+-- | Apply a function to the value inside a Compose.
+composedly :: (f (g a) -> f' (g' a')) -> Compose f g a -> Compose f' g' a'
+composedly f = Compose . f . getCompose
+
+-- Mutable singly-linked lists built from cons-cells
+
+type MSLL s a = Clay s (Compose ((,) a) Maybe)
+type SLL    a =    Fix (Compose ((,) a) Maybe)
+
+cons :: a -> Maybe (MSLL s a) -> Squishy s (MSLL s a)
+cons car cdr = newClay (Compose (car, cdr))
+
+setCar :: MSLL s a -> a -> Squishy s ()
+setCar x = modifyClay x . composedly . first  . const
+
+setCdr :: MSLL s a -> Maybe (MSLL s a) -> Squishy s ()
+setCdr x = modifyClay x . composedly . second . const
+
+list1 :: SLL Char
+list1 = runKilning $ do
+   a <- cons 'a' Nothing
+   b <- cons 'b' (Just a)
+   c <- cons 'c' (Just b)
+   setCdr a $ Just c
+   return c
+
+sllToList :: SLL a -> [a]
+sllToList sll = case (getCompose . unFix) sll of
+   (x,Nothing) -> [x]
+   (x,Just xs) -> x : sllToList xs
+
+-- Mutable graphs with node and edge labels
+
+type MNode s n e = Clay s (Compose (Compose ((,) n) []) ((,) e))
+type Node    n e =    Fix (Compose (Compose ((,) n) []) ((,) e))
+
+node :: n -> [(e, MNode s n e)] -> Squishy s (MNode s n e)
+node n list = newClay (Compose (Compose (n,list)))
+
+emptyNode :: n -> Squishy s (MNode s n e)
+emptyNode n = node n []
+
+readNode :: MNode s n e -> Squishy s (n, [(e, MNode s n e)])
+readNode = fmap (getCompose . getCompose) . readClay
+
+relabelNode :: n -> MNode s n e -> Squishy s ()
+relabelNode n = flip modifyClay (composedly . composedly . first . const $ n)
+
+editEdges :: ([(e, MNode s n e)] -> [(e, MNode s n e)]) -> MNode s n e -> Squishy s ()
+editEdges f = flip modifyClay (composedly . composedly . second $ f)
+
+addEdge :: e -> MNode s n e -> MNode s n e -> Squishy s ()
+addEdge label from to = editEdges ((label, to) :) from
+
+graph1 :: Node String String
+graph1 = runKilning $ do
+   a <- emptyNode "a"
+   b <- emptyNode "b"
+   c <- emptyNode "c"
+   d <- emptyNode "d"
+   addEdge "a -> b" a b
+   addEdge "b -> c" b c
+   addEdge "c -> d" c d
+   addEdge "c -> a" c a
+   addEdge "d -> a" d a
+   return a
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+Copyright (c) 2014, Kenneth Foner
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. 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.
+
+3. Neither the name of the copyright holder nor the names of its 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 HOLDER 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+Data.Kiln
+=========
+
+We functional programmers know that mutable state is unglamorous, but sometimes useful. `Data.Kiln` lets you create and manipulate mutable recursive structures (`Clay`) in the `Squishy` monad (built over `ST`), then once you're finished with mutation, bake them into immutable, pure, lazy data. The library is polymorphic in the "shape" of each mutable "node" in the graph, requiring only that it be Traversable, which means that all sorts of structures, from cons-cells to tagged state diagrams, can be represented and manipulated safely and efficiently, with an ultimately pure output.
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/data-kiln.cabal b/data-kiln.cabal
new file mode 100644
--- /dev/null
+++ b/data-kiln.cabal
@@ -0,0 +1,23 @@
+name:                data-kiln
+version:             0.1.0.0
+synopsis:            Sculpt mutable recursive data with reference equality; bake it using a data kiln into an immutable lazy structure
+description:         We functional programmers know that mutable state is unglamorous, but sometimes useful. Data.Kiln lets you create and manipulate mutable recursive structures (Clay) in the Squishy monad (built over ST), then once you're finished with mutation, bake them into immutable, pure, lazy data. The library is polymorphic in the shape of each mutable node in the graph, requiring only that it be Traversable, which means that all sorts of structures, from cons-cells to tagged state diagrams, can be represented and manipulated safely and efficiently, with an ultimately pure output.
+license:             BSD3
+license-file:        LICENSE
+author:              Kenneth Foner
+maintainer:          kenny.foner@gmail.com
+copyright:           Copyright (c) 2014 Kenneth Foner
+homepage:            https://github.com/kwf/data-kiln
+bug-reports:         https://github.com/kwf/data-kiln/issues
+category:            Data
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  exposed-modules:     Control.Monad.Squishy, Data.Kiln, Data.Kiln.Examples
+  -- other-modules:       
+  other-extensions:    RankNTypes, GeneralizedNewtypeDeriving, ScopedTypeVariables
+  build-depends:       base >=4.7 && <4.8, transformers >=0.4 && <0.5, mtl >=2.2 && <2.3, data-fix >=0.0 && <0.1, IfElse >=0.85 && <0.86, containers >=0.5 && <0.6
+  -- hs-source-dirs:      
+  default-language:    Haskell2010
