diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # Changelog
 
+## 1.2 (2024-09-17)
+- default centos-stream to mirror.stream.centos.org
+- initial support for CS Alternative Live respins
+- refactoring for new Release type
+- new --list command to list all spins/editions
+- checksum now only runs for downloaded (specified) image
+- fix fedora.gpg url
+- prompt to retry (continue) download if curl errors
+- --debug: output redirects, including http -> https
+
 ## 1.1 (2024-05-23)
 - add c10s and Fedora IoT edition
 - print multiple matches and download latest
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -12,13 +12,13 @@
 
 `dl-fedora rawhide` : downloads the latest Fedora Rawhide Workstation Live iso
 
-`dl-fedora 40 silverblue` : downloads the Fedora Silverblue iso
+`dl-fedora 41 silverblue` : downloads the Fedora Silverblue iso
 
 `dl-fedora respin kde` : downloads the latest KDE Live respin
 
-`dl-fedora 39 server --arch aarch64` : will download the Server iso for armv8
+`dl-fedora 40 server --arch aarch64` : will download the Server iso for armv8
 
-`dl-fedora --run 40` : will download Fedora Workstation and boot the Live image with qemu-kvm.
+`dl-fedora --run 41` : will download Fedora Workstation and boot the Live image with qemu-kvm.
 
 `dl-fedora --check respin` : checks if there is a newer respin iso image
 available.
@@ -38,7 +38,7 @@
 `download.fedoraproject.org` by default.
 If you want to ensure getting the very latest image you can use `--latest`,
 which will then download from `dl.fedoraproject.org` instead
-_if_ your mirror is not synced yet. (This behavior changed with 0.10.)
+_if_ your mirror is not synced yet.
 
 If the image is already found to be downloaded
 it will not be re-downloaded of course.
@@ -53,27 +53,28 @@
 `$ dl-fedora --version`
 
 ```
-1.1
+1.2
 ```
 `$ dl-fedora --help`
 
 ```
 Fedora iso downloader
 
-Usage: dl-fedora [--version] [-g|--gpg-keys] [--no-checksum | --checksum] 
-                 [--debug] [-T|--no-http-timeout] 
-                 [(-c|--check) | (-l|--local) | (-R|--replace)] [-n|--dry-run] 
-                 [-r|--run] 
-                 [(-L|--latest) | (-d|--dl) | (-k|--koji) | (-m|--mirror URL)] 
-                 [--dvd] [--cs-devel | --cs-test] [-a|--arch ARCH] RELEASE 
-                 [EDITION]
+Usage: dl-fedora [--version] [-g|--gpg-keys] [--no-checksum | --checksum]
+                 [--debug] [-T|--no-http-timeout]
+                 [(-c|--check) | (-l|--local) | --list | (-R|--replace)]
+                 [-n|--dry-run] [-r|--run]
+                 [(-L|--latest) | (-d|--dl) | (-k|--koji) | (-m|--mirror URL)]
+                 [--dvd] [--cs-live-respin]
+                 [--cs-devel | --cs-test | --cs-production] [-a|--arch ARCH]
+                 RELEASE [EDITION]
 
   Tool for downloading Fedora iso file images.
   RELEASE = release number, respin, rawhide, test (Beta), stage (RC), eln, c9s, c10s
   EDITION = {cloud,container,everything,server,workstation,budgie,cinnamon,i3,
              kde,lxde,lxqt,mate,soas,sway,xfce,silverblue,kinoite,onyx,sericea,
              iot} [default: workstation]
-  
+
   See <https://github.com/juhp/dl-fedora/#readme>
 
 Available options:
@@ -86,6 +87,7 @@
   -T,--no-http-timeout     Do not timeout for http response
   -c,--check               Check if newer image available
   -l,--local               Show current local image
+  --list                   List spins and editions
   -R,--replace             Delete previous snapshot image after downloading
                            latest one
   -n,--dry-run             Don't actually download anything
@@ -98,9 +100,11 @@
                            https://download.fedoraproject.org/pub]
   --dvd                    Download dvd iso instead of boot netinst (for Server,
                            eln, centos)
+  --cs-live-respin         Centos Stream Alternative Live image
   --cs-devel               Use centos-stream development compose
-  --cs-test                Use centos-stream test compose (default is
-                           production)
+  --cs-test                Use centos-stream test compose
+  --cs-production          Use centos-stream production compose (default is
+                           mirror.stream.centos.org)
   -a,--arch ARCH           Specify arch [default: x86_64]
 ```
 
