diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,17 @@
-## [_Unreleased_](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.3...main)
+## [_Unreleased_](https://github.com/freckle/aws-sns-verify/compare/v0.1.0.0...main)
+
+## [v0.1.0.0](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.3...v0.1.0.0)
+
+- Replace `memory` with `ram` for `crypton-1.1`
+
+- Raise a slew of lower bounds, to accomplish the above
+
+- Drop support for GHCs lower than 9.4, to accomplish the above
+
+- Return more specific errors in URI validation
+
+  Rather than respond `BadUri` for non-URI, unexpected scheme, or invalid domain
+  name, we now have 3 distinct error constructors for each of those.
 
 ## [v0.0.0.3](https://github.com/freckle/aws-sns-verify/compare/v0.0.0.2...v0.0.0.3)
 
diff --git a/aws-sns-verify.cabal b/aws-sns-verify.cabal
--- a/aws-sns-verify.cabal
+++ b/aws-sns-verify.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               aws-sns-verify
-version:            0.0.0.3
+version:            0.1.0.0
 license:            MIT
 license-file:       LICENSE
 copyright:          2022 Freckle By Renaissance
@@ -59,18 +59,18 @@
         -Wno-all-missed-specialisations
 
     build-depends:
-        aeson,
+        aeson >=2.2.5.0,
         base >=4.7 && <5,
-        bytestring,
-        crypton-x509,
-        crypton-x509-validation,
-        errors,
-        http-conduit,
-        memory,
-        network-uri,
-        pem,
-        regex-tdfa,
-        text
+        bytestring >=0.12.2.0,
+        crypton-x509 >=1.9.0,
+        crypton-x509-validation >=1.9.0,
+        errors >=2.3.0,
+        http-conduit >=2.3.9.1,
+        network-uri >=2.6.4.2,
+        pem >=0.2.4,
+        ram >=0.22.0,
+        regex-tdfa >=1.3.2.5,
+        text >=2.1.3
 
     if impl(ghc >=8.10)
         ghc-options:
@@ -110,17 +110,17 @@
         -Wno-all-missed-specialisations -threaded -rtsopts -with-rtsopts=-N
 
     build-depends:
-        aeson-qq,
-        async,
+        aeson-qq >=0.8.4,
+        async >=2.2.6,
         aws-sns-verify,
         base >=4.7 && <5,
-        crypton-x509-validation,
-        hspec,
-        http-types,
-        regex-tdfa,
-        text,
-        wai,
-        warp
+        crypton-x509-validation >=1.9.1,
+        hspec >=2.11.17,
+        http-types >=0.12.5,
+        regex-tdfa >=1.3.2.5,
+        text >=2.1.3,
+        wai >=3.2.4,
+        warp >=3.4.9
 
     if impl(ghc >=8.10)
         ghc-options:
diff --git a/library/Amazon/SNS/Verify/Prelude.hs b/library/Amazon/SNS/Verify/Prelude.hs
--- a/library/Amazon/SNS/Verify/Prelude.hs
+++ b/library/Amazon/SNS/Verify/Prelude.hs
@@ -6,7 +6,7 @@
 import Prelude as X
 
 import Control.Error (ExceptT, throwE)
-import Control.Exception as X (Exception)
+import Control.Exception as X (Exception (..))
 import qualified Control.Exception
 import Control.Monad as X (join, (<=<))
 import Control.Monad.IO.Class as X (MonadIO, liftIO)
diff --git a/library/Amazon/SNS/Verify/Validate.hs b/library/Amazon/SNS/Verify/Validate.hs
--- a/library/Amazon/SNS/Verify/Validate.hs
+++ b/library/Amazon/SNS/Verify/Validate.hs
@@ -10,7 +10,7 @@
 import Amazon.SNS.Verify.Payload
 import Amazon.SNS.Verify.ValidURI (validRegPattern, validScheme)
 import Control.Error (ExceptT, catMaybes, headMay, runExceptT, throwE)
-import Control.Monad (when)
+import Control.Monad (unless, when)
 import Data.ByteArray.Encoding (Base (Base64), convertFromBase)
 import Data.PEM (pemContent, pemParseLBS)
 import qualified Data.Text as T
@@ -79,22 +79,27 @@
   => SNSPayload
   -> ExceptT SNSNotificationValidationError m SignedCertificate
 retrieveCertificate SNSPayload {..} = do
-  certUrlStr <- unTryE id $ validateCertUrl snsSigningCertURL
+  certUrlStr <- validateCertUrl snsSigningCertURL
   response <- httpLbs $ parseRequest_ certUrlStr
   pems <- unTryE BadPem $ pemParseLBS $ getResponseBody response
   cert <-
     fromMaybeM (throwE $ BadPem "Empty List") $ pemContent <$> headMay pems
   unTryE BadCert $ decodeSignedCertificate cert
 
-validateCertUrl :: Text -> Either SNSNotificationValidationError String
+validateCertUrl
+  :: Monad m => Text -> ExceptT SNSNotificationValidationError m String
 validateCertUrl certUrl = do
-  uri <- fromMaybeM (Left $ BadUri certUrlStr) $ parseURI certUrlStr
-  if uriScheme uri
-    == validScheme
-    && maybe "" uriRegName (uriAuthority uri)
-    =~ validRegPattern
-    then Right certUrlStr
-    else Left $ BadUri certUrlStr
+  uri <- fromMaybeM (throwE $ BadUri certUrlStr) $ parseURI certUrlStr
+
+  let
+    scheme = uriScheme uri
+    mDomain = uriRegName <$> uriAuthority uri
+
+  unless (scheme == validScheme) $ throwE $ BadScheme scheme validScheme
+  domain <- fromMaybeM (throwE $ NoAuthority certUrlStr) mDomain
+  unless (domain =~ validRegPattern) $ throwE $ BadDomain domain validRegPattern
+
+  pure certUrlStr
  where
   certUrlStr = T.unpack certUrl
 
@@ -146,6 +151,9 @@
 data SNSNotificationValidationError
   = BadPem String
   | BadUri String
+  | BadScheme String String
+  | NoAuthority String
+  | BadDomain String String
   | BadSignature String
   | BadCert String
   | BadJSONParse String
@@ -155,4 +163,17 @@
   | UnsubscribeMessage
   | SubscribeMessageResponded
   deriving stock (Show, Eq)
-  deriving anyclass (Exception)
+
+instance Exception SNSNotificationValidationError where
+  displayException = \case
+    BadScheme actual expected ->
+      "BadScheme, expected "
+        <> expected
+        <> " but got "
+        <> actual
+    BadDomain actual regex ->
+      "BadDomain, "
+        <> actual
+        <> " does not match the regular expression "
+        <> regex
+    ex -> show ex
diff --git a/tests/Amazon/SNS/Verify/ValidateSpec.hs b/tests/Amazon/SNS/Verify/ValidateSpec.hs
--- a/tests/Amazon/SNS/Verify/ValidateSpec.hs
+++ b/tests/Amazon/SNS/Verify/ValidateSpec.hs
@@ -102,7 +102,7 @@
                               "SynthesisTaskNotification { TaskId: 680a1f1b-f3ae-4474-aa8f-3b6dfe52e656, Status: COMPLETED }"
                         }
                 }
-      go `shouldReturn` Left (BadUri "http://attacker.com/evil.pem")
+      go `shouldReturn` Left (BadDomain "attacker.com" "^localhost$")
 
     it "successfully validates an SNS subscription" $ do
       -- pendingWith "need valid subscription payload"
