diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
 # Changelog
 
+## 1.0 (2023-09-15)
+- default to download.fp.o: replace --no-dl with --latest
+- new --check (-c) command mode: checks for newer image
+- --local mode now only derefs symlink to show latest local image
+- help: list ostree editions last
+- add Onyx (F39 Budgie ostree image)
+- allow "sb" as alias for Silverblue
+- offer to fix permissions of a partially downloaded run iso file
+
 ## 0.9.6 (2023-08-18)
 - --no-dl option to avoid dl.fp.o even if newer
 - support c8s and add options for centos-stream channels
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -20,8 +20,13 @@
 
 `dl-fedora --run 39` : will download Fedora Workstation and boot the Live image with qemu-kvm.
 
-`dl-fedora --local rawhide` : shows the current locally available image (as well as the latest one). With `--dryrun` it doesn't check for newest iso.
+`dl-fedora --check respin` : checks if there is a newer respin iso image
+available.
 
+`dl-fedora --local rawhide` : shows the current locally available image.
+It can be combined with `--run` to quickly run the latest local image,
+without a newer download.
+
 `dl-fedora c9s` : downloads a Centos Stream 9 net installer.
 
 By default dl-fedora downloads to `~/Downloads/`
@@ -29,8 +34,11 @@
 but if you create an `iso` subdirectory there (`~/Downloads/iso/`)
 it will use that directory instead.
 
-`dl-fedora` downloads the latest image redirected from `download.fedoraproject.org` by default, after checking for the latest image at `dl.fedoraproject.org`.
-However if the mirror is not synced yet it will download from `dl.fedoraproject.org` instead. You can use `--no-dl` to avoid checking and downloading from the main dl.fedoraproject.org mirror (in this case a nightly image might not be the very latest).
+`dl-fedora` downloads the latest mirrored image redirected from
+`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 the image is already found to be downloaded
 it will not be re-downloaded of course.
@@ -44,40 +52,42 @@
 ## Usage
 ```shellsession
 $ dl-fedora --version
-0.9.6
+1.0
 $ dl-fedora --help
 Fedora iso downloader
 
-Usage: dl-fedora [--version] [-g|--gpg-keys]
-                 [(-C|--no-checksum) | (-c|--checksum)] [-n|--dry-run] [--debug]
-                 [-T|--no-http-timeout] [-l|--local] [-r|--run] [-R|--replace]
-                 [(-d|--dl) | (-D|--no-dl) | (-k|--koji) | (-m|--mirror URL)]
+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)]
                  [--cs-devel | --cs-test] [-a|--arch ARCH] RELEASE [EDITION]
 
   Tool for downloading Fedora iso file images.
   RELEASE = release number, respin, rawhide, test (Beta), stage (RC), eln, c8s, c9s
-  EDITION = {cloud,container,everything,server,workstation,silverblue,kinoite,
-             sericea,budgie,cinnamon,i3,kde,lxde,lxqt,mate,soas,sway,
-             xfce} [default: workstation]
+  EDITION = {cloud,container,everything,server,workstation,budgie,cinnamon,i3,
+             kde,lxde,lxqt,mate,soas,sway,xfce,silverblue,kinoite,onyx,
+             sericea} [default: workstation]
 
-  See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>
-  and also <https://fedoramagazine.org/verify-fedora-iso-file>.
+  See <https://github.com/juhp/dl-fedora/#readme>
 
 Available options:
   -h,--help                Show this help text
   --version                Show version
   -g,--gpg-keys            Import Fedora GPG keys for verifying checksum file
-  -C,--no-checksum         Do not check checksum
-  -c,--checksum            Do checksum even if already downloaded
-  -n,--dry-run             Don't actually download anything
+  --no-checksum            Do not check checksum
+  --checksum               Do checksum even if already downloaded
   --debug                  Debug output
   -T,--no-http-timeout     Do not timeout for http response
-  -l,--local               Show current local image via symlink
-  -r,--run                 Boot image in Qemu
+  -c,--check               Check if newer image available
+  -l,--local               Show current local image
   -R,--replace             Delete previous snapshot image after downloading
                            latest one
+  -n,--dry-run             Don't actually download anything
+  -r,--run                 Boot image in QEMU
+  -L,--latest              Get latest image either from mirror or dl.fp.o if
+                           newer
   -d,--dl                  Use dl.fedoraproject.org (dl.fp.o)