diff --git a/dl-fedora.cabal b/dl-fedora.cabal
--- a/dl-fedora.cabal
+++ b/dl-fedora.cabal
@@ -1,6 +1,6 @@
 cabal-version:       1.18
 name:                dl-fedora
-version:             1.1
+version:             1.2
 synopsis:            Fedora image download tool
 description:
             Tool to download Fedora and Centos Stream iso and image files.
@@ -9,16 +9,16 @@
 license-file:        LICENSE
 homepage:            https://github.com/juhp/dl-fedora
 bug-reports:         https://github.com/juhp/dl-fedora/issues
-author:              Jens Petersen
-maintainer:          juhpetersen@gmail.com
+author:              Jens Petersen <juhpetersen@gmail.com>
+maintainer:          Jens Petersen <juhpetersen@gmail.com>
 copyright:           2019-2024  Jens Petersen
 category:            Utility
 build-type:          Simple
 extra-doc-files:     README.md
-                   , CHANGELOG.md
+                     CHANGELOG.md
 tested-with:         GHC == 8.2.2, GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4,
                      GHC == 8.10.7, GHC == 9.0.2, GHC == 9.2.8, GHC == 9.4.8,
-                     GHC == 9.6.5, GHC == 9.8.2
+                     GHC == 9.6.6, GHC == 9.8.2
 
 source-repository head
   type:     git
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -23,16 +23,17 @@
                             responseTimeoutNone)
 import Network.HTTP.Client.TLS
 import Network.HTTP.Directory
+import Numeric.Natural
 
 import Options.Applicative (fullDesc, header, progDescDoc)
 
 import Paths_dl_fedora (version)
 
