diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,6 @@
+# Changelog
+
+## 0.1
+- initial release
+- uses aeson, ansi-terminal, time, typed-process
+- reads image json from remote registry with skopeo and extracts creation time
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018-2021,2023 Jens Petersen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# skopedate
+
+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
+
+Check/compares dates of container images for Fedora, Centos, etc, using skopeo.
+Currently the list of predefined registries is hardcoded.
+
+## Usage
+```
+$ skopedate --version
+0.1
+$ skopedate --help
+Checks dates of latest container images
+
+Usage: skopedate [--version] [-d|--debug] IMAGE
+
+  A tool for seeing the dates of latest container images in registries
+
+Available options:
+  -h,--help                Show this help text
+  --version                Show version
+  -d,--debug               show debug output
+```
+
+```
+$ skopedate fedora:39
+registry.fedoraproject.org           2023-08-11 17:33:49 +0800
+candidate-registry.fedoraproject.org 2023-08-11 17:33:49 +0800
+quay.io/fedora                       2023-08-11 17:33:49 +0800
+docker.io                            2023-08-05 02:23:06 +0800
+```
+
+```
+$ skopedate centos/centos:stream9
+quay.io 2023-08-08 11:39:14 +0800
+```
+
+```
+$ skopedate fedora-toolbox:39
+registry.fedoraproject.org           2023-08-11 17:33:52 +0800
+candidate-registry.fedoraproject.org 2023-08-11 17:33:52 +0800
+```
+
+## Installation
+
+`stack install` or `cabal install`
+
+## Contribute
+skopedate is distributed under the MIT license.
+
+See https://github.com/juhp/skopedate
diff --git a/skopedate.cabal b/skopedate.cabal
new file mode 100644
--- /dev/null
+++ b/skopedate.cabal
@@ -0,0 +1,66 @@
+name:                skopedate
+version:             0.1
+synopsis:            Check dates of container images
+description:         This tool checks the dates of a container image in
+                     registries.
+license:             MIT
+license-file:        LICENSE
+author:              Jens Petersen <juhpetersen@gmail.com>
+maintainer:          Jens Petersen <juhpetersen@gmail.com>
+copyright:           2018-2021,2023 Jens Petersen
+category:            Utility
+homepage:            https://github.com/juhp/skopedate
+bug-reports:         https://github.com/juhp/skopedate/issues
+build-type:          Simple
+extra-doc-files:     README.md
+                     CHANGELOG.md
+cabal-version:       2.0
+tested-with:         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.5, GHC == 9.6.2
+
+source-repository head
+  type:                git
+  location:            https://github.com/juhp/skopedate.git
+
+executable skopedate
+  main-is:             src/Main.hs
+  autogen-modules:       Paths_skopedate
+  other-modules:       Paths_skopedate
+
+  build-depends:       aeson
+                     , base < 5
+                     , bytestring
+                     , extra
+                     , http-query
+                     , simple-cmd >= 0.2.1
+                     , simple-cmd-args
+                     , time
+                     , typed-process
+  default-language:    Haskell2010
+
+  ghc-options:         -Wall
+  if impl(ghc >= 8.0)
+    ghc-options:       -Wcompat
+                       -Widentities
+                       -Wincomplete-uni-patterns
+                       -Wincomplete-record-updates
+                       -Wredundant-constraints
+  if impl(ghc >= 8.2)
+    ghc-options:       -fhide-source-paths
+  if impl(ghc >= 8.4)
+    ghc-options:       -Wmissing-export-lists
+                       -Wpartial-fields
+  if impl(ghc >= 8.10)
+    ghc-options:       -Wunused-packages
+
+test-suite test
+    main-is: tests.hs
+    type: exitcode-stdio-1.0
+    hs-source-dirs: test
+
+    default-language: Haskell2010
+
+    ghc-options:   -Wall
+    build-depends: base >= 4 && < 5,
+                   simple-cmd
+    build-tool-depends: skopedate:skopedate
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE CPP, OverloadedStrings #-}
+
+module Main (main) where
+
+import Control.Monad.Extra (when)
+import Data.Aeson (decode)
+import qualified Data.ByteString.Lazy.Char8 as B
+import Data.List.Extra (breakOn, isPrefixOf)
+import Data.Time.Clock (UTCTime)
+import Data.Time.Format (defaultTimeLocale, formatTime)
+import Data.Time.LocalTime (utcToLocalZonedTime)
+import Network.HTTP.Query (lookupKey, (+/+))
+import SimpleCmd (needProgram, warning)
+import SimpleCmdArgs (simpleCmdArgs, switchWith, strArg)
+import System.IO (hFlush, stdout)
+import System.Process.Typed (proc, readProcessStdout,
+#if MIN_VERSION_typed_process(0,2,8)
+                             ExitCode(ExitSuccess)
+#endif
+                            )
+#if !MIN_VERSION_typed_process(0,2,8)
+import System.Exit (ExitCode(ExitSuccess))
+#endif
+
+import Paths_skopedate
+
+main :: IO ()
+main = do
+  simpleCmdArgs (Just version) "Checks dates of latest container images"
+    "A tool for seeing the dates of latest container images in registries" $
+    checkRegistries
+    <$> switchWith 'd' "debug" "show debug output"
+    <*> strArg "IMAGE"
+
+imageRegistries :: String -> [String]
+imageRegistries image =
+  let (untagged,_tag) = breakOn ":" image
+  in
+    case filter (\(i,_) -> i `isPrefixOf` untagged) matchOS of
+      (_,rs):_ -> rs
+      [] ->
+        case breakOn "/" untagged of
+          (_, "") -> ["docker.io"]
+          (before,_after) ->
+            ["docker.io" | '.' `notElem` before]
+  where
+    matchOS :: [(String,[String])]
+    matchOS = [("fedora-toolbox",["registry.fedoraproject.org",
+                                  "candidate-registry.fedoraproject.org"]),
+               ("fedora",["registry.fedoraproject.org",
+                          "candidate-registry.fedoraproject.org",
+                          "quay.io/fedora",
+                          "docker.io"]),
+               ("centos", ["quay.io"]),
+               ("ubi", ["registry.access.redhat.com"]),
+               ("opensuse", ["registry.opensuse.org", "docker.io"])]
+
+checkRegistries :: Bool -> String -> IO ()
+checkRegistries debug image = do
+  needProgram "skopeo"
+  case imageRegistries image of
+    [] -> skopeoInspectTimeRel "" (length image) image
+    regs ->
+      let width = maximum $ map length regs
+      in mapM_ (skopeoInspectTimeRel image width) regs
+  where
+    skopeoInspectTimeRel :: String -> Int -> String -> IO ()
+    skopeoInspectTimeRel img width reg = do
+      let ref = "docker://" ++ reg +/+ img
+      putStr $ if debug
+               then ref
+               else reg ++ replicate (width - length reg + 1) ' '
+      hFlush stdout
+      (ok,out) <- readProcessStdout $ proc "skopeo" ["inspect", ref]
+      when (ok == ExitSuccess) $ do
+        when debug $ do
+          putChar '\n'
+          B.putStrLn out
+        case parseTimeRel out of
+          Just res -> printTime res
+          Nothing -> warning "image not found"
+        where
+          parseTimeRel :: B.ByteString -> Maybe (UTCTime, Maybe String)
+          parseTimeRel bs = do
+            obj <- decode bs
+            utc <- lookupKey "Created" obj
+            let mlabels = lookupKey "Labels" obj
+            return (utc, mlabels >>= lookupKey "release")
+
+          printTime (u,mr) = do
+            t <- utcToLocalZonedTime u
+            putStrLn $ formatTime defaultTimeLocale "%Y-%m-%d %H:%M:%S %z" t ++ maybeRel mr
+
+          maybeRel :: Maybe String -> String
+          maybeRel Nothing = ""
+          maybeRel (Just r) =
+            " rel:" ++ (if length r < 2 then " " else "") ++ r
diff --git a/test/tests.hs b/test/tests.hs
new file mode 100644
--- /dev/null
+++ b/test/tests.hs
@@ -0,0 +1,23 @@
+import SimpleCmd
+import System.IO
+
+program :: String -> IO ()
+program image =
+  putStrLn ("\n# " ++ image) >>
+  cmd_ "skopedate" [image]
+
+images :: [String]
+images =
+  [ "fedora:39"
+  , "centos/centos:stream9"
+  , "fedora-toolbox:39"
+  , "ubi9"
+  , "opensuse/tumbleweed"
+  , "docker.io/library/alpine"
+  ]
+
+main :: IO ()
+main = do
+  hSetBuffering stdout NoBuffering
+  mapM_ program images
+  putStrLn $ "\n" ++ show (length images) ++ " command tests run"
