diff --git a/Source/IO.hs b/Source/IO.hs
--- a/Source/IO.hs
+++ b/Source/IO.hs
@@ -57,6 +57,7 @@
     ) where
 
 import Lawless
+-- import IO.Base
 import Control.Monad.IO.Class
 import qualified System.Path.Directory as D
 import qualified System.Path.IO as PIO
@@ -73,7 +74,7 @@
 
 default (Text)
 
--- * Printable I/O to stdio
+-- * 'Printable' IO to stdio
 
 -- | Exception representing a failure to parse a 'Textual'.
 data ParseError = ParseError {
diff --git a/Source/STM.hs b/Source/STM.hs
new file mode 100644
--- /dev/null
+++ b/Source/STM.hs
@@ -0,0 +1,21 @@
+{-# Language DataKinds, TemplateHaskell #-}
+
+{-|
+Module:             STM
+Description:        Higher-level STM operations.
+Copyright:          © 2017 All rights reserved.
+License:            GPL-3
+Maintainer:         Evan Cofsky <evan@theunixman.com>
+Stability:          experimental
+Portability:        POSIX
+-}
+
+module STM (
+    module STM.Base,
+    module STM.Flag,
+    module STM.Scatter
+    ) where
+
+import STM.Base
+import STM.Flag
+import STM.Scatter
diff --git a/Source/STM/Base.hs b/Source/STM/Base.hs
new file mode 100644
--- /dev/null
+++ b/Source/STM/Base.hs
@@ -0,0 +1,65 @@
+{-|
+Module:             STM.Base
+Description:        Lifted STM operations.
+Copyright:          © 2017 All rights reserved.
+License:            GPL-3
+Maintainer:         Evan Cofsky <evan@theunixman.com>
+Stability:          experimental
+Portability:        POSIX
+-}
+
+module STM.Base (
+    module IO.Base,
+    module Control.Concurrent.STM.TVar,
+    module Control.Concurrent.STM.TMVar,
+    module Control.Concurrent.STM.TBChan,
+    IO,
+    STM,
+    atomically,
+    newTVarIO,
+    newTMVarIO,
+    newEmptyTMVarIO,
+    newTBChanIO,
+    newTBChan
+    ) where
+
+import Lawless
+import IO.Base
+import Control.Concurrent.STM (STM)
+import qualified Control.Concurrent.STM as STM
+import qualified Control.Concurrent.STM.TVar as TVar
+import Control.Concurrent.STM.TVar (TVar, readTVar, writeTVar,  modifyTVar)
+import qualified Control.Concurrent.STM.TMVar as TMVar
+import Control.Concurrent.STM.TMVar (TMVar, readTMVar)
+import qualified Control.Concurrent.STM.TBChan as TBChan
+import Control.Concurrent.STM.TBChan (
+    TBChan, readTBChan, writeTBChan, isEmptyTBChan
+    )
+import Data.Word
+
+-- | 'STM.atomically' lifted to 'MonadBase' 'IO'.
+atomically :: MonadBase IO m => STM α -> m α
+atomically = liftBase ∘ STM.atomically
+
+-- | 'newTVarIO' at the top level, lifted.
+newTVarIO ∷ MonadBase IO m ⇒ a → m (TVar a)
+newTVarIO = liftBase ∘ TVar.newTVarIO
+
+-- | 'newTMVarIO' at the top level, lifted.
+newTMVarIO ∷ MonadBase IO m ⇒ a → m (TMVar a)
+newTMVarIO = liftBase ∘ TMVar.newTMVarIO
+
+-- | 'newTMVarIO' at the top level, lifted.
+newEmptyTMVarIO ∷ MonadBase IO m ⇒ m (TMVar a)
+newEmptyTMVarIO = liftBase TMVar.newEmptyTMVarIO
+
+newtype BoundedLength = BoundedLength Word64
+    deriving (Eq, Ord, Show, Bounded, Enum, Num, Integral, Real)
+
+-- | 'newTBChanIO' at the top level, lifted, only with a saturated length.
+newTBChanIO ∷ (MonadBase IO m, Integral l) ⇒ l → m (TBChan a)
+newTBChanIO =
+    liftBase ∘ TBChan.newTBChanIO ∘ max 0 ∘ fromIntegral
+
+newTBChan ∷ Integral l ⇒ l → STM (TBChan a)
+newTBChan = TBChan.newTBChan ∘ max 0 ∘ fromIntegral
diff --git a/Source/STM/Flag.hs b/Source/STM/Flag.hs
new file mode 100644
--- /dev/null
+++ b/Source/STM/Flag.hs
@@ -0,0 +1,42 @@
+{-|
+Module:             STM.Flag
+Description:        A flag plus a function to run an 'Async' and lower the flag when it's started.
+Copyright:          © 2017 All rights reserved.
+License:            GPL-3
+Maintainer:         Evan Cofsky <evan@theunixman.com>
+Stability:          experimental
+Portability:        POSIX
+-}
+
+module STM.Flag where
+
+import Lawless
+import Control.Concurrent.Async.Lifted
+import Control.Concurrent.STM.TSem
+import STM.Base
+
+-- | A 'Flag' used for mutexes.
+newtype Flag = Flag TSem
+
+-- | Creates a new 'Flag' in the held state.
+newFlag ∷ (MonadBase IO m) ⇒ m Flag
+newFlag = Flag <$> atomically (newTSem 0)
+
+-- | Waits for a 'Flag' to be unheld.
+waitFlag ∷ Flag → STM ()
+waitFlag (Flag s) = waitTSem s
+
+-- | Signals a 'Flag' has been released.
+lowerFlag ∷ Flag → STM ()
+lowerFlag (Flag s) = signalTSem s
+
+-- | Runs @f@ in a new 'Async', waiting for the 'Async' to start
+-- before continuing.
+run ∷ MonadBaseControl IO m ⇒ m a → m (Async (StM m a))
+run f = do
+    g ← newFlag
+    a ← async $ do
+        atomically $ lowerFlag g
+        f
+    atomically $ waitFlag g
+    return a
diff --git a/Source/STM/Hub.hs b/Source/STM/Hub.hs
new file mode 100644
--- /dev/null
+++ b/Source/STM/Hub.hs
@@ -0,0 +1,57 @@
+{-|
+Module:             STM.Hub
+Description:        Hub's are bounded single-reader-multiple-writer queues.
+Copyright:          © 2017 All rights reserved.
+License:            GPL-3
+Maintainer:         Evan Cofsky <evan@theunixman.com>
+Stability:          experimental
+Portability:        POSIX
+-}
+
+module STM.Hub (
+    HubLength,
+    Hub,
+    hub,
+
+    hubMsg,
+    hubEmpty,
+
+    Source,
+    source,
+    sourceMsg
+    )where
+
+import Lawless
+import STM.Base
+import Data.Word
+
+-- * The 'Hub'
+
+-- | The length of a 'Hub', guaranteed to be positive.
+newtype HubLength = HubLength Word64
+    deriving (Eq, Ord, Show, Bounded, Enum, Num, Integral, Real)
+
+-- | The 'Hub' receives the messages from various 'Source's.
+newtype Hub a = Hub {unHub ∷ TBChan a}
+
+hub ∷ MonadBase IO m ⇒ HubLength → m (Hub a)
+hub l = liftBase $ Hub <$> (newTBChanIO $ fromIntegral l)
+
+hubMsg ∷ MonadBase IO m ⇒ (Hub a) → (m a)
+hubMsg = atomically ∘ readTBChan ∘ unHub
+
+hubEmpty ∷ MonadBase IO m ⇒ (Hub a) → (m Bool)
+hubEmpty = atomically ∘  isEmptyTBChan ∘ unHub
+
+-- * The 'Source'
+
+-- | A 'Source' attached to a 'Hub' that can send messages to it.
+newtype Source a = Source {unSource ∷ TBChan a}
+
+-- | Create a 'Source' attached to a 'Hub'.
+source ∷ Getter (Hub a) (Source a)
+source = to $ Source ∘ unHub
+
+-- | Send a message to a 'Hub' through a 'Source'.
+sourceMsg ∷ MonadBase IO m ⇒ Source a → a → m ()
+sourceMsg s = atomically ∘ writeTBChan (unSource s)
diff --git a/Source/STM/Scatter.hs b/Source/STM/Scatter.hs
new file mode 100644
--- /dev/null
+++ b/Source/STM/Scatter.hs
@@ -0,0 +1,51 @@
+{-|
+Module:             STM.Scatter
+Description:        Broadcast channel with multiple listeners and memory safety.
+Copyright:          © 2017 All rights reserved.
+License:            GPL-3
+Maintainer:         Evan Cofsky <evan@theunixman.com>
+Stability:          experimental
+Portability:        POSIX
+-}
+
+module STM.Scatter (
+    Scatter,
+    scatter,
+    scatterMsg,
+
+    Gather,
+    gather,
+    gatherMsg
+    )where
+
+import Lawless
+import STM.Base
+import Control.Concurrent.STM.TChan
+
+-- * The 'Scatter'
+
+-- | A 'Scatter' is a single source that can broadcast messages
+-- to any number of subscribers.
+newtype Scatter a = Scatter {unScatter ∷ TChan a}
+
+-- | Creates a new 'Scatter'.
+scatter ∷ MonadBase IO m ⇒ m (Scatter a)
+scatter = liftBase $ Scatter <$> newTChanIO
+
+-- | Post a message to a 'Scatter' to all the subscribed 'Gather's.
+scatterMsg ∷ MonadBase IO m ⇒ Scatter a → a → m ()
+scatterMsg s a = atomically $ writeTChan (unScatter s) a
+
+-- * The 'Gather'
+
+-- | A 'Gather' receives copies of all messages posted to the
+-- 'Scatter' it was derived from.
+newtype Gather a = Gather {unGather ∷ TChan a}
+
+-- | Create a new 'Gather' from a given 'Scatter'.
+gather ∷ MonadBase IO m ⇒ Getter (Scatter a) (m (Gather a))
+gather = to $ \s → Gather <$> (atomically ∘ dupTChan ∘ unScatter) s
+
+-- | Read a message posted to a 'Gather' by a 'Scatter'.
+gatherMsg ∷ MonadBase IO m ⇒ Gather a → m a
+gatherMsg g = atomically $ readTChan (unGather g)
diff --git a/liblawless.cabal b/liblawless.cabal
--- a/liblawless.cabal
+++ b/liblawless.cabal
@@ -1,5 +1,5 @@
 name:                liblawless
-version:             0.21.3
+version:             0.23.0
 synopsis:            Prelude based on protolude for GHC 8 and beyond.
 license:             GPL-3
 license-file:        LICENSE
@@ -29,7 +29,7 @@
 source-repository this
   type:     git
   location:   https://gitlab.com/theunixman/liblawless.git
-  tag: v0.21.3
+  tag: v0.23.0
 
 library
   ghc-options: -Wall -Wno-redundant-constraints -Wno-type-defaults -O2
@@ -49,6 +49,11 @@
                   Parser
                   Path
                   Set
+                  STM
+                  STM.Base
+                  STM.Flag
+                  STM.Hub
+                  STM.Scatter
                   Text
                   Text.IO
                   Textual
@@ -96,6 +101,7 @@
                 exceptions                 >= 0.8.3 && < 0.9,
                 hjsonschema                >= 1.6.2 && < 1.7,
                 lens                       >= 4.14 && < 4.16,
+                lifted-async  >= 0.9.1.1 && < 0.10,
                 machines                   >= 0.6.1 && < 0.7,
                 managed             >= 1.0.5 && < 1.1,
                 monad-control >= 1.0.1.0 && < 1.1,
@@ -108,6 +114,7 @@
                 random                     >= 1.1 && < 1.2,
                 semigroups                 >= 0.18.2 && < 0.19,
                 stm                        >= 2.4.4 && < 2.5,
+                stm-chans             >= 3.0.0.4 && < 3.1,
                 stm-containers             >= 0.2.15 && < 0.3,
                 QuickCheck                 >= 2.8 && < 2.10,
                 temporary                  >= 1.2.0 && < 1.3,
@@ -117,6 +124,7 @@
                 text-printer               >= 0.4 && < 0.5,
                 time                       >= 1.6.0 && < 1.7,
                 transformers               >= 0.5.2 && < 0.6,
+                transformers-base >= 0.4.4 && < 0.5,
                 zippers                    >= 0.2.2 && < 0.3
   hs-source-dirs:      Source
   default-language:    Haskell2010
