diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
 Changelog
 =========
 
+Version 0.2.7.1
+---------------
+
+*June 1, 2025*
+
+<https://github.com/mstksg/backprop/releases/tag/v0.2.7.1>
+
+*   Actually export internal constructors and utilities in
+    `Numeric.Backprop.Internal`.
+
 Version 0.2.7.0
 ---------------
 
diff --git a/backprop.cabal b/backprop.cabal
--- a/backprop.cabal
+++ b/backprop.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.12
 name:               backprop
-version:            0.2.7.0
+version:            0.2.7.1
 synopsis:           Heterogeneous automatic differentation
 description:
   Write your functions to compute your result, and the library will
diff --git a/src/Numeric/Backprop/Internal.hs b/src/Numeric/Backprop/Internal.hs
--- a/src/Numeric/Backprop/Internal.hs
+++ b/src/Numeric/Backprop/Internal.hs
@@ -35,8 +35,9 @@
 --
 -- @since 0.2.7.0
 module Numeric.Backprop.Internal (
-  BVar,
-  W,
+  -- * Exported for re-use
+  BVar (..),
+  W (..),
   backpropWithN,
   evalBPN,
   constVar,
@@ -66,6 +67,23 @@
   -- * Debug
   debugSTN,
   debugIR,
+
+  -- * Only used internally
+  TapeNode (..),
+  SomeTapeNode (..),
+  BRef (..),
+  Runner (..),
+  initWengert,
+  insertNode,
+  bvConst,
+  forceBVar,
+  forceInpRef,
+  forceSomeTapeNode,
+  forceTapeNode,
+  fillWengert,
+  bumpMaybe,
+  initRunner,
+  gradRunner,
 ) where
 
 import Control.DeepSeq
@@ -207,10 +225,12 @@
 --
 -- See "Numeric.Backprop#liftops" and documentation for
 -- 'Numeric.Backprop.liftOp' for more information.
-data BVar s a = BV
-  { _bvRef :: !(BRef s)
-  , _bvVal :: !a
-  }
+data BVar s a
+  = -- | @since 0.2.7.1
+    BV
+    { _bvRef :: !(BRef s)
+    , _bvVal :: !a
+    }
 
 -- | @since 0.1.5.1
 deriving instance Typeable (BVar s a)
@@ -218,6 +238,7 @@
 -- | @since 0.2.6.3
 instance X.IsoHKD (BVar s) a
 
+-- | @since 0.2.7.1
 data BRef (s :: Type)
   = BRInp !Int
   | BRIx !Int
@@ -240,6 +261,7 @@
 forceBVar (BV r !_) = force r `seq` ()
 {-# INLINE forceBVar #-}
 
+-- | @since 0.2.7.1
 data InpRef :: Type -> Type where
   IR ::
     { _irIx :: !(BVar s b)
@@ -248,6 +270,7 @@
     } ->
     InpRef a
 
+-- | @since 0.2.7.1
 forceInpRef :: InpRef a -> ()
 forceInpRef (IR v !_ !_) = forceBVar v `seq` ()
 {-# INLINE forceInpRef #-}
@@ -256,6 +279,7 @@
 debugIR :: InpRef a -> String
 debugIR IR{..} = show (_bvRef _irIx)
 
+-- | @since 0.2.7.1
 data TapeNode :: Type -> Type where
   TN ::
     { _tnInputs :: !(Rec InpRef as)
@@ -263,16 +287,19 @@
     } ->
     TapeNode a
 
+-- | @since 0.2.7.1
 forceTapeNode :: TapeNode a -> ()
 forceTapeNode (TN inps !_) = VR.rfoldMap forceInpRef inps `seq` ()
 {-# INLINE forceTapeNode #-}
 
+-- | @since 0.2.7.1
 data SomeTapeNode :: Type where
   STN ::
     { _stnNode :: !(TapeNode a)
     } ->
     SomeTapeNode
 
+-- | @since 0.2.7.1
 forceSomeTapeNode :: SomeTapeNode -> ()
 forceSomeTapeNode (STN n) = forceTapeNode n
 
@@ -285,12 +312,16 @@
 --
 -- For the end user, one can just imagine @'Reifies' s 'W'@ as a required
 -- constraint on @s@ that allows backpropagation to work.
-newtype W = W {wRef :: IORef (Int, [SomeTapeNode])}
+newtype W
+  = -- | @since 0.2.7.1
+    W {wRef :: IORef (Int, [SomeTapeNode])}
 
+-- | @since 0.2.7.1
 initWengert :: IO W
 initWengert = W <$> newIORef (0, [])
 {-# INLINE initWengert #-}
 
+-- | @since 0.2.7.1
 insertNode ::
   TapeNode a ->
   -- | val
@@ -659,11 +690,13 @@
   BVar s b
 coerceVar v@(BV r x) = forceBVar v `seq` BV r (coerce x)
 
+-- | @since 0.2.7.1
 data Runner s = R
   { _rDelta :: !(MV.MVector s (Maybe Any))
   , _rInputs :: !(MV.MVector s (Maybe Any))
   }
 
+-- | @since 0.2.7.1
 initRunner ::
   (Int, [SomeTapeNode]) ->
   (Int, [Maybe Any]) ->
@@ -677,6 +710,7 @@
   return $ R delts inps
 {-# INLINE initRunner #-}
 
+-- | @since 0.2.7.1
 gradRunner ::
   forall b s.
   () =>
@@ -709,6 +743,7 @@
     {-# INLINE propagate #-}
 {-# INLINE gradRunner #-}
 
+-- | @since 0.2.7.1
 bumpMaybe ::
   -- | val
   a ->
