diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.0.0
+
+* Initial version
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2024 Tom Ellis
+
+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/bluefin.cabal b/bluefin.cabal
new file mode 100644
--- /dev/null
+++ b/bluefin.cabal
@@ -0,0 +1,34 @@
+cabal-version:      3.0
+name:               bluefin
+version:            0.0.0.0
+license:            MIT
+license-file:       LICENSE
+author:             Tom Ellis
+maintainer:         Tom Ellis
+build-type:         Simple
+extra-doc-files:    CHANGELOG.md
+description:        The Bluefin effect system
+synopsis:           The Bluefin effect system
+
+common warnings
+    ghc-options: -Wall
+
+library
+    import:           warnings
+    default-extensions:
+      NoImplicitPrelude
+    exposed-modules:
+      Bluefin,
+      Bluefin.Compound,
+      Bluefin.Coroutine,
+      Bluefin.EarlyReturn,
+      Bluefin.Eff,
+      Bluefin.Exception,
+      Bluefin.Jump,
+      Bluefin.IO,
+      Bluefin.State,
+      Bluefin.Stream,
+    build-depends:
+      bluefin-internal < 0.1
+    hs-source-dirs:   src
+    default-language: Haskell2010
diff --git a/src/Bluefin.hs b/src/Bluefin.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin.hs
@@ -0,0 +1,198 @@
+module Bluefin
+  ( -- * In brief
+
+    -- | Bluefin is an effect system which allows you, though
+    -- value-level handles, to freely mix a variety of effects
+    -- including
+    --
+    --  * "Bluefin.EarlyReturn", for early return
+    --  * "Bluefin.Exception", for exceptions
+    --  * "Bluefin.IO", for I/O
+    --  * "Bluefin.State", for mutable state
+    --  * "Bluefin.Stream", for streams
+
+    -- * Introduction
+
+    -- | Bluefin is a Haskell effect system with a new style of API.
+    -- It is distinct from prior effect systems because effects are
+    -- accessed explicitly through value-level handles which occur as
+    -- arguments to effectful operations. Handles (such as
+    -- 'Bluefin.State.State' handles, which allow access to mutable
+    -- state) are introduced by handlers (such as
+    -- 'Bluefin.State.evalState', which sets the initial state).
+    -- Here's an example where a mutable state effect handle, @sn@, is
+    -- introduced by its handler, 'Bluefin.State.evalState'.
+    --
+    -- @
+    -- -- If @n < 10@ then add 10 to it, otherwise
+    -- -- return it unchanged
+    -- example1 :: Int -> Int
+    -- example1 n = 'Bluefin.Eff.runPureEff' $
+    --   -- Create a new state handle, sn, and
+    --   -- initialize the value of the state to n
+    --   'Bluefin.State.evalState' n $ \\sn -> do
+    --     n' <- 'Bluefin.State.get' sn
+    --     when (n' < 10) $
+    --       'Bluefin.State.modify' sn (+ 10)
+    --     get sn
+    -- @
+    --
+    -- @
+    -- >>> example1 5
+    -- 15
+    -- >>> example1 12
+    -- 12
+    -- @
+    --
+    -- The handle @sn@ is used in much the same way as an
+    -- 'Data.STRef.STRef' or 'Data.IORef.IORef'.
+
+    -- ** Multiple effects of the same type
+
+    -- | A benefit of value-level effect handles is that it's simple
+    -- to have multiple effects of the same type in scope at the same
+    -- time.  It's easy to disambiguate them because they are distinct
+    -- values!  It is not simple with existing effect systems because
+    -- they require the disambiguation to occur at the type level.
+    -- Here is an example with two mutable @Int@ state effects in
+    -- scope.
+    --
+    -- @
+    -- -- Compare two values and add 10
+    -- -- to the smaller
+    -- example2 :: (Int, Int) -> (Int, Int)
+    -- example2 (m, n) = 'Bluefin.Eff.runPureEff' $
+    --   'Bluefin.State.evalState' m $ \\sm -> do
+    --     evalState n $ \\sn -> do
+    --       do
+    --         n' <- 'Bluefin.State.get' sn
+    --         m' <- get sm
+    --
+    --         if n' < m'
+    --           then 'Bluefin.State.modify' sn (+ 10)
+    --           else modify sm (+ 10)
+    --
+    --       n' <- get sn
+    --       m' <- get sm
+    --
+    --       pure (n', m')
+    -- @
+    --
+    -- @
+    -- >>> example2 (5, 10)
+    -- (15, 10)
+    -- >>> example2 (30, 3)
+    -- (30, 13)
+    -- @
+
+    -- ** Effect scoping
+
+    -- | Bluefin's use of the type system is very similar to
+    -- 'Control.Monad.ST': it ensures that a handle can never escape
+    -- the scope of its handler.  That is, once the handler has
+    -- finished running there is no way you can use the handle
+    -- anymore.
+
+    -- * Comparison to other effect systems
+
+    -- ** Everything except effectful
+
+    -- | The design of Bluefin is strongly inspired by and based on
+    -- effectful.  All the points in [effectful's comparison of itself
+    -- to other effect
+    -- systems](https://github.com/haskell-effectful/effectful?tab=readme-ov-file#motivation)
+    -- apply to Bluefin too.
+
+    -- ** effectful
+
+    -- | The major difference between Bluefin and effectful is that in
+    -- Bluefin effects are represented as value-level handles whereas
+    -- in effectful they are represented only at the type level.
+    -- effectful could be described as "a well-typed implementation of
+    -- the @ReaderT@ @IO@ pattern", and Bluefin could be described as
+    -- a well-typed implementation of something even simpler: "the
+    -- functions-that-return-@IO@ pattern".  The aim of the Bluefin
+    -- style of value-level effect tracking is to make it even easier
+    -- to mix effects, especially effects of the same type. Only time
+    -- will tell which approach is preferable in practice.
+
+    -- Haddock seems to have trouble with italic sections spanning
+    -- lines :(
+
+    -- | "/Why not just implement Bluefin as an alternative API on/
+    -- /top of effectful?/"
+    --
+    -- It would be great to share code between the two projects!  But
+    -- there are two Bluefin features that I don't know to implement
+    -- in terms of effectful: 'Bluefin.Bluefin.Coroutine's and
+    -- 'Bluefin.Bluefin.Compound' effects.
+
+    -- * Implementation
+
+    -- | Bluefin has a similar implementation style to effectful.
+    -- 'Bluefin.Eff.Eff' is an opaque wrapper around 'IO',
+    -- 'Bluefin.State.State' is an opaque wrapper around
+    -- 'Data.IORef.IORef', and 'Bluefin.Exception.throw' throws an
+    -- actual @IO@ exception.  'Bluefin.Coroutine.Coroutine', which
+    -- doesn't exist in effectful, is implemented simply as a
+    -- function.
+    --
+    -- @
+    -- newtype 'Bluefin.Eff.Eff' (es :: 'Bluefin.Eff.Effects') a = 'Bluefin.Internal.UnsafeMkEff' (IO a)
+    -- newtype 'Bluefin.State.State' s (st :: Effects) = 'Bluefin.Internal.UnsafeMkState' (IORef s)
+    -- newtype 'Bluefin.Coroutine.Coroutine' a b (s :: Effects) = 'Bluefin.Internal.UnsafeMkCoroutine' (a -> IO b)
+    -- @
+    --
+    -- The type parameters of kind 'Bluefin.Eff.Effects' are phantom
+    -- type parameters which track which effects can be used in an
+    -- operation. Bluefin uses them to ensure that effects cannot
+    -- escape the scope of their handler, in the same way that the
+    -- type parameter to the 'Control.Monad.ST.ST' monad ensures that
+    -- mutable state references cannot escape
+    -- 'Control.Monad.ST.runST'.  When the type system indicates that
+    -- there are no unhandled effects it is safe to run the underlying
+    -- @IO@ action using 'System.IO.Unsafe.unsafePerformIO', which is
+    -- the approach taken to implement 'Bluefin.Eff.runPureEff'.
+
+    -- * Tips
+
+    -- | * Use @NoMonoLocalBinds@ and @NoMonomorphismRestriction@ for
+    -- better type inference.
+    --
+    -- * Writing a handler often requires an explicit type signature.
+
+    -- * Example
+
+    -- |
+    -- @
+    -- countPositivesNegatives :: [Int] -> String
+    -- countPositivesNegatives is = 'Bluefin.Eff.runPureEff' $
+    --   'Bluefin.State.evalState' (0 :: Int) $ \\positives -> do
+    --       r \<- 'Bluefin.Exception.try' $ \\ex ->
+    --           evalState (0 :: Int) $ \\negatives -> do
+    --               for_ is $ \\i -> do
+    --                   case compare i 0 of
+    --                       GT -> 'Bluefin.State.modify' positives (+ 1)
+    --                       EQ -> throw ex ()
+    --                       LT -> modify negatives (+ 1)
+    --
+    --               p <- 'Bluefin.State.get' positives
+    --               n <- get negatives
+    --
+    --               pure $
+    --                 "Positives: "
+    --                   ++ show p
+    --                   ++ ", negatives "
+    --                   ++ show n
+    --
+    --       case r of
+    --           Right r' -> pure r'
+    --           Left () -> do
+    --               p <- get positives
+    --               pure $
+    --                 "We saw a zero, but before that there were "
+    --                   ++ show p
+    --                   ++ " positives"
+    -- @
+  )
+where
diff --git a/src/Bluefin/Compound.hs b/src/Bluefin/Compound.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Compound.hs
@@ -0,0 +1,16 @@
+module Bluefin.Compound
+  ( -- | @Compound@ allows combining two effects into one, for
+    -- encapsulation.  It is not documented yet.
+
+    -- * Handle
+    Compound,
+
+    -- * Handler
+    runCompound,
+
+    -- * Effectful operations
+    withCompound,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/Coroutine.hs b/src/Bluefin/Coroutine.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Coroutine.hs
@@ -0,0 +1,18 @@
+module Bluefin.Coroutine
+  ( -- | @Coroutine@ allows to yield values and receive results back.
+    -- It is not documented yet.  You might want to start with
+    -- "Bluefin.Stream", which is the most common way to use
+    -- coroutines.
+
+    -- * Handle
+    Coroutine,
+
+    -- * Handlers
+    forEach,
+
+    -- * Effectful operations
+    yieldCoroutine,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/EarlyReturn.hs b/src/Bluefin/EarlyReturn.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/EarlyReturn.hs
@@ -0,0 +1,15 @@
+module Bluefin.EarlyReturn
+  ( -- | Early return allows to define a block from which you can
+    -- return early.  Early return is implemented as an exception, and
+    -- its API is just an alternate interface to exceptions.
+
+    -- * Handle
+    EarlyReturn,
+    -- * Handlers
+    withEarlyReturn,
+    -- * Effectful operations
+    returnEarly,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/Eff.hs b/src/Bluefin/Eff.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Eff.hs
@@ -0,0 +1,22 @@
+module Bluefin.Eff
+  ( -- * 'Eff' monad
+    Eff,
+    -- * Run an 'Eff'
+    runPureEff,
+    runEff,
+    -- * Type classes
+
+    -- | See "Bluefin.Eff.IO" for the most direct way of doing I/O in
+    -- Bluefin.  If you really want to use 'MonadIO' you can use
+    -- 'withMonadIO'.
+
+    withMonadIO,
+    withMonadFail,
+    -- * Effect tracking
+    Effects,
+    (:>),
+    (:&),
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/Exception.hs b/src/Bluefin/Exception.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Exception.hs
@@ -0,0 +1,13 @@
+module Bluefin.Exception
+  ( -- * Handle
+    Exception,
+    -- * Handlers
+    try,
+    handle,
+    catch,
+    -- * Effectful operations
+    throw,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/IO.hs b/src/Bluefin/IO.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/IO.hs
@@ -0,0 +1,20 @@
+module Bluefin.IO
+  ( -- | You can run 'IO' operations inside 'Eff'.
+
+    -- * Handle
+    IOE,
+    -- * Handlers
+    runEff,
+    -- * Effectful operations
+    effIO,
+    -- * IO type classes
+    withMonadIO,
+    withEffToIO,
+    -- ** @EffReader@
+    EffReader,
+    effReader,
+    runEffReader,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/Jump.hs b/src/Bluefin/Jump.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Jump.hs
@@ -0,0 +1,14 @@
+module Bluefin.Jump
+  ( -- | Jump allows you to jump back to a previously-set location.
+    -- It is not documented yet.
+
+    -- * Handle
+    Jump,
+    -- * Handlers
+    withJump,
+    -- * Effectful operations
+    jumpTo,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/State.hs b/src/Bluefin/State.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/State.hs
@@ -0,0 +1,14 @@
+module Bluefin.State
+  ( -- * Handle
+    State,
+    -- * Handlers
+    evalState,
+    runState,
+    -- * Effectful operations
+    get,
+    put,
+    modify,
+  )
+where
+
+import Bluefin.Internal
diff --git a/src/Bluefin/Stream.hs b/src/Bluefin/Stream.hs
new file mode 100644
--- /dev/null
+++ b/src/Bluefin/Stream.hs
@@ -0,0 +1,18 @@
+module Bluefin.Stream
+  ( -- * Handle
+    Stream,
+    -- * Handlers
+    forEach,
+    yieldToList,
+    yieldToReverseList,
+    enumerate,
+    enumerateFrom,
+    mapMaybe,
+    catMaybes,
+    -- * Effectful operations
+    yield,
+    inFoldable,
+  )
+where
+
+import Bluefin.Internal
