diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -59,6 +59,12 @@
 
 For more about how to use annotations, see [the GHC User’s Guide](https://downloads.haskell.org/ghc/latest/docs/users_guide/extending_ghc.html#source-annotations).
 
+## licensing
+
+This package is licensed under [The GNU AGPL 3.0 or later](./LICENSE). If you need a license for usage that isn’t covered under the AGPL, please contact [Greg Pfeil](mailto:greg@technomadic.org?subject=licensing%20no-recursion).
+
+When last checked, all transitive dependencies were licensed under [The 3-Clause BSD License](http://hackage.haskell.org/package/base-4.19.0.0/src/LICENSE). However, you should review the [license report](docs/license-report.md) for more details.
+
 ## comparisons
 
 Other projects similar to this one, and how they differ.
diff --git a/docs/license-report.md b/docs/license-report.md
new file mode 100644
--- /dev/null
+++ b/docs/license-report.md
@@ -0,0 +1,42 @@
+**NB**: This captures the licenses associated with a particular set of dependency versions. If your own build solves differently, it’s possible that the licenses may have changed, or even that the set of dependencies itself is different. Please make sure you run [`cabal-plan license-report`](https://hackage.haskell.org/package/cabal-plan) on your own components rather than assuming this is authoritative.
+
+# Dependency License Report
+
+Bold-faced **`package-name`**s denote standard libraries bundled with `ghc-9.8.1`.
+
+## Direct dependencies of `no-recursion:lib:no-recursion`
+
+| Name | Version | [SPDX](https://spdx.org/licenses/) License Id | Description | Also depended upon by |
+| --- | --- | --- | --- | --- |
+| **`base`** | [`4.19.0.0`](http://hackage.haskell.org/package/base-4.19.0.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/base-4.19.0.0/src/LICENSE) | Basic libraries | *(core library)* |
+| **`ghc`** | [`9.8.1`](http://hackage.haskell.org/package/ghc-9.8.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-9.8.1/src/LICENSE) | The GHC API |  |
+
+## Indirect transitive dependencies
+
+| Name | Version | [SPDX](https://spdx.org/licenses/) License Id | Description | Depended upon by |
+| --- | --- | --- | --- | --- |
+| **`array`** | [`0.5.6.0`](http://hackage.haskell.org/package/array-0.5.6.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/array-0.5.6.0/src/LICENSE) | Mutable and immutable arrays | `binary`, `containers`, `deepseq`, `ghc`, `ghci`, `stm` |
+| **`binary`** | [`0.8.9.1`](http://hackage.haskell.org/package/binary-0.8.9.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/binary-0.8.9.1/src/LICENSE) | Binary serialisation for Haskell values using lazy ByteStrings | `ghc`, `ghc-boot`, `ghci` |
+| **`bytestring`** | [`0.12.0.2`](http://hackage.haskell.org/package/bytestring-0.12.0.2) | [`BSD-3-Clause`](http://hackage.haskell.org/package/bytestring-0.12.0.2/src/LICENSE) | Fast, compact, strict and lazy byte strings with a list interface | `binary`, `filepath`, `ghc`, `ghc-boot`, `ghci`, `unix` |
+| **`containers`** | [`0.6.8`](http://hackage.haskell.org/package/containers-0.6.8) | [`BSD-3-Clause`](http://hackage.haskell.org/package/containers-0.6.8/src/LICENSE) | Assorted concrete container types | `binary`, `ghc`, `ghc-boot`, `ghc-heap`, `ghci`, `hpc` |
+| **`deepseq`** | [`1.5.0.0`](http://hackage.haskell.org/package/deepseq-1.5.0.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/deepseq-1.5.0.0/src/LICENSE) | Deep evaluation of data structures | `bytestring`, `containers`, `filepath`, `ghc`, `ghc-boot`, `ghci`, `hpc`, `pretty`, `process`, `time` |
+| **`directory`** | [`1.3.8.1`](http://hackage.haskell.org/package/directory-1.3.8.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/directory-1.3.8.1/src/LICENSE) | Platform-agnostic library for filesystem operations | `ghc`, `ghc-boot`, `hpc`, `process` |
+| **`exceptions`** | [`0.10.7`](http://hackage.haskell.org/package/exceptions-0.10.7) | [`BSD-3-Clause`](http://hackage.haskell.org/package/exceptions-0.10.7/src/LICENSE) | Extensible optionally-pure exceptions | `filepath`, `ghc`, `semaphore-compat` |
+| **`filepath`** | [`1.4.100.4`](http://hackage.haskell.org/package/filepath-1.4.100.4) | [`BSD-3-Clause`](http://hackage.haskell.org/package/filepath-1.4.100.4/src/LICENSE) | Library for manipulating FilePaths in a cross platform way. | `directory`, `ghc`, `ghc-boot`, `ghci`, `hpc`, `process`, `unix` |
+| **`ghc-bignum`** | [`1.3`](http://hackage.haskell.org/package/ghc-bignum-1.3) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-bignum-1.3/src/LICENSE) | GHC BigNum library | `base` |
+| **`ghc-boot`** | [`9.8.1`](http://hackage.haskell.org/package/ghc-boot-9.8.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-boot-9.8.1/src/LICENSE) | Shared functionality between GHC and its boot libraries | `ghc`, `ghci` |
+| **`ghc-boot-th`** | [`9.8.1`](http://hackage.haskell.org/package/ghc-boot-th-9.8.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-boot-th-9.8.1/src/LICENSE) | Shared functionality between GHC and the @template-haskell@ library | `ghc-boot`, `template-haskell` |
+| **`ghc-heap`** | [`9.8.1`](http://hackage.haskell.org/package/ghc-heap-9.8.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-heap-9.8.1/src/LICENSE) | Functions for walking GHC's heap | `ghc`, `ghci` |
+| **`ghc-prim`** | [`0.11.0`](http://hackage.haskell.org/package/ghc-prim-0.11.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/ghc-prim-0.11.0/src/LICENSE) | GHC primitives | *(core library)* |
+| **`ghci`** | [`9.8.1`](http://hackage.haskell.org/package/ghci-9.8.1) |  *MISSING* | *MISSING* | `ghc` |
+| **`hpc`** | [`0.7.0.0`](http://hackage.haskell.org/package/hpc-0.7.0.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/hpc-0.7.0.0/src/LICENSE) | Code Coverage Library for Haskell | `ghc` |
+| **`mtl`** | [`2.3.1`](http://hackage.haskell.org/package/mtl-2.3.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/mtl-2.3.1/src/LICENSE) | Monad classes for transformers, using functional dependencies | `exceptions` |
+| **`pretty`** | [`1.1.3.6`](http://hackage.haskell.org/package/pretty-1.1.3.6) | [`BSD-3-Clause`](http://hackage.haskell.org/package/pretty-1.1.3.6/src/LICENSE) | Pretty-printing library | `template-haskell` |
+| **`process`** | [`1.6.18.0`](http://hackage.haskell.org/package/process-1.6.18.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/process-1.6.18.0/src/LICENSE) | Process libraries | `ghc` |
+| **`semaphore-compat`** | [`1.0.0`](http://hackage.haskell.org/package/semaphore-compat-1.0.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/semaphore-compat-1.0.0) | Cross-platform abstraction for system semaphores | `ghc` |
+| **`stm`** | [`2.5.2.1`](http://hackage.haskell.org/package/stm-2.5.2.1) | [`BSD-3-Clause`](http://hackage.haskell.org/package/stm-2.5.2.1/src/LICENSE) | Software Transactional Memory | `exceptions`, `ghc` |
+| **`template-haskell`** | [`2.21.0.0`](http://hackage.haskell.org/package/template-haskell-2.21.0.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/template-haskell-2.21.0.0/src/LICENSE) | Support library for Template Haskell | `bytestring`, `containers`, `exceptions`, `filepath`, `ghc`, `ghci` |
+| **`time`** | [`1.12.2`](http://hackage.haskell.org/package/time-1.12.2) | [`BSD-2-Clause`](http://hackage.haskell.org/package/time-1.12.2/src/LICENSE) | A time library | `directory`, `ghc`, `hpc`, `unix` |
+| **`transformers`** | [`0.6.1.0`](http://hackage.haskell.org/package/transformers-0.6.1.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/transformers-0.6.1.0/src/LICENSE) | Concrete functor and monad transformers | `exceptions`, `ghc`, `ghci`, `mtl` |
+| **`unix`** | [`2.8.3.0`](http://hackage.haskell.org/package/unix-2.8.3.0) | [`BSD-3-Clause`](http://hackage.haskell.org/package/unix-2.8.3.0/src/LICENSE) | POSIX functionality | `directory`, `ghc`, `ghc-boot`, `ghci`, `process`, `semaphore-compat` |
+
diff --git a/no-recursion.cabal b/no-recursion.cabal
--- a/no-recursion.cabal
+++ b/no-recursion.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: no-recursion
-version: 0.1.1.0
+version: 0.1.2.0
 synopsis: A GHC plugin to remove support for recursion
 description: General recursion can be the cause of a lot of problems. This
              removes recursion from GHC, allowing you to guarantee you’re using
@@ -18,6 +18,7 @@
   LICENSE
 extra-doc-files:
   README.md
+  docs/*.md
 tested-with:
   GHC == {
 --  GHCup   Nixpkgs
@@ -32,35 +33,41 @@
     9.2.1,  9.2.8,
     9.4.1,  9.4.8,
     9.6.1,  9.6.2,
-            9.8.1
+            9.8.1,
+    9.10.1
   }
 
 source-repository head
   type: git
   location: https://github.com/sellout/no-recursion
 
--- This mimics the GHC2021 extension
--- (https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/control.html?highlight=doandifthenelse#extension-GHC2021),
+-- This mimics the GHC2024 extension
+-- (https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/control.html?highlight=doandifthenelse#extension-GHC2024),
 -- but supporting compilers back to GHC 7.10. If the oldest supported compiler
--- is GHC 9.2, then this stanza can be removed and `import: GHC2021` can be
--- replaced by `default-language: GHC2021`.
-common GHC2021
+-- is GHC 9.10, then this stanza can be removed and `import: GHC2024` can be
+-- replaced by `default-language: GHC2024`. If the oldest supported compiler is
+-- GHC 9.2, then this can be simplified by setting `default-language: GHC2021`
+-- and only including the extensions added by GHC2024.
+common GHC2024
   default-language: Haskell2010
   default-extensions:
     BangPatterns
     BinaryLiterals
     ConstraintKinds
+    DataKinds
     DeriveDataTypeable
     DeriveGeneric
     -- DeriveLift -- uncomment if the oldest supported version is GHC 8.10.1+
     DeriveTraversable
     -- DerivingStrategies -- uncomment if the oldest supported version is GHC 8.2.1+
+    DisambiguateRecordFields
     DoAndIfThenElse
     EmptyCase
     ExistentialQuantification
+    ExplicitNamespaces
     FlexibleContexts
     FlexibleInstances
-    GADTSyntax
+    GADTs
     GeneralizedNewtypeDeriving
     -- HexFloatLiterals -- uncomment if the oldest supported version is GHC 8.4.1+
     -- ImportQualifiedPost -- uncomment if the oldest supported version is GHC 8.10.1+
@@ -76,6 +83,7 @@
     PolyKinds
     PostfixOperators
     RankNTypes
+    RoleAnnotations
     ScopedTypeVariables
     StandaloneDeriving
     -- StandaloneKindSignatures -- uncomment if the oldest supported version is GHC 8.10.1+
@@ -95,9 +103,9 @@
     that’s deprecated.
 
 common defaults
-  import: GHC2021
+  import: GHC2024
   build-depends:
-    base ^>= {4.8.2, 4.9.1, 4.10.1, 4.11.0, 4.12.0, 4.13.0, 4.14.0, 4.15.0, 4.16.0, 4.17.0, 4.18.0, 4.19.0},
+    base ^>= {4.8.2, 4.9.1, 4.10.1, 4.11.0, 4.12.0, 4.13.0, 4.14.0, 4.15.0, 4.16.0, 4.17.0, 4.18.0, 4.19.0, 4.20.0},
   ghc-options:
     -fpackage-trust
     -trust base
@@ -136,9 +144,11 @@
       -Wno-missing-poly-kind-signatures
       -- Inference good.
       -Wno-missing-role-annotations
+  if impl(ghc >= 9.10.1)
+    ghc-options:
+      -trust ghc-internal
   default-extensions:
     DefaultSignatures
-    ExplicitNamespaces
     FunctionalDependencies
     LiberalTypeSynonyms
     -- replace with `LexicalNegation` if the oldest supported version is GHC 9.0.1+
@@ -165,38 +175,47 @@
 custom-setup
   setup-depends:
     -- TODO: Remove `Cabal` dep once haskell/cabal#3751 is fixed.
-    Cabal ^>= {3.0.0, 3.2.0, 3.4.0, 3.6.0, 3.8.0, 3.10.0},
-    base ^>= {4.8.2, 4.9.1, 4.10.1, 4.11.0, 4.12.0, 4.13.0, 4.14.0, 4.15.0, 4.16.0, 4.17.0, 4.18.0, 4.19.0},
+    Cabal ^>= {3.0.0, 3.2.0, 3.4.0, 3.6.0, 3.8.0, 3.10.0, 3.12.0},
+    base ^>= {4.8.2, 4.9.1, 4.10.1, 4.11.0, 4.12.0, 4.13.0, 4.14.0, 4.15.0, 4.16.0, 4.17.0, 4.18.0, 4.19.0, 4.20.0},
     cabal-doctest ^>= {1.0.0},
 
 library
   import: defaults
   hs-source-dirs: src
   build-depends:
-    ghc ^>= {7.10.3, 8.0.2, 8.2.2, 8.4.1, 8.6.1, 8.8.1, 8.10.1, 9.0.1, 9.2.1, 9.4.1, 9.6.1, 9.8.1},
+    ghc ^>= {7.10.3, 8.0.2, 8.2.2, 8.4.1, 8.6.1, 8.8.1, 8.10.1, 9.0.1, 9.2.1, 9.4.1, 9.6.1, 9.8.1, 9.10.1},
   if impl(ghc < 8.0.1)
     build-depends:
       semigroups ^>= {0.20},
     ghc-options:
       -trust containers
       -trust semigroups
+  if impl(ghc >= 9.10.1)
+    ghc-options:
+      -trust ghc-internal
   exposed-modules:
     NoRecursion
 
 test-suite doctests
   import: defaults
-  type: exitcode-stdio-1.0
   hs-source-dirs: tests
-  main-is: doctests.hs
-  build-depends:
-    doctest ^>= {0.16.0, 0.18.1, 0.20.1, 0.21.1, 0.22.2},
-    no-recursion,
+  -- doctest doesn’t yet support GHC 9.10
+  if impl(ghc < 9.10.1)
+    type: exitcode-stdio-1.0
+    main-is: doctests.hs
+    build-depends:
+      doctest ^>= {0.16.0, 0.18.1, 0.20.1, 0.21.1, 0.22.2},
+      no-recursion,
+  else
+    type: exitcode-stdio-1.0
+    main-is: skipped.hs
   -- TODO: The sections below here are necessary because we don’t have control
   --       over the generated `Build_doctests.hs` file. So we have to silence
   --       all of its warnings one way or another.
   if impl(ghc >= 8.0.1)
     ghc-options:
       -Wno-missing-import-lists
+      -- This used to warn even when `Safe` was explicit.
       -Wno-safe
   else
     build-depends:
@@ -210,7 +229,6 @@
       -Wno-missing-export-lists
   if impl(ghc >= 8.8.1)
     ghc-options:
-      -- This used to warn even when `Safe` was explicit.
       -Wno-missing-deriving-strategies
   default-extensions:
     -- Since we can’t add `{-# LANGUAGE Safe -#}` to the generated
diff --git a/src/NoRecursion.hs b/src/NoRecursion.hs
--- a/src/NoRecursion.hs
+++ b/src/NoRecursion.hs
@@ -45,7 +45,7 @@
 defaultPurePlugin = Plugins.defaultPlugin
 #endif
 
--- | The entrypoint for the `NoRecursion` plugin.
+-- | The entrypoint for the "NoRecursion" plugin.
 plugin :: Plugins.Plugin
 plugin = defaultPurePlugin {Plugins.installCoreToDos = \_opts -> pure . install}
 
diff --git a/tests/doctests.hs b/tests/doctests.hs
deleted file mode 100644
--- a/tests/doctests.hs
+++ /dev/null
@@ -1,14 +0,0 @@
-{-# LANGUAGE Unsafe #-}
-
-module Main (main) where
-
--- NB: This unqualified module comes from semigroups in GHC <8, and base
---     otherwise.
-import safe Data.Semigroup (Semigroup ((<>)))
-import safe "base" Data.Function (($))
-import safe "base" System.IO (IO)
-import "doctest" Test.DocTest (doctest)
-import "this" Build_doctests (flags, module_sources, pkgs)
-
-main :: IO ()
-main = doctest $ flags <> pkgs <> module_sources
diff --git a/tests/skipped.hs b/tests/skipped.hs
new file mode 100644
--- /dev/null
+++ b/tests/skipped.hs
@@ -0,0 +1,13 @@
+-- __NB__: This module is /not/ @Unsafe@, but because of how it’s conditionally
+--         compiled with doctests.hs, Hlint and Ormolu get confused if it’s
+--         marked @Safe@.
+{-# LANGUAGE Unsafe #-}
+
+-- | This module allow us to skip test suites entirely for certain system
+--   configurations.
+module Main (main) where
+
+import System.IO (IO, putStrLn)
+
+main :: IO ()
+main = putStrLn "Skipped doctests suite on this system."
