diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
index 3fe2b2ade3fecfeeb25156d92f76a23ff947eb83..21ffac75869298e9d393232a7458290b6d56fdc7 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java
@@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.LinkedBlockingQueue;
 
 import net.i2p.I2PAppContext;
+import net.i2p.app.ClientAppManager;
 import net.i2p.data.Base64;
 import net.i2p.data.DataHelper;
 import net.i2p.update.*;
@@ -198,7 +199,9 @@ public class SnarkManager implements CompleteListener {
         public void timeReached() {
             if (!_running)
                 return;
-            _umgr = _context.updateManager();
+            ClientAppManager cmgr = _context.clientAppManager();
+            if (cmgr != null)
+                _umgr = (UpdateManager) cmgr.getRegisteredApp(UpdateManager.APP_NAME);
             if (_umgr != null) {
                 _uhandler = new UpdateHandler(_context, _umgr, SnarkManager.this);
                 _umgr.register(_uhandler, UpdateType.ROUTER_SIGNED, UpdateMethod.TORRENT, 10);
diff --git a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
index 6efc217a922cd0486deced21d5c0a5eb0bbaa32b..84d656abecce31fbeb891f2e13899b54145c1a7d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
+++ b/apps/routerconsole/java/src/net/i2p/router/update/ConsoleUpdateManager.java
@@ -17,12 +17,16 @@ import java.util.StringTokenizer;
 import java.util.concurrent.ConcurrentHashMap;
 
 import net.i2p.I2PAppContext;
+import net.i2p.app.ClientAppManager;
+import net.i2p.app.ClientAppState;
+import static net.i2p.app.ClientAppState.*;
 import net.i2p.crypto.SU3File;
 import net.i2p.crypto.TrustedUpdate;
 import net.i2p.data.DataHelper;
 import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.RouterVersion;
+import net.i2p.router.app.RouterApp;
 import net.i2p.router.web.ConfigServiceHandler;
 import net.i2p.router.web.ConfigUpdateHandler;
 import net.i2p.router.web.Messages;
@@ -50,7 +54,7 @@ import net.i2p.util.VersionComparator;
  *
  *  @since 0.9.4
  */
-public class ConsoleUpdateManager implements UpdateManager {
+public class ConsoleUpdateManager implements UpdateManager, RouterApp {
     
     private final RouterContext _context;
     private final Log _log;
@@ -68,6 +72,8 @@ public class ConsoleUpdateManager implements UpdateManager {
     private final Map<UpdateItem, Version> _installed;
     private final boolean _allowTorrent;
     private static final DecimalFormat _pct = new DecimalFormat("0.0%");
+    private final ClientAppManager _cmgr;
+    private volatile ClientAppState _state = UNINITIALIZED;
 
     private volatile String _status;
 
@@ -77,8 +83,12 @@ public class ConsoleUpdateManager implements UpdateManager {
     private static final long TASK_CLEANER_TIME = 15*60*1000;
     private static final String PROP_UNSIGNED_AVAILABLE = "router.updateUnsignedAvailable";
 
-    public ConsoleUpdateManager(RouterContext ctx) {
+    /**
+     *  @param args ignored
+     */
+    public ConsoleUpdateManager(RouterContext ctx, ClientAppManager listener, String[] args) {
         _context = ctx;
+        _cmgr = listener;
         _log = ctx.logManager().getLog(ConsoleUpdateManager.class);
         _registeredUpdaters = new ConcurrentHashSet<RegisteredUpdater>();
         _registeredCheckers = new ConcurrentHashSet<RegisteredChecker>();
@@ -98,13 +108,34 @@ public class ConsoleUpdateManager implements UpdateManager {
         //_allowTorrent = RouterVersion.BUILD != 0 || _context.random().nextInt(100) < 60;
         // Finally, for 0.9.12, 18 months later...
         _allowTorrent = true;
+        _state = INITIALIZED;
     }
 
+    /**
+     *  @return null if not found
+     */
     public static ConsoleUpdateManager getInstance() {
-        return (ConsoleUpdateManager) I2PAppContext.getGlobalContext().updateManager();
+        ClientAppManager cmgr = I2PAppContext.getGlobalContext().clientAppManager();
+        if (cmgr == null)
+            return null;
+        return (ConsoleUpdateManager) cmgr.getRegisteredApp(APP_NAME);
     }
 
+    /////// ClientApp methods
+
+    /**
+     *  UpdateManager interface
+     */
     public void start() {
+        startup();
+    }
+
+    /**
+     *  ClientApp interface
+     *  @since 0.9.12
+     */
+    public synchronized void startup() {
+        changeState(STARTING);
         notifyInstalled(NEWS, "", Long.toString(NewsHelper.lastUpdated(_context)));
         notifyInstalled(ROUTER_SIGNED, "", RouterVersion.VERSION);
         notifyInstalled(ROUTER_SIGNED_SU3, "", RouterVersion.VERSION);
@@ -118,7 +149,6 @@ public class ConsoleUpdateManager implements UpdateManager {
                 notifyInstalled(PLUGIN, plugin, ver);
         }
 
-        _context.registerUpdateManager(this);
         DummyHandler dh = new DummyHandler(_context, this);
         register((Checker)dh, TYPE_DUMMY, METHOD_DUMMY, 0);
         register((Updater)dh, TYPE_DUMMY, METHOD_DUMMY, 0);
@@ -162,10 +192,27 @@ public class ConsoleUpdateManager implements UpdateManager {
         //register((Updater)puh, PLUGIN, FILE, 0);
         new NewsTimerTask(_context, this);
         _context.simpleScheduler().addPeriodicEvent(new TaskCleaner(), TASK_CLEANER_TIME);
+        changeState(RUNNING);
+        if (_cmgr != null)
+            _cmgr.register(this);
     }
 
+    /**
+     *  UpdateManager interface
+     */
     public void shutdown() {
-        _context.unregisterUpdateManager(this);
+        shutdown(null);
+    }
+
+    /**
+     *  ClientApp interface
+     *  @param args ignored
+     *  @since 0.9.12
+     */
+    public synchronized void shutdown(String[] args) {
+        if (_state == STOPPED)
+            return;
+        changeState(STOPPING);
         stopChecks();
         stopUpdates();
         _registeredUpdaters.clear();
@@ -173,6 +220,30 @@ public class ConsoleUpdateManager implements UpdateManager {
         _available.clear();
         _downloaded.clear();
         _installed.clear();
+        changeState(STOPPED);
+    }
+
+    /** @since 0.9.12 */
+    public ClientAppState getState() {
+        return _state;
+    }
+
+    /** @since 0.9.12 */
+    public String getName() {
+        return APP_NAME;
+    }
+
+    /** @since 0.9.12 */
+    public String getDisplayName() {
+        return "Console Update Manager";
+    }
+
+    /////// end ClientApp methods
+
+    private synchronized void changeState(ClientAppState state) {
+        _state = state;
+        if (_cmgr != null)
+            _cmgr.notify(this, state, null, null);
     }
 
     /**
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
index b5f1e1c51f378bd31261df3ab6f41686102d19e3..09a0def7cfe67ed8501e0efb7ac60818b94c757c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java
@@ -370,7 +370,7 @@ public class ConfigClientsHandler extends FormHandler {
      *  @param app null for a new install
      */
     private void installPlugin(String app, String url) {
-        ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager();
+        ConsoleUpdateManager mgr = UpdateHandler.updateManager(_context);
         if (mgr == null) {
             addFormError("Update manager not registered, cannot install");
             return;
@@ -397,7 +397,7 @@ public class ConfigClientsHandler extends FormHandler {
     }
 
     private void checkPlugin(String app) {
-        ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager();
+        ConsoleUpdateManager mgr = UpdateHandler.updateManager(_context);
         if (mgr == null) {
             addFormError("Update manager not registered, cannot check");
             return;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
index 09f00c388c162843f061e9b6ad76dfce0979ea3c..bbf8d390c9a7711cf489255f9e7cbf4a83548b1b 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHandler.java
@@ -139,7 +139,7 @@ public class ConfigUpdateHandler extends FormHandler {
         if (_action == null)
             return;
         if (_action.equals(_("Check for updates"))) {
-            ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager();
+            ConsoleUpdateManager mgr = UpdateHandler.updateManager(_context);
             if (mgr == null) {
                 addFormError("Update manager not registered, cannot check");
                 return;
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 24633f495ccd21ffe2b8cd57ffcb6870f24a0bc9..cb09a1e5eb048bf50c03cb42d99aec5bbb993093 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java
@@ -121,7 +121,7 @@ public class PluginStarter implements Runnable {
         if (toUpdate.isEmpty())
             return;
 
-        ConsoleUpdateManager mgr = (ConsoleUpdateManager) ctx.updateManager();
+        ConsoleUpdateManager mgr = UpdateHandler.updateManager(ctx);
         if (mgr == null)
             return;
         if (mgr.isUpdateInProgress())
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
index b931b0bee7e0eb4efbc6a9237cb86db49b7355f6..b8799df161169edb73ae47608894afaad454db9f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -645,7 +645,7 @@ public class RouterConsoleRunner implements RouterApp {
         t.setPriority(Thread.NORM_PRIORITY - 1);
         t.start();
         
-            ConsoleUpdateManager um = new ConsoleUpdateManager(_context);
+            ConsoleUpdateManager um = new ConsoleUpdateManager(_context, _mgr, null);
             um.start();
         
             if (PluginStarter.pluginsEnabled(_context)) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
index 4148a63afa4fcf53e43040d8023db50f20aa5b48..cf2b737a21f91db7fd94acbc74ecbfa240e5fced 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java
@@ -1,7 +1,9 @@
 package net.i2p.router.web;
 
+import net.i2p.app.ClientAppManager;
 import net.i2p.router.RouterContext;
 import net.i2p.router.update.ConsoleUpdateManager;
+import net.i2p.update.UpdateManager;
 import net.i2p.update.UpdateType;
 import static net.i2p.update.UpdateType.*;
 import net.i2p.util.Log;
@@ -33,6 +35,17 @@ public class UpdateHandler {
         _context = ctx;
         _log = ctx.logManager().getLog(UpdateHandler.class);
     }
+
+    /**
+     *  @return null if not found
+     *  @since 0.9.12
+     */
+    public static ConsoleUpdateManager updateManager(RouterContext ctx) {
+        ClientAppManager cmgr = ctx.clientAppManager();
+        if (cmgr == null)
+            return null;
+        return (ConsoleUpdateManager) cmgr.getRegisteredApp(UpdateManager.APP_NAME);
+    }
     
     /**
      * Configure this bean to query a particular router context
@@ -75,7 +88,7 @@ public class UpdateHandler {
     }
 
     private void update(UpdateType type) {
-        ConsoleUpdateManager mgr = (ConsoleUpdateManager) _context.updateManager();
+        ConsoleUpdateManager mgr = updateManager(_context);
         if (mgr == null)
             return;
         if (mgr.isUpdateInProgress(ROUTER_SIGNED) || mgr.isUpdateInProgress(ROUTER_UNSIGNED) ||
diff --git a/apps/routerconsole/jsp/debug.jsp b/apps/routerconsole/jsp/debug.jsp
index f36e9f31ffa2b3cbc6176ba4be07976545c28364..7744f651d4497bc9115c11ef488baf8df2fd0d21 100644
--- a/apps/routerconsole/jsp/debug.jsp
+++ b/apps/routerconsole/jsp/debug.jsp
@@ -30,7 +30,14 @@
     /*
      *  Print out the status for the UpdateManager
      */
-    ctx.updateManager().renderStatusHTML(out);
+    net.i2p.app.ClientAppManager cmgr = ctx.clientAppManager();
+    if (cmgr != null) {
+        net.i2p.router.update.ConsoleUpdateManager umgr =
+            (net.i2p.router.update.ConsoleUpdateManager) cmgr.getRegisteredApp(net.i2p.update.UpdateManager.APP_NAME);
+        if (umgr != null) {
+            umgr.renderStatusHTML(out);
+        }
+    }
 
     /*
      *  Print out the status for the AppManager
diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java
index d20ceb982da0d44be89f7b809e3e2398c4427eee..4eb2fcdbdbf10e4579ccf5ff1e26eb55035fd4b5 100644
--- a/core/java/src/net/i2p/I2PAppContext.java
+++ b/core/java/src/net/i2p/I2PAppContext.java
@@ -1017,15 +1017,6 @@ public class I2PAppContext {
         }
     }
 
-    /**
-     *  The controller of router, plugin, and other updates.
-     *  @return always null in I2PAppContext, the update manager if in RouterContext and it is registered
-     *  @since 0.9.4
-     */
-    public UpdateManager updateManager() {
-        return null;
-    }
-
     /**
      *  The RouterAppManager in RouterContext, null always in I2PAppContext
      *  @return null always
diff --git a/core/java/src/net/i2p/update/UpdateManager.java b/core/java/src/net/i2p/update/UpdateManager.java
index 3a1315812b10c8bb8d1f330055fefa9885bb6819..ce18c59ee976685050c636427d578604aa4495e8 100644
--- a/core/java/src/net/i2p/update/UpdateManager.java
+++ b/core/java/src/net/i2p/update/UpdateManager.java
@@ -18,6 +18,12 @@ import java.util.Map;
  */
 public interface UpdateManager {
     
+    /**
+     *  The name we register with the ClientAppManager
+     *  @since 0.9.12
+     */
+    public static final String APP_NAME = "update";
+
     /**
      *  Call once for each type/method pair.
      */
diff --git a/router/java/src/net/i2p/router/RouterContext.java b/router/java/src/net/i2p/router/RouterContext.java
index ba837ccf2de7ba4623f0a38b068a02d7d812f4dd..0afd98a7b571d2c1bdcd0ca89b9c1e435040b60b 100644
--- a/router/java/src/net/i2p/router/RouterContext.java
+++ b/router/java/src/net/i2p/router/RouterContext.java
@@ -67,7 +67,7 @@ public class RouterContext extends I2PAppContext {
     private final Set<Runnable> _finalShutdownTasks;
     // split up big lock on this to avoid deadlocks
     private volatile boolean _initialized;
-    private final Object _lock1 = new Object(), _lock2 = new Object(), _lock3 = new Object();
+    private final Object _lock1 = new Object(), _lock2 = new Object();
 
     private static final List<RouterContext> _contexts = new CopyOnWriteArrayList<RouterContext>();
     
@@ -546,42 +546,6 @@ public class RouterContext extends I2PAppContext {
         return _internalClientManager;
     }
 
-    /**
-     *  The controller of router, plugin, and other updates.
-     *  @return The manager if it is registered, else null
-     *  @since 0.9.4
-     */
-    @Override
-    public UpdateManager updateManager() {
-        return _updateManager;
-    }
-
-    /**
-     *  Register as the update manager.
-     *  @throws IllegalStateException if one was already registered
-     *  @since 0.9.4
-     */
-    public void registerUpdateManager(UpdateManager mgr) {
-        synchronized(_lock3) {
-            if (_updateManager != null)
-                throw new IllegalStateException();
-            _updateManager = mgr;
-        }
-    }
-
-    /**
-     *  Unregister the update manager.
-     *  @throws IllegalStateException if it was not registered
-     *  @since 0.9.4
-     */
-    public void unregisterUpdateManager(UpdateManager mgr) {
-        synchronized(_lock3) {
-            if (_updateManager != mgr)
-                throw new IllegalStateException();
-            _updateManager = null;
-        }
-    }
-
     /**
      *  The RouterAppManager.
      *  @return the manager