diff --git a/gradle.properties b/gradle.properties
index 3db4c9ef5130cb536f024d384d06a2402c1a2751..508138283bd312a1b9f16170b13f483f90aeb813 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,7 +10,7 @@ micronautVersion = 3.2.3
 micronautRxVersion = 2.1.1
 nodePluginVersion = 3.0.1
 nodeVersion = 16.3.0
-nshmpLibVersion = 1.1.2
+nshmpLibVersion = 1.1.3
 nshmpWsUtilsVersion = 0.3.9
 openApiVersion = 4.0.0
 shadowVersion = 7.1.2
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java b/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java
index 950887ee013a5a5f0e6eecc918f36b0119d3296d..c98ce87010fdb04c897227cfe35d88ebf975834d 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/DisaggCalc.java
@@ -129,7 +129,7 @@ public class DisaggCalc {
       fh.setFormatter(new Logging.ConsoleFormatter());
       log.getParent().addHandler(fh);
 
-      log.info(PROGRAM + ": " + HazardCalc.VERSION);
+      log.info(PROGRAM + " version: " + HazardCalc.VERSION);
       Path modelPath = Paths.get(args[0]);
       HazardModel model = HazardModel.load(modelPath);
 
@@ -535,7 +535,7 @@ public class DisaggCalc {
 
   private static final String USAGE = new StringBuilder()
       .append(NEWLINE)
-      .append(PROGRAM).append(" [").append(HazardCalc.VERSION).append("]").append(NEWLINE)
+      .append(PROGRAM).append(" version:").append(HazardCalc.VERSION)
       .append(NEWLINE)
       .append("Usage:").append(NEWLINE)
       .append("  ").append(USAGE_COMMAND).append(NEWLINE)
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/HazardCalc.java b/src/main/java/gov/usgs/earthquake/nshmp/HazardCalc.java
index 0133cb4e7135d4c262721e67ebed7b2af6127e29..5e665d15c1ce2e1052f3ddb73f4d31d808103dc2 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/HazardCalc.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/HazardCalc.java
@@ -29,9 +29,11 @@ import gov.usgs.earthquake.nshmp.calc.HazardCalcs;
 import gov.usgs.earthquake.nshmp.calc.HazardExport;
 import gov.usgs.earthquake.nshmp.calc.Site;
 import gov.usgs.earthquake.nshmp.calc.Sites;
+import gov.usgs.earthquake.nshmp.internal.AppVersion.VersionInfo;
 import gov.usgs.earthquake.nshmp.internal.Logging;
 import gov.usgs.earthquake.nshmp.model.HazardModel;
 import gov.usgs.earthquake.nshmp.model.SiteData;
+import gov.usgs.earthquake.nshmp.www.HazVersion;
 
 /**
  * Compute probabilisitic seismic hazard from a {@link HazardModel}.
@@ -94,7 +96,7 @@ public class HazardCalc {
       fh.setFormatter(new Logging.ConsoleFormatter());
       log.getParent().addHandler(fh);
 
-      log.info(PROGRAM + ": " + VERSION);
+      log.info(PROGRAM + " version: " + VERSION);
       Path modelPath = Paths.get(args[0]);
       HazardModel model = HazardModel.load(modelPath);
 
@@ -252,8 +254,18 @@ public class HazardCalc {
     return Optional.of(sb.toString());
   }
 
-  /** The Git application version. */
-  public static final String VERSION = "TODO get version from resource";
+  private static final VersionInfo[] versions = HazVersion.appVersions();
+  public static final StringBuilder VERSION = versions();
+
+  private static StringBuilder versions() {
+    StringBuilder sb = new StringBuilder().append(NEWLINE);
+    for (VersionInfo component : HazVersion.appVersions()) {
+      sb.append("  ").append(component.projectName)
+          .append(": ").append(component.version)
+          .append(NEWLINE);
+    }
+    return sb;
+  }
 
   private static final String PROGRAM = HazardCalc.class.getSimpleName();
   private static final String USAGE_COMMAND =
@@ -265,7 +277,7 @@ public class HazardCalc {
 
   private static final String USAGE = new StringBuilder()
       .append(NEWLINE)
-      .append(PROGRAM).append(" [").append(VERSION).append("]").append(NEWLINE)
+      .append(PROGRAM).append(" version:").append(VERSION)
       .append(NEWLINE)
       .append("Usage:").append(NEWLINE)
       .append("  ").append(USAGE_COMMAND).append(NEWLINE)
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/RateCalc.java b/src/main/java/gov/usgs/earthquake/nshmp/RateCalc.java
index c143872d92a9848130164b9e9ee00a08c174bc5e..6c2f651eab405400afb16d2cfb3489b5b2e03873 100644
--- a/src/main/java/gov/usgs/earthquake/nshmp/RateCalc.java
+++ b/src/main/java/gov/usgs/earthquake/nshmp/RateCalc.java
@@ -96,7 +96,7 @@ public class RateCalc {
       fh.setFormatter(new Logging.ConsoleFormatter());
       log.getParent().addHandler(fh);
 
-      log.info(PROGRAM + ": " + HazardCalc.VERSION);
+      log.info(PROGRAM + " version: " + HazardCalc.VERSION);
       Path modelPath = Paths.get(args[0]);
       HazardModel model = HazardModel.load(modelPath);
 
@@ -222,7 +222,7 @@ public class RateCalc {
 
   private static final String USAGE = new StringBuilder()
       .append(NEWLINE)
-      .append(PROGRAM).append(" [").append(HazardCalc.VERSION).append("]").append(NEWLINE)
+      .append(PROGRAM).append(" version:").append(HazardCalc.VERSION)
       .append(NEWLINE)
       .append("Usage:").append(NEWLINE)
       .append("  ").append(USAGE_COMMAND).append(NEWLINE)
diff --git a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Versions.java b/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Versions.java
deleted file mode 100644
index b7925784e50d530b26e7b5175fd8ef45bcbc82b5..0000000000000000000000000000000000000000
--- a/src/main/java/gov/usgs/earthquake/nshmp/www/meta/Versions.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package gov.usgs.earthquake.nshmp.www.meta;
-
-import java.io.InputStream;
-import java.util.Map;
-import java.util.Properties;
-
-import com.google.common.collect.ImmutableMap;
-
-import gov.usgs.earthquake.nshmp.HazardCalc;
-
-/*
- * Application and model version data. References are string-based as opposed to
- * enum-based (e.g. Edition) to avoid circular references in enum
- * initializations.
- */
-class Versions {
-
-  static final String NSHMP_HAZ_VERSION = HazardCalc.VERSION;
-  private static final Map<String, String> MODEL_VERSIONS;
-  private static final String UNKNOWN = "unknown";
-
-  static {
-    ImmutableMap.Builder<String, String> modelMap = ImmutableMap.builder();
-
-    /* Always runs from a war (possibly unpacked). */
-    try (InputStream in = Metadata.class.getResourceAsStream("/service.properties")) {
-      Properties props = new Properties();
-      props.load(in);
-      in.close();
-
-      for (String key : props.stringPropertyNames()) {
-        String value = props.getProperty(key);
-        /* Model versions. */
-        modelMap.put(key, value);
-      }
-    } catch (Exception e) {
-      /* Do nothing; probably running outside standard build. */
-    }
-
-    MODEL_VERSIONS = modelMap.build();
-  }
-
-  static String modelVersion(String id) {
-    return MODEL_VERSIONS.getOrDefault(id + ".version", UNKNOWN);
-  }
-
-}