-  -D,--no-dl               Do not use dl.fp.o (even if newer)
   -k,--koji                Use koji.fedoraproject.org
   -m,--mirror URL          Mirror url for /pub [default
                            https://download.fedoraproject.org/pub]
@@ -87,7 +97,12 @@
   -a,--arch ARCH           Specify arch [default: x86_64]
 ```
 
+## References
+See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>,
+<https://admin.fedoraproject.org/mirrormanager/>,
+and also <https://fedoramagazine.org/verify-fedora-iso-file>.
+
 ## Contribution
-dl-fedora is distributed under the GPL 3 license or later.
+dl-fedora is distributed under the GPL license version 3 or later.
 
 Please report issues or pull requests at <https://github.com/juhp/dl-fedora>.
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:             0.9.6
+version:             1.0
 synopsis:            Fedora image download tool
 description:
             Tool to download Fedora and Centos Stream iso and image files.
@@ -16,8 +16,9 @@
 build-type:          Simple
 extra-doc-files:     README.md
                    , CHANGELOG.md
-tested-with:         GHC == 9.4.5, GHC == 9.2.8, GHC == 9.0.2, GHC == 8.10.7,
-                     GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4, GHC == 8.2.2
+tested-with:         GHC == 9.6.2, GHC == 9.4.7, GHC == 9.2.8, GHC == 9.0.2,
+                     GHC == 8.10.7, GHC == 8.8.4, GHC == 8.6.5, GHC == 8.4.4,
+                     GHC == 8.2.2
 
 source-repository head
   type:     git
@@ -41,8 +42,9 @@
                        http-types,
                        optparse-applicative,
                        regex-posix,
-                       simple-cmd >= 0.2.0,
+                       simple-cmd >= 0.2.7,
                        simple-cmd-args >= 0.1.8,
+                       simple-prompt >= 0.2,
                        text,
                        time,
                        unix,
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -29,8 +29,9 @@
 import Paths_dl_fedora (version)
 
 import SimpleCmd (cmd, cmd_, cmdN, error', grep_, pipe_, pipeBool, pipeFile_,
-                  {-removePrefix,-} warning, (+-+))
+                  sudoLog, warning, (+-+))
 import SimpleCmdArgs
+import SimplePrompt (yesNo)
 
 import System.Directory (createDirectory, doesDirectoryExist, doesFileExist,
                          findExecutable, getPermissions,
@@ -40,6 +41,7 @@
                         (</>), (<.>))
 import System.Posix.Files (createSymbolicLink, fileSize, getFileStatus,
                            readSymbolicLink)
+import System.Posix.User (getLoginName)
 
 import Text.Read
 import qualified Text.ParserCombinators.ReadP as R
@@ -54,10 +56,7 @@
                    | Everything
                    | Server
                    | Workstation
-                   | Silverblue
-                   | Kinoite
-                   | Sericea
-                   | Budgie  -- first spin used below
+                   | Budgie  -- first spin: used below
                    | Cinnamon
                    | I3
                    | KDE
@@ -66,7 +65,11 @@
                    | MATE
                    | SoaS
                    | Sway
-                   | Xfce
+                   | Xfce  -- last spin: used below
+                   | Silverblue
+                   | Kinoite
+                   | Onyx
+                   | Sericea
  deriving (Show, Enum, Bounded, Eq)
 
 showEdition :: FedoraEdition -> String
@@ -82,6 +85,7 @@
     s <- look
     let e = lower s
         editionMap =
+          ("sb", Silverblue) :
           map (\ ed -> (lowerEdition ed, ed)) [minBound..maxBound]
         res = lookup e editionMap
     case res of
@@ -91,7 +95,7 @@
 type URL = String
 
 fedoraSpins :: [FedoraEdition]
-fedoraSpins = [Budgie ..]
+fedoraSpins = [Budgie .. Xfce]
 
 data CheckSum = AutoCheckSum | NoCheckSum | CheckSum
   deriving Eq
@@ -115,6 +119,8 @@
 showChannel CSTest = "test"
 showChannel CSDevelopment = "development"
 
+data Mode = Check | Local | Download Bool -- replace
+
 main :: IO ()
 main = do
   let pdoc = Just $ P.vcat
@@ -122,20 +128,20 @@
                P.text ("RELEASE = " <> intercalate ", " ["release number", "respin", "rawhide", "test (Beta)", "stage (RC)", "eln", "c8s", "c9s"]),
                P.text "EDITION = " <> P.lbrace <> P.align (P.fillCat (P.punctuate P.comma (map (P.text . lowerEdition) [(minBound :: FedoraEdition)..maxBound])) <> P.rbrace) <> P.text " [default: workstation]" ,
                P.text "",
-               P.text "See <https://fedoraproject.org/wiki/Infrastructure/MirrorManager>",
-               P.text "and also <https://fedoramagazine.org/verify-fedora-iso-file>."
+               P.text "See <https://github.com/juhp/dl-fedora/#readme>"
              ]
   sysarch <- readArch <$> cmd "rpm" ["--eval", "%{_arch}"]
   simpleCmdArgsWithMods (Just version) (fullDesc <> header "Fedora iso downloader" <> progDescDoc pdoc) $
     program
     <$> switchWith 'g' "gpg-keys" "Import Fedora GPG keys for verifying checksum file"
     <*> checkSumOpts
-    <*> switchWith 'n' "dry-run" "Don't actually download anything"
     <*> switchLongWith "debug" "Debug output"
     <*> switchWith 'T' "no-http-timeout" "Do not timeout for http response"
-    <*> switchWith 'l' "local" "Show current local image via symlink"
-    <*> switchWith 'r' "run" "Boot image in Qemu"
-    <*> switchWith 'R' "replace" "Delete previous snapshot image after downloading latest one"
+    <*> (flagWith' Check 'c' "check" "Check if newer image available" <|>
+         flagWith' Local 'l' "local" "Show current local image" <|>
+         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
     <*> (flagLongWith' CSDevelopment "cs-devel" "Use centos-stream development compose" <|>
          flagLongWith CSProduction CSTest "cs-test" "Use centos-stream test compose (default is production)")
@@ -145,25 +151,25 @@
   where
     mirrorOpt :: Parser Mirror
     mirrorOpt =
+      flagWith' DefaultLatest 'L' "latest" "Get latest image either from mirror or dl.fp.o if newer" <|>
       flagWith' DlFpo 'd' "dl" "Use dl.fedoraproject.org (dl.fp.o)" <|>
-      flagWith' UseMirror 'D' "no-dl" "Do not use dl.fp.o (even if newer)" <|>
       flagWith' KojiFpo 'k' "koji" "Use koji.fedoraproject.org" <|>
       Mirror <$> strOptionWith 'm' "mirror" "URL" ("Mirror url for /pub [default " ++ downloadFpo ++ "]") <|>
-      pure DefaultLatest
+      pure UseMirror
 
     checkSumOpts :: Parser CheckSum
     checkSumOpts =
-      flagWith' NoCheckSum 'C' "no-checksum" "Do not check checksum" <|>
-      flagWith AutoCheckSum CheckSum 'c' "checksum" "Do checksum even if already downloaded"
+      flagLongWith' NoCheckSum "no-checksum" "Do not check checksum" <|>
+      flagLongWith AutoCheckSum CheckSum "checksum" "Do checksum even if already downloaded"
 
 data Primary = Primary {primaryUrl :: String,
                         primarySize :: Maybe Integer,
                         primaryTime :: Maybe UTCTime}
 
-program :: Bool -> CheckSum -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool
+program :: Bool -> CheckSum -> Bool -> Bool -> Mode -> Bool -> Bool
         -> Mirror -> CentosChannel -> Arch -> String -> FedoraEdition
         -> IO ()
-program gpg checksum dryrun debug notimeout local run removeold mirror channel arch tgtrel edition = do
+program gpg checksum debug notimeout mode dryrun run mirror channel arch tgtrel edition = do
   let mirrorUrl =
         case mirror of
           Mirror m -> m
@@ -178,38 +184,50 @@
   mgr <- if notimeout
          then newManager (tlsManagerSettings {managerResponseTimeout = responseTimeoutNone})
          else httpManager
-  if local
-    then do
-    symlink <-
-      if dryrun
-      then do
-      filePrefix <- getFilePrefix showdestdir
-      -- FIXME support non-iso
-      return $ filePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> "iso"
-      else do
-      (fileurl, filenamePrefix, prime, _mchecksum, _done) <-
-        findURL mgr mirrorUrl showdestdir
-      tz <- getCurrentTimeZone
-      putStrLn $ unwords ["Newest:", takeFileName fileurl, renderTime tz (primaryTime prime)]
-      putStrLn fileurl
-      return $ filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
-    if run
-      then bootImage symlink showdestdir
-      else showSymlink symlink showdestdir
-    else do
-    (fileurl, filenamePrefix, prime, mchecksum, done) <- findURL mgr mirrorUrl showdestdir
-    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
-    unless dryrun $ do
-      let localfile = takeFileName fileurl
-      updateSymlink localfile symlink showdestdir
-      when run $ bootImage localfile showdestdir
+  case mode of
+    Check -> do
+      (fileurl, filenamePrefix, _prime, _mchecksum, done) <-
+        findURL mgr mirrorUrl showdestdir True
+      if done
+        then do
+        putStrLn $ "Local:" +-+ takeFileName fileurl +-+ "is latest"
+        when debug $
+          putStrLn fileurl
+        else do
+        let symlink = filenamePrefix <> (if tgtrel == "eln" then "-" <> showArch arch else "") <> "-latest" <.> takeExtension fileurl
+        mtarget <- derefSymlink symlink
+        case mtarget of
+          Just target -> do
+            if target == takeFileName fileurl
+              then putStrLn $ "Latest:" +-+ target +-+ "(locally incomplete)"
+              else do
+              putStrLn $ "Local:" +-+ target
+              putStrLn $ "Newer:" +-+ takeFileName fileurl
+          Nothing -> putStrLn $ "Available:" +-+ takeFileName fileurl
+    Local -> do
+      prefix <- getFilePrefix showdestdir
+        -- FIXME support non-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
+    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
+      downloadFile dryrun debug done mgr fileurl prime showdestdir >>=
+        fileChecksum mgr mchecksum showdestdir
+      unless dryrun $ do
+        let localfile = takeFileName fileurl
+        updateSymlink localfile symlink showdestdir removeold
+        when run $ bootImage dryrun localfile showdestdir
   where
-    findURL :: Manager -> String -> String
+    findURL :: Manager -> String -> String -> Bool
             -- urlpath, fileprefix, primary, checksum, downloaded
             -> IO (URL, String, Primary, Maybe String, Bool)
-    findURL mgr mirrorUrl showdestdir = do
+    findURL mgr mirrorUrl showdestdir quiet = do
       (path,mrelease) <- urlPathMRel mgr
       -- use http-directory trailingSlash (0.1.7)
       let primaryDir =
@@ -218,7 +236,7 @@
               "eln" -> odcsFpo
               "c8s" -> odcsStream
               "c9s" -> odcsStream
-              _ -> if mirror == DefaultLatest
+              _ -> if mirror `elem` [DefaultLatest, DlFpo]
                    then dlFpo
                    else downloadFpo
             +/+ path <> "/"
@@ -241,12 +259,19 @@
             exists <- doesFileExist localfile
             if exists
               then do
-              done <- checkLocalFileSize localfile primeSize primeTime showdestdir
+              done <- checkLocalFileSize localfile primeSize primeTime showdestdir quiet
               if done
                 then return (primeUrl,True)
                 else do
-                unlessM (writable <$> getPermissions localfile) $
-                  error' $ localfile <> " does have write permission, aborting!"
+                unlessM (writable <$> getPermissions localfile) $ do
+                  putStrLn $ localfile <> " does not have write permission!"
+                  yes <- yesNo "Fix ownership"
+                  if yes
+                    then do
+                    user <- getLoginName
+                    sudoLog "chown" [user,localfile]
+                    else
+                    error' $ localfile <> " does have write permission, aborting!"
                 findMirror primeUrl path file
               else findMirror primeUrl path file
           let finalDir = dropFileName finalurl
@@ -301,14 +326,14 @@
         _ -> error' $ tgtrel ++ " is unsupported with --dryrun"
 
     checkLocalFileSize :: FilePath -> Maybe Integer -> Maybe UTCTime
-                       -> String -> IO Bool
-    checkLocalFileSize localfile mprimeSize mprimeTime showdestdir = do
+                       -> String -> Bool -> IO Bool
+    checkLocalFileSize localfile mprimeSize mprimeTime showdestdir quiet = do
       localsize <- toInteger . fileSize <$> getFileStatus localfile
       if Just localsize == mprimeSize
         then do
-        when (not run && takeExtension localfile == ".iso") $ do
+        unless quiet $ do
           tz <- getCurrentTimeZone
-          -- FIXME abbreviate size
+          -- FIXME abbreviate size?
           putStrLn $ unwords [showdestdir </> localfile, renderTime tz mprimeTime, "size " ++ show localsize ++ " okay"]
         return True
         else do
@@ -442,7 +467,7 @@
       if not exists then return False
         else do
         (primeSize,primeTime) <- httpFileSizeTime mgr url
-        ok <- checkLocalFileSize checksumfile primeSize primeTime showdestdir
+        ok <- checkLocalFileSize checksumfile primeSize primeTime showdestdir True
         unless ok $ error' $ "Checksum file filesize mismatch for " ++ checksumfile
         return ok
 
@@ -450,8 +475,8 @@
     checkForFedoraKeys =
       pipeBool ("gpg",["--list-keys"]) ("grep", ["-q", " Fedora .*(" <> tgtrel <> ").*@fedoraproject.org>"])
 
-    updateSymlink :: FilePath -> FilePath -> FilePath -> IO ()
-    updateSymlink target symlink showdestdir = do
+    updateSymlink :: FilePath -> FilePath -> FilePath -> Bool -> IO ()
+    updateSymlink target symlink showdestdir removeold = do
       mmsymlinkTarget <- do
         havefile <- doesFileExist symlink
         if havefile
@@ -476,17 +501,12 @@
           createSymbolicLink target symlink
           putStrLn $ unwords [showdestdir </> symlink, "->", target]
 
-    showSymlink :: FilePath -> FilePath -> IO ()
-    showSymlink symlink showdestdir = do
-      msymlinkTarget <- do
-        havefile <- doesFileExist symlink
-        if havefile
-          then Just <$> readSymbolicLink symlink
-          else return Nothing
-      case msymlinkTarget of
-        Just symlinktarget ->
-          putStrLn $ "Local: " ++ showdestdir </> symlinktarget
-        _ -> return ()
+    derefSymlink :: FilePath -> IO (Maybe FilePath)
+    derefSymlink symlink = do
+      havefile <- doesFileExist symlink
+      if havefile
+        then Just <$> readSymbolicLink symlink
+        else return Nothing
 
 renderTime :: TimeZone -> Maybe UTCTime -> String
 renderTime tz mprimeTime =
@@ -522,6 +542,7 @@
 editionType :: FedoraEdition -> String
 editionType Server = "dvd"
 editionType Kinoite = "ostree"
+editionType Onyx = "ostree"
 editionType Sericea = "ostree"
 editionType Silverblue = "ostree"
 editionType Everything = "netinst"
@@ -537,8 +558,8 @@
 liveRespin :: FedoraEdition -> String
 liveRespin = take 4 . upper . showEdition
 
-bootImage :: FilePath -> String -> IO ()
-bootImage img showdir = do
+bootImage :: Bool -> FilePath -> String -> IO ()
+bootImage dryrun img showdir = do
   let fileopts =
         case takeExtension img of
           ".iso" -> ["-boot", "d", "-cdrom"]
@@ -548,7 +569,8 @@
     Just qemu -> do
       let args = ["-m", "2048", "-rtc", "base=localtime", "-cpu", "host"] ++ fileopts
       cmdN qemu (args ++ [showdir </> img])
-      cmd_ qemu (args ++ [img])
+      unless dryrun $
+        cmd_ qemu (args ++ [img])
     Nothing -> error' "Need qemu to run image"
 
 #if !MIN_VERSION_http_directory(0,1,6)
diff --git a/test/tests.hs b/test/tests.hs
--- a/test/tests.hs
+++ b/test/tests.hs
@@ -16,21 +16,21 @@
 
 tests :: Bool -> [[String]]
 tests ghAction =
-  [["-n", previous, "-c"]
+  [["-n", previous, "--checksum"]
   ,["-n", "rawhide", "silverblue"]
-  ,["-n", "respin"]
+  ,["-c", "respin"]
   ,["-l", current]
   ,["-l", "rawhide", "-n"]
   ] ++
   if ghAction then []
   else
-    [["-n", current, "silverblue"]
+    [["-c", current, "silverblue"]
     ,["-n", previous, "kde"]
     ,["-T", "-n", current, "everything"]
     ,["-n", previous, "server", "--arch", "aarch64"]
     ,["-n", "c8s"]
     ,["-n", "c9s"]
-    ,["-n", "eln"]
+    ,["-c", "eln"]
     ]
 
 main :: IO ()
