diff --git a/apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateHandler.java
index 46df8803915e772faba6ddb059160ab6575f4d86..d4068a111d9efeef32f39a8239c2ff80ba6be4a1 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/PluginUpdateHandler.java
@@ -9,6 +9,8 @@ import java.util.Properties;
 import net.i2p.router.RouterContext;
 import net.i2p.router.web.PluginStarter;
 import net.i2p.update.*;
+import net.i2p.util.Log;
+import net.i2p.util.SystemVersion;
 
 /**
  * Check for or download an updated version of a plugin.
@@ -27,12 +29,14 @@ import net.i2p.update.*;
 class PluginUpdateHandler implements Checker, Updater {
     private final RouterContext _context;
     private final ConsoleUpdateManager _mgr;
+    private final Log _log;
 
     public PluginUpdateHandler(RouterContext ctx, ConsoleUpdateManager mgr) {
         _context = ctx;
+        _log = _context.logManager().getLog(PluginUpdateHandler.class);
         _mgr = mgr;
     }
-    
+
     /** check a single plugin */
     @Override
     public UpdateTask check(UpdateType type, UpdateMethod method,
@@ -48,6 +52,10 @@ class PluginUpdateHandler implements Checker, Updater {
             xpi2pURL = props.getProperty("updateURL");
         List<URI> updateSources = null;
         if (xpi2pURL != null) {
+            xpi2pURL = xpi2pURL.replace("$OS", SystemVersion.getOS());
+            xpi2pURL = xpi2pURL.replace("$ARCH", SystemVersion.getArch());
+            if (_log.shouldLog(Log.INFO))
+                _log.info("Checking for updates for " + appName + ": " + xpi2pURL);
             try {
                 updateSources = Collections.singletonList(new URI(xpi2pURL));
             } catch (URISyntaxException use) {}
@@ -61,7 +69,7 @@ class PluginUpdateHandler implements Checker, Updater {
         UpdateRunner update = new PluginUpdateChecker(_context, _mgr, updateSources, appName, oldVersion);
         return update;
     }
-    
+
     /** download a single plugin */
     @Override
     public UpdateTask update(UpdateType type, UpdateMethod method, List<URI> updateSources,
@@ -83,4 +91,4 @@ class PluginUpdateHandler implements Checker, Updater {
         return update;
     }
 }
-    
+
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
index 9ce5153ea759ebfe5c0b11958b0823f9074efa09..a6f0d98233dbffc7e6a8807d1f4a317a60966a9a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
@@ -838,6 +838,8 @@ public class PluginStarter implements Runnable {
                         argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                         argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                         argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
+                        argVal[i] = argVal[i].replace("$OS", SystemVersion.getOS());
+                        argVal[i] = argVal[i].replace("$ARCH", SystemVersion.getArch());
                     }
                 }
                 ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal);
@@ -877,6 +879,8 @@ public class PluginStarter implements Runnable {
                     argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                     argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                     argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath());
+                    argVal[i] = argVal[i].replace("$OS", SystemVersion.getOS());
+                    argVal[i] = argVal[i].replace("$ARCH", SystemVersion.getArch());
                 }
             }
 
@@ -887,6 +891,8 @@ public class PluginStarter implements Runnable {
                     cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath());
                     cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath());
                     cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath());
+                    cp = cp.replace("$OS", SystemVersion.getOS());
+                    cp = cp.replace("$ARCH", SystemVersion.getArch());
                 }
 
                 // Old way - add for the whole JVM
diff --git a/core/java/src/net/i2p/util/SystemVersion.java b/core/java/src/net/i2p/util/SystemVersion.java
index e2cd6ef4eb0b314007e7d8c340da4f7937550945..b33bda99228447fef3b6b2caa089852722f4a325 100644
--- a/core/java/src/net/i2p/util/SystemVersion.java
+++ b/core/java/src/net/i2p/util/SystemVersion.java
@@ -117,6 +117,56 @@ public abstract class SystemVersion {
         }
     }
 
+    /**
+     * returns the OS of the system running I2P as a lower-case string
+     * for reference in clients.config and plugin.config files.
+     *
+     * matches the conventions of the Go cross compiler
+     *
+     * @return the OS of the system running I2P as a lower-case string
+     * @since 0.9.53
+     */
+    public static String getOS() {
+        if (isWindows())
+            return "windows";
+        if (isMac())
+            return "mac";
+        if (isGNU())
+            return "linux"; /* actually... */
+        if (isLinuxService())
+            return "linux";
+        if (isAndroid())
+            return "android";
+        /** Everybody else knows if they're on a Windows machine or a
+         * Mac, so for now, assume linux here.
+         */
+        return "linux";
+    }
+
+    /**
+     * returns the architecture of the system running I2P as a string
+     * for reference in clients.config and plugin.config files.
+     *
+     * matches the conventions of the Go cross compiler
+     *
+     * @return the architecture of the system running I2P as a string
+     * @since 0.9.53
+     */
+    public static String getArch() {
+        if (is64Bit()){
+            if (isARM())
+                return "arm64";
+            if (isX86())
+                return "amd64";
+        }
+        if (isARM())
+            return "arm";
+        if (isX86())
+            return "386";
+        return "unknown";
+    }
+
+
     public static boolean isWindows() {
         return _isWin;
     }