-import SimpleCmd (cmd, cmd_, cmdLog_, cmdN, error', grep_,
+import SimpleCmd (cmd, cmd_, cmdBool, cmdN, error', grep_, logMsg,
                   pipe_, pipeBool, pipeFile_,
                   sudoLog, warning, (+-+))
 import SimpleCmdArgs
-import SimplePrompt (yesNo)
+import SimplePrompt (yesNo, yesNoDefault)
 
 import System.Directory (createDirectory, doesDirectoryExist, doesFileExist,
                          findExecutable, getPermissions,
@@ -102,13 +103,15 @@
 data CheckSum = AutoCheckSum | NoCheckSum | CheckSum
   deriving Eq
 
-dlFpo, downloadFpo, kojiPkgs, odcsFpo, csComposes, odcsStream :: String
-dlFpo = "https://dl.fedoraproject.org/pub"
-downloadFpo = "https://download.fedoraproject.org/pub"
+dlFpo, downloadFpo, kojiPkgs, odcsFpo, csComposes, odcsStream, csMirror
+  :: String
+dlFpo = "https://dl.fedoraproject.org/pub" -- main repo
+downloadFpo = "https://download.fedoraproject.org/pub" -- mirror redirect
 kojiPkgs = "https://kojipkgs.fedoraproject.org/compose"
 odcsFpo = "https://odcs.fedoraproject.org/composes"
 csComposes = "https://composes.stream.centos.org"
-odcsStream = "https://odcs.stream.centos.org"
+odcsStream = "https://odcs.stream.centos.org" -- no cdn
+csMirror = "https://mirror.stream.centos.org"
 
 data Mirror = DlFpo | UseMirror | KojiFpo | Mirror String | DefaultLatest
   deriving Eq
@@ -121,8 +124,38 @@
 showChannel CSTest = "test"
 showChannel CSDevelopment = "development"
 
-data Mode = Check | Local | Download Bool -- replace
+data Release = Fedora Natural
+             | FedoraRespin
+             | Rawhide
+             | FedoraTest
+             | FedoraStage
+             | ELN
+             | CS Natural
+  deriving Eq
 
+readRelease :: String -> Release
+readRelease rel =
+  case lower rel of
+    'f': n@(_:_) | all isDigit n -> Fedora (read n)
+    n@(_:_) | all isDigit n ->
+              let v = read n in
+                case compare v 11 of
+                  LT -> CS v
+                  EQ -> ELN
+                  GT -> Fedora v
+    -- FIXME hardcoding
+    "c8s" -> CS 8
+    "c9s" -> CS 9
+    "c10s" -> CS 10
+    "respin" -> FedoraRespin
+    "rawhide" -> Rawhide
+    "test" -> FedoraTest
+    "stage" -> FedoraStage
+    "eln" -> ELN
+    _ -> error' "unknown release"
+
+data Mode = Check | Local | List | Download Bool -- replace
+
 main :: IO ()
 main = do
   let pdoc = Just $ P.vcat
@@ -136,20 +169,23 @@
   simpleCmdArgsWithMods (Just version) (fullDesc <> header "Fedora iso downloader" <> progDescDoc pdoc) $
     program
     <$> switchWith 'g' "gpg-keys" "Import Fedora GPG keys for verifying checksum file"
-    <*> checkSumOpts
+    <*> checksumOpts
     <*> switchLongWith "debug" "Debug output"
     <*> switchWith 'T' "no-http-timeout" "Do not timeout for http response"
     <*> (flagWith' Check 'c' "check" "Check if newer image available" <|>
          flagWith' Local 'l' "local" "Show current local image" <|>
+         flagLongWith' List "list" "List spins and editions" <|>
          Download <$> switchWith 'R' "replace" "Delete previous snapshot image after downloading latest one")
     <*> switchWith 'n' "dry-run" "Don't actually download anything"
     <*> switchWith 'r' "run" "Boot image in QEMU"
     <*> mirrorOpt
     <*> switchLongWith "dvd" "Download dvd iso instead of boot netinst (for Server, eln, centos)"
-    <*> (flagLongWith' CSDevelopment "cs-devel" "Use centos-stream development compose" <|>
-         flagLongWith CSProduction CSTest "cs-test" "Use centos-stream test compose (default is production)")
+    <*> switchLongWith "cs-live-respin" "Centos Stream Alternative Live image"
+    <*> optional (flagLongWith' CSDevelopment "cs-devel" "Use centos-stream development compose" <|>
+                  flagLongWith' CSTest "cs-test" "Use centos-stream test compose" <|>
+                  flagLongWith' CSProduction "cs-production" "Use centos-stream production compose (default is mirror.stream.centos.org)")
     <*> (optionWith (eitherReader eitherArch) 'a' "arch" "ARCH" ("Specify arch [default:" +-+ showArch sysarch ++ "]") <|> pure sysarch)
-    <*> strArg "RELEASE"
+    <*> (readRelease <$> strArg "RELEASE")
     <*> (fromMaybe Workstation <$> optional (argumentWith auto "EDITION"))
   where
     mirrorOpt :: Parser Mirror
@@ -160,8 +196,8 @@
       Mirror <$> strOptionWith 'm' "mirror" "URL" ("Mirror url for /pub [default " ++ downloadFpo ++ "]") <|>
       pure UseMirror
 
-    checkSumOpts :: Parser CheckSum
-    checkSumOpts =
+    checksumOpts :: Parser CheckSum
+    checksumOpts =
       flagLongWith' NoCheckSum "no-checksum" "Do not check checksum" <|>
       flagLongWith AutoCheckSum CheckSum "checksum" "Do checksum even if already downloaded"
 
@@ -170,18 +206,25 @@
                         primaryTime :: Maybe UTCTime}
 
 program :: Bool -> CheckSum -> Bool -> Bool -> Mode -> Bool -> Bool
-        -> Mirror -> Bool -> CentosChannel -> Arch -> String -> FedoraEdition
-        -> IO ()
-program gpg checksum debug notimeout mode dryrun run mirror dvdnet channel arch tgtrel edition = do
+        -> Mirror -> Bool -> Bool -> Maybe CentosChannel -> Arch -> Release
+        -> FedoraEdition -> IO ()
+program gpg checksum debug notimeout mode dryrun run mirror dvdnet cslive mchannel arch tgtrel edition = do
+  when (isJust mchannel && not (isCentosStream tgtrel)) $
+    error' "channels are only for centos-stream"
   let mirrorUrl =
         case mirror of
           Mirror m -> m
           KojiFpo -> kojiPkgs
           DlFpo -> dlFpo
-          _ -- UseMirror or DefaultLatest
-            | tgtrel == "eln" -> odcsFpo
-            | tgtrel `elem` ["c8s","c9s","c10s"] -> csComposes
-            | otherwise -> downloadFpo
+          -- UseMirror or DefaultLatest
+          _ ->
+            case tgtrel of
+              ELN -> odcsFpo
+              CS n | n >= 9 -> if isJust mchannel
+                               then csComposes
+                               else csMirror
+              CS 8 -> csComposes
+              _ -> downloadFpo
   showdestdir <- setDownloadDir dryrun "iso"
   when debug $ print showdestdir
   mgr <- if notimeout
@@ -196,7 +239,7 @@
         putStrLn $ "Local:" +-+ takeFileName fileurl +-+ "is latest"
         when debug $ print fileurl
         else do
-        let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
+        let symlink = filenamePrefix <> (if tgtrel == ELN then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
         mtarget <- derefSymlink symlink
         case mtarget of
           Just target -> do
@@ -209,18 +252,21 @@
     Local -> do
       prefix <- getFilePrefix showdestdir
         -- FIXME support non-iso
-      let symlink = prefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> "iso"
+      let symlink = prefix <> (if tgtrel == ELN then "-" <> showArch arch else "") <> "-latest" <.> "iso"
       mtarget <- derefSymlink symlink
       whenJust mtarget $ \target -> do
         putStrLn $ "Local:" +-+ target
         when run $
           bootImage dryrun target showdestdir
+    List ->
+      -- FIXME only list/check for editions for release
+      putStrLn $ (unwords . sort . map lowerEdition) [(minBound :: FedoraEdition)..maxBound]
     Download removeold -> do
       (fileurl, filenamePrefix, prime, mchecksum, done) <-
         findURL mgr mirrorUrl showdestdir False
-      let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
+      let symlink = filenamePrefix <> (if tgtrel == ELN then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
       downloadFile dryrun debug done mgr fileurl prime showdestdir >>=
-        fileChecksum mgr mchecksum showdestdir
+        fileChecksum mgr fileurl mchecksum showdestdir
       unless dryrun $ do
         let localfile = takeFileName fileurl
         updateSymlink localfile symlink showdestdir removeold
@@ -234,11 +280,12 @@
       let path' = trailingSlash path
       (primeDir,hrefs) <-
         case tgtrel of
-          "koji" -> getUrlDirectory kojiPkgs path'
-          "eln" -> getUrlDirectory odcsFpo path'
-          "c8s" -> getUrlDirectory odcsStream path'
-          "c9s" -> getUrlDirectory odcsStream path'
-          "c10s" -> getUrlDirectory odcsStream path'
+          -- "koji" -> getUrlDirectory kojiPkgs path'
+          ELN -> getUrlDirectory odcsFpo path'
+          CS n ->
+            if n < 9 || isJust mchannel
+            then getUrlDirectory odcsStream path'
+            else getUrlDirectory csMirror path'
           _ ->
             if mirror `elem` [DefaultLatest, DlFpo]
             then getUrlDirectory dlFpo path'
@@ -251,7 +298,7 @@
       let prefixPat = makeFilePrefix mrelease
           selector = if '*' `elem` prefixPat then (=~ prefixPat) else (prefixPat `isPrefixOf`)
           fileslen = groupSortOn length $ filter selector $ map T.unpack hrefs
-          mchecksum = find ((if tgtrel == "respin" then T.isPrefixOf else T.isSuffixOf) (T.pack "CHECKSUM")) hrefs
+          mchecksum = find ((if tgtrel == FedoraRespin then T.isPrefixOf else T.isSuffixOf) (T.pack "CHECKSUM")) hrefs
       if null fileslen
         then error' $ "no match for " <> prefixPat <> " in " <> primeDir
         else do
@@ -291,7 +338,10 @@
           getUrlDirectory :: String -> FilePath -> IO (String, [T.Text])
           getUrlDirectory top path = do
             let url = top +/+ path
-            when debug $ print url
+            when debug $ do
+              print url
+              redirs <- httpRedirects mgr $ replace "https:" "http:" url
+              print redirs
             ls <- httpDirectory mgr url
             return (url, ls)
 
@@ -336,13 +386,12 @@
     getRelease =
       case tgtrel of
         -- FIXME IoT uses version for instead of Rawhide
-        "rawhide" -> Just "Rawhide"
-        "respin" -> Nothing
-        "eln" -> Nothing
-        "c8s" -> Nothing
-        "c9s" -> Nothing
-        rel | all isDigit rel -> Just rel
-        _ -> error' $ tgtrel ++ " is unsupported with --dryrun"
+        Rawhide -> Just "Rawhide"
+        FedoraRespin -> Nothing
+        ELN -> Nothing
+        CS _ -> Nothing
+        Fedora rel -> Just (show rel)
+        _ -> error' "release target is unsupported with --dryrun"
 
     checkLocalFileSize :: Integer -> FilePath -> Maybe Integer -> Maybe UTCTime
                        -> String -> Bool -> IO Bool
@@ -370,20 +419,26 @@
             if edition `elem` fedoraSpins
             then joinPath ["Spins", showArch arch, "iso"]
             else joinPath [showEdition edition, showArch arch, editionMedia edition]
-      if edition == IoT && isFedora tgtrel
-        then return ("alt/iot" +/+ tgtrel +/+ subdir, Nothing)
-        else
-        case tgtrel of
-          "respin" -> return ("alt/live-respins", Nothing)
-          "rawhide" -> return ("fedora/linux/development/rawhide" +/+ subdir, Just "Rawhide")
-          "test" -> testRelease mgr subdir
-          "stage" -> stageRelease mgr subdir
-          "eln" -> return ("production/latest-Fedora-ELN/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
-          "c8s" -> return ("stream-8" +/+ showChannel channel +/+ "latest-CentOS-Stream/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
-          "c9s" -> return (showChannel channel +/+ "latest-CentOS-Stream/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
-          "c10s" -> return ("stream-10" +/+ showChannel channel +/+ "latest-CentOS-Stream/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
-          rel | all isDigit rel -> released mgr rel subdir
-          _ -> error' "Unknown release"
+      case tgtrel of
+        FedoraRespin -> return ("alt/live-respins", Nothing)
+        Rawhide -> return ("fedora/linux/development/rawhide" +/+ subdir, Just "Rawhide")
+        FedoraTest -> testRelease mgr subdir
+        FedoraStage -> stageRelease mgr subdir
+        ELN -> return ("production/latest-Fedora-ELN/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
+        CS 8 ->
+          return ("stream-8" +/+ showChannel (fromMaybe CSProduction mchannel) +/+ "latest-CentOS-Stream/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
+        CS n -> return $
+                case mchannel of
+                  Nothing ->
+                    if cslive
+                    then ("SIGs" +/+ show n ++ "-stream/altimages/images/live" +/+ showArch arch, Nothing)
+                    else (show n ++ "-stream" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
+                  Just channel ->
+                    ((if n == 9 then id else (("stream-" ++ show n) +/+)) $ showChannel channel +/+ "latest-CentOS-Stream/compose" +/+ "BaseOS" +/+ showArch arch +/+ "iso", Nothing)
+        Fedora n ->
+          if edition == IoT
+          then return ("alt/iot" +/+ show n +/+ subdir, Nothing)
+          else released mgr (show n) subdir
 
     testRelease :: Manager -> FilePath -> IO (FilePath, Maybe String)
     testRelease mgr subdir = do
@@ -432,11 +487,12 @@
     makeFilePrefix :: Maybe String -> String
     makeFilePrefix mrelease =
       case tgtrel of
-        "respin" -> "F[1-9][0-9]*-" <> liveRespin edition <> "-x86_64" <> "-LIVE"
-        "eln" -> "Fedora-ELN-.*" ++ renderdvdboot
-        "c8s" -> "CentOS-Stream-8-.*" ++ renderdvdboot
-        "c9s" -> "CentOS-Stream-9-.*" ++ renderdvdboot
-        "c10s" -> "CentOS-Stream-10-.*" ++ renderdvdboot
+        FedoraRespin -> "F[1-9][0-9]*-" <> liveRespin edition <> "-x86_64" <> "-LIVE"
+        ELN -> "Fedora-eln-.*" ++ renderdvdboot
+        CS n ->
+          if cslive
+          then "CentOS-Stream-Image-" ++ csLive edition ++ "-Live" <.> showArch arch ++ '-' : show n
+          else "CentOS-Stream-" ++ show n ++ "-.*" ++ renderdvdboot
         _ ->
           let showRel r = if last r == '/' then init r else r
               rel = maybeToList (showRel <$> mrelease)
@@ -462,16 +518,18 @@
     editionType Container = "Base-Generic"
     editionType _ = "Live"
 
-    fileChecksum :: Manager -> Maybe URL -> String -> Maybe Bool -> IO ()
-    fileChecksum _ Nothing _ _ = return ()
-    fileChecksum mgr (Just url) showdestdir mneedChecksum =
+    fileChecksum :: Manager -> String -> Maybe URL -> String -> Maybe Bool
+                 -> IO ()
+    fileChecksum _ _ Nothing _ _ = return ()
+    fileChecksum mgr imageurl (Just url) showdestdir mneedChecksum =
       when ((mneedChecksum == Just True && checksum /= NoCheckSum) || (isJust mneedChecksum && checksum == CheckSum)) $ do
         let checksumdir = ".dl-fedora-checksums"
-            checksumfile = checksumdir </> takeFileName url
+            checksumfilename = takeFileName url
+            checksumpath = checksumdir </> checksumfilename
         exists <- do
           dirExists <- doesDirectoryExist checksumdir
           if dirExists
-            then checkChecksumfile mgr url checksumfile showdestdir
+            then checkChecksumfile mgr url checksumpath showdestdir
             else createDirectory checksumdir >> return False
         putStrLn ""
         haveChksum <-
@@ -481,33 +539,43 @@
             remoteExists <- httpExists mgr url
             if remoteExists
               then do
-              withCurrentDirectory checksumdir $
-                curl debug $ (if debug then ["--silent", "--show-error"] else []) ++ ["--remote-name", url]
-              doesFileExist checksumfile
+              withCurrentDirectory checksumdir $ do
+                ok <- curl debug $ (if debug then ["--silent", "--show-error"] else []) ++ ["--remote-name", url]
+                unless ok $ error' $ "failed to download" +-+ url
+              doesFileExist checksumpath
               else return False
         if not haveChksum
           then putStrLn "No checksum file found"
           else do
-          pgp <- grep_ "PGP" checksumfile
+          pgp <- grep_ "PGP" checksumpath
           when (gpg && pgp) $ do
-            havekey <- checkForFedoraKeys
-            unless havekey $ do
-              putStrLn "Importing Fedora GPG keys:\n"
-              -- https://fedoramagazine.org/verify-fedora-iso-file/
-              pipe_ ("curl",["--silent", "--show-error", "https://getfedora.org/static/fedora.gpg"]) ("gpg",["--import"])
-              putStrLn ""
-          chkgpg <- if pgp
-            then checkForFedoraKeys
+            case tgtrel of
+              Fedora n -> do
+                havekey <- checkForFedoraKeys n
+                unless havekey $ do
+                  putStrLn "Importing Fedora GPG keys:\n"
+                  -- https://fedoramagazine.org/verify-fedora-iso-file/
+                  pipe_ ("curl",["--silent", "--show-error", "https://fedoraproject.org/fedora.gpg"]) ("gpg",["--import"])
+                  putStrLn ""
+              _ -> return ()
+          chkgpg <-
+            if pgp
+            then
+              case tgtrel of
+                Fedora n -> checkForFedoraKeys n
+                _ -> return False
             else return False
-          let shasum = if "CHECKSUM512" `isPrefixOf` takeFileName checksumfile
+          let shasum = if "CHECKSUM512" `isPrefixOf` checksumfilename
                        then "sha512sum" else "sha256sum"
           if chkgpg then do
             putStrLn $ "Running gpg verify and " <> shasum <> ":"
-            pipeFile_ checksumfile ("gpg",["-q"]) (shasum, ["-c", "--ignore-missing"])
+            pipeFile_ checksumpath ("gpg",["-q"]) (shasum, ["-c", "--ignore-missing"])
             else do
             putStrLn $ "Running " <> shasum <> ":"
             -- FIXME ignore other downloaded iso's (eg partial images error)
-            cmd_ shasum ["-c", "--ignore-missing", checksumfile]
+            pipe_
+              ("grep",[takeFileName imageurl,checksumpath])
+              (shasum,["-c","-"])
 
     checkChecksumfile :: Manager -> URL -> FilePath -> String -> IO Bool
     checkChecksumfile mgr url checksumfile showdestdir = do
@@ -522,9 +590,9 @@
         unless ok $ error' $ "Checksum file filesize mismatch for " ++ checksumfile
         return ok
 
-    checkForFedoraKeys :: IO Bool
-    checkForFedoraKeys =
-      pipeBool ("gpg",["--list-keys"]) ("grep", ["-q", " Fedora .*(" <> tgtrel <> ").*@fedoraproject.org>"])
+    checkForFedoraKeys :: Natural -> IO Bool
+    checkForFedoraKeys n =
+      pipeBool ("gpg",["--list-keys"]) ("grep", ["-q", " Fedora .*(" <> show n <> ").*@fedoraproject.org>"])
 
     updateSymlink :: FilePath -> FilePath -> FilePath -> Bool -> IO ()
     updateSymlink target symlink showdestdir removeold = do
@@ -585,8 +653,16 @@
       else do
       tz <- getCurrentTimeZone
       putStrLn $ unwords ["downloading", takeFileName url, renderTime tz mtime, "to", showdestdir]
-      curl debug ["--fail", "--remote-name", url]
+      doCurl
       return (Just True)
+  where
+    doCurl = do
+       ok <- curl debug ["--fail", "--remote-name", url]
+       unless ok $ do
+         yes <- yesNoDefault True "retry download"
+         if yes
+         then doCurl
+         else error' "download failed"
 
 editionMedia :: FedoraEdition -> String
 editionMedia Cloud = "images"
@@ -655,9 +731,22 @@
 showArch S390X = "s390x"
 showArch PPC64LE = "ppc64le"
 
-isFedora :: String -> Bool
-isFedora tgtrel = tgtrel == "rawhide" || all isDigit tgtrel
+curl :: Bool -> [String] -> IO Bool
+curl debug args = do
+  let opts = ["--location", "--continue-at", "-"]
+  when debug $
+    logMsg $ unwords $ "curl" : opts ++ args
+  cmdBool "curl" $ opts ++ args
 
-curl :: Bool -> [String] -> IO ()
-curl debug args =
-  (if debug then cmdLog_ else cmd_) "curl" $ ["--location", "--continue-at", "-"] ++ args
+isCentosStream :: Release -> Bool
+isCentosStream (CS _) = True
+isCentosStream _ = False
+
+-- FIXME support other editions: MAX, MIN
+csLive :: FedoraEdition -> String
+csLive Workstation = "GNOME"
+csLive Cinnamon = "CINNAMON"
+csLive KDE = "KDE"
+csLive MATE = "MATE"
+csLive Xfce = "XFCE"
+csLive ed = error' $ "unsupported edition:" +-+ showEdition ed
diff --git a/test/tests.hs b/test/tests.hs
--- a/test/tests.hs
+++ b/test/tests.hs
@@ -5,11 +5,11 @@
 dlFedora :: Bool -> [String] -> IO ()
 dlFedora ghAction args =
   putStrLn "" >> cmdLog "dl-fedora"
-  ((if ghAction then ("-T" :) else id) args)
+  ((if ghAction then ("-d" :) . ("-T" :) else id) args)
 
 -- FIXME automate me
 branched :: Int
-branched = 40
+branched = 41
 current, previous :: String
 current = show branched
 previous = show (branched - 1)
@@ -33,6 +33,7 @@
     ,["-n", "c9s", "--dvd"]
     ,["-n", "c10s"]
     ,["-c", "eln"]
+    ,["-n", "c9s", "--cs-live-respin"]
     ]
 
 main :: IO ()
