diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java
index e01b28fb041b7f02956a996001eccfb9bbc95d80..fb2589b52bdc5ec2ed1d0e2368e17f13845c3099 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerImpl.java
@@ -673,6 +673,8 @@ class I2PSocketManagerImpl implements I2PSocketManager, I2PSessionListener {
      * Ping the specified peer, returning true if they replied to the ping within 
      * the timeout specified, false otherwise.  This call blocks.
      *
+     * @deprecated timeout is ignored - use I2PSocketManagerFull.ping()
+     * @param timeoutMs ignored
      */
     public boolean ping(Destination peer, long timeoutMs) {
         try {
diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml
index f77a14387aea09c2f91f28ecd1ad97df778757ab..61d1ddc85fdaa7d0c479f17f8cd03e517af51931 100644
--- a/apps/routerconsole/java/build.xml
+++ b/apps/routerconsole/java/build.xml
@@ -73,11 +73,26 @@
         <delete dir="./tmpextract" />
         
         <ant target="war" />
+
+        <!-- Update the messages_*.po files.
+             We need to supply the bat file for windows, and then change the fail property to true -->
+        <exec executable="sh" osfamily="unix" failifexecutionfails="false" >
+            <arg value="./bundle-messages.sh" />
+        </exec>
+        <exec executable="sh" osfamily="mac" failifexecutionfails="false" >
+            <arg value="./bundle-messages.sh" />
+        </exec>
+        <exec executable="cmd" osfamily="windows" failifexecutionfails="false" >
+            <arg value="/c" />
+            <arg value="bundle-messages.bat" />
+        </exec>
+        <!-- jar again to get the latest messages_*.class files -->
+        <jar destfile="./build/routerconsole.jar" basedir="./build/obj" includes="**/*.class" update="true" />
     </target>
     <target name="war" depends="precompilejsp">
         <!-- Don't include the css in the war, the main build.xml will copy it to docs/themes/console/ -->
         <war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
-             basedir="../jsp/" excludes="web.xml, *.css, *.java, *.jsp, web-fragment.xml">
+             basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, web-fragment.xml">
         </war>
     </target>
     <target name="precompilejsp" unless="precompilejsp.uptodate">
@@ -115,6 +130,7 @@
         </java>
         
         <javac debug="true" deprecation="on" source="1.5" target="1.5" 
+               encoding="UTF-8"
                destdir="../jsp/WEB-INF/classes/" 
                srcdir="../jsp/WEB-INF/classes" includes="**/*.java">
             <compilerarg line="${javac.compilerargs}" />
@@ -133,10 +149,13 @@
                 <pathelement location="../../../core/java/build/i2p.jar" />
             </classpath>
         </javac>
+
+        <!-- save these so we can run gettext on the generated java files later
         <delete>
             <fileset dir="../jsp/WEB-INF/" includes="**/*.java" />
             <fileset dir="../jsp/WEB-INF/" includes="**/*.jsp" />
         </delete>
+        -->
         <copy file="../jsp/web.xml" tofile="../jsp/web-out.xml" />
         <loadfile property="jspc.web.fragment" srcfile="../jsp/web-fragment.xml" />
         <replace file="../jsp/web-out.xml">
diff --git a/apps/routerconsole/java/bundle-messages.sh b/apps/routerconsole/java/bundle-messages.sh
new file mode 100755
index 0000000000000000000000000000000000000000..629fc95f05b294dc8abd9c3ee9f758055889ef65
--- /dev/null
+++ b/apps/routerconsole/java/bundle-messages.sh
@@ -0,0 +1,67 @@
+#
+# Update messages_xx.po and messages_xx.class files,
+# from both java and jsp sources.
+# Requires installed programs xgettext, msgfmt, msgmerge, and find.
+# zzz - public domain
+#
+CLASS=net.i2p.router.web.messages
+TMPFILE=build/javafiles.txt
+export TZ=UTC
+
+for i in ../locale/messages_*.po
+do
+	# get language
+	LG=${i#../locale/messages_}
+	LG=${LG%.po}
+
+	# make list of java files newer than the .po file
+	find src ../jsp/WEB-INF -name *.java -newer $i > $TMPFILE
+	if [ -s build/obj/net/i2p/router/web/messages_$LG.class -a ! -s $TMPFILE ]
+	then
+		continue
+	fi
+
+	echo "Generating ${CLASS}_$LG ResourceBundle..."
+
+	# extract strings from java and jsp files, and update messages.po files
+	# translate calls must be one of the forms:
+	# _("foo")
+	# cssHelper._("foo")
+	# cssHelper.title("foo")
+	# handler._("foo")
+	# formhandler._("foo")
+	# In a jsp, you must use a helper or handler that has the context set.
+	# To start a new translation, copy the header from an old translation to the new .po file,
+	# then ant distclean updater.
+	find src ../jsp/WEB-INF -name *.java > $TMPFILE
+	xgettext -f $TMPFILE -F -L java \
+                 --keyword=_ --keyword=cssHelper._ --keyword=cssHelper.title --keyword=handler._ --keyword=formhandler._ \
+	         -o ${i}t
+	if [ $? -ne 0 ]
+	then
+		echo 'Warning - xgettext failed, not updating translations'
+		rm -f ${i}t
+		break
+	fi
+	msgmerge -U --backup=none $i ${i}t
+	if [ $? -ne 0 ]
+	then
+		echo 'Warning - msgmerge failed, not updating translations'
+		rm -f ${i}t
+		break
+	fi
+	rm -f ${i}t
+	# so we don't do this again
+	touch $i
+
+	# convert to class files in build/obj
+	msgfmt --java -r $CLASS -l $LG -d build/obj $i
+	if [ $? -ne 0 ]
+	then
+		echo 'Warning - xgettext failed, not updating translations'
+		break
+	fi
+done
+rm -f $TMPFILE
+# todo: return failure
+exit 0
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java
index a2f3341f9df920f0687c4b21103d45ffc162d8f5..715c800931a7c1148ff248e76149a30bbc111ed7 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/CSSHelper.java
@@ -22,4 +22,21 @@ public class CSSHelper extends HelperBase {
         }
         return url;
     }
+
+    /** change default language for the router but don't save it */
+    public void setLang(String lang) {
+        if (lang != null && lang.length() > 0)
+            _context.router().setConfigSetting(Messages.PROP_LANG, lang);
+    }
+
+    /** translate the title and display consistently */
+    public String title(String s) {
+         StringBuilder buf = new StringBuilder(128);
+         buf.append("<title>")
+            .append(_("I2P Router Console"))
+            .append(" - ")
+            .append(_(s))
+            .append("</title>");
+         return buf.toString();
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
index 82d8461f15715cb4a1217ee816578bee7c7dc904..164ac8df20239387891a348812e6a5fe3cca3862 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigUpdateHelper.java
@@ -87,7 +87,7 @@ public class ConfigUpdateHelper extends HelperBase {
         buf.append("<select name=\"updatePolicy\">");
         
         if ("notify".equals(policy))
-            buf.append("<option value=\"notify\" selected=\"true\">Notify only</option>");
+            buf.append("<option value=\"notify\" selected=\"true\">").append(_("Notify only")).append("</option>");
         else
             buf.append("<option value=\"notify\">Notify only</option>");
 
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java
index 795eeb26c3c9f12d9dbba810709d140c93cba76d..0a1cbdc347e1390a03ce1504ca0ec71067066ff6 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ContentHelper.java
@@ -62,7 +62,8 @@ public class ContentHelper extends HelperBase {
 
     /**
      * Convert file.ext to file_lang.ext if it exists.
-     * Get lang from either the cgi lang param or from the default locale.
+     * Get lang from the cgi lang param, then properties, then from the default locale.
+     * _context must be set to check the property.
      */
     private String filename() {
         int lastdot = _page.lastIndexOf('.');
@@ -70,9 +71,13 @@ public class ContentHelper extends HelperBase {
             return _page;
         String lang = _lang;
         if (lang == null || lang.length() <= 0) {
-            lang = Locale.getDefault().getLanguage();
-            if (lang == null || lang.length() <= 0)
-                return _page;
+            if (_context != null)
+                lang = _context.getProperty(Messages.PROP_LANG);
+            if (lang == null || lang.length() <= 0) {
+                lang = Locale.getDefault().getLanguage();
+                if (lang == null || lang.length() <= 0)
+                    return _page;
+            }
         }
         if (lang.equals("en"))
             return _page;
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
index 0aba83517e9e7484f3ee105101a37f32a9f2035e..8ed39f5c194152b1afcaafccdceeeb333b08d23a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/FormHandler.java
@@ -190,4 +190,8 @@ public class FormHandler {
         }
     }
     
+    /** translate a string */
+    public String _(String s) {
+        return Messages.getString(s, _context);
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
index 848e48d50dfb38540ce0ab973953418e0f9a299b..63762ec14d8f1651db5c8f09e07cf8ac98c3002c 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/HelperBase.java
@@ -29,4 +29,9 @@ public abstract class HelperBase {
     //public RouterContext getContext() { return _context; }
 
     public void setWriter(Writer out) { _out = out; }
+
+    /** translate a string */
+    public String _(String s) {
+        return Messages.getString(s, _context);
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LocaleWebAppHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/LocaleWebAppHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..b7ac2b8f8f292f1e6f75265ff3a456a8e7aa4693
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/LocaleWebAppHandler.java
@@ -0,0 +1,69 @@
+package net.i2p.router.web;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Map;
+
+import net.i2p.I2PAppContext;
+
+import org.mortbay.http.HttpRequest;
+import org.mortbay.http.HttpResponse;
+import org.mortbay.jetty.servlet.WebApplicationHandler;
+
+/**
+ * Convert foo.jsp to foo_xx.jsp for language xx.
+ * This is appropriate for jsps with large amounts of text.
+ * This does not work for included jsps (e.g. summary*)
+ *
+ * @author zzz
+ */
+public class LocaleWebAppHandler extends WebApplicationHandler
+{
+    private I2PAppContext _context;
+
+    public LocaleWebAppHandler(I2PAppContext ctx) {
+        super();
+        _context = ctx;
+    }
+    
+    /**
+     *  Handle foo.jsp by converting to foo_xx.jsp
+     *  for language xx, where xx is the language for the default locale,
+     *  or as specified in the routerconsole.lang property.
+     *  Unless language==="en".
+     */
+    public void handle(String pathInContext,
+                       String pathParams,
+                       HttpRequest httpRequest,
+                       HttpResponse httpResponse)
+         throws IOException
+    {
+        //System.err.println("Path: " + pathInContext);
+        String newPath = pathInContext;
+        if (pathInContext.endsWith(".jsp")) {
+            int len = pathInContext.length();
+            // ...but leave foo_xx.jsp alone
+            if (len < 8 || pathInContext.charAt(len - 7) != '_') {
+                String lang = _context.getProperty(Messages.PROP_LANG);
+                if (lang == null || lang.length() <= 0)
+                    lang = Locale.getDefault().getLanguage();
+                if (lang != null && lang.length() > 0 && !lang.equals("en")) {
+                    String testPath = pathInContext.substring(0, len - 4) + '_' + lang + ".jsp";
+                    // Do we have a servlet for the new path that isn't the catchall *.jsp?
+                    Map.Entry servlet = getHolderEntry(testPath);
+                    if (servlet != null) {
+                        String servletPath = (String) servlet.getKey();
+                        if (servletPath != null && !servletPath.startsWith("*")) {
+                            // success!!
+                            //System.err.println("Servlet is: " + servletPath);
+                            newPath = testPath;
+                        }
+                    }
+                }
+            }
+        }
+        //System.err.println("New path: " + newPath);
+        super.handle(newPath, pathParams, httpRequest, httpResponse);
+        //System.err.println("Was handled? " + httpRequest.isHandled());
+    }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/Messages.java b/apps/routerconsole/java/src/net/i2p/router/web/Messages.java
new file mode 100644
index 0000000000000000000000000000000000000000..df7c5319f3fb0dc3fd090fa09c3bb8924d7242f5
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/Messages.java
@@ -0,0 +1,80 @@
+package net.i2p.router.web;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import net.i2p.I2PAppContext;
+import net.i2p.util.ConcurrentHashSet;
+
+/**
+ * Translate strings efficiently.
+ * We don't include an English or default ResourceBundle, we simply check
+ * for "en" and return the original string.
+ * Support real-time language changing with the routerconsole.lang property.
+ *
+ * @author zzz, from a base generated by eclipse.
+ */
+public class Messages {
+    public static final String PROP_LANG = "routerconsole.lang";
+    private static final String BUNDLE_NAME = "net.i2p.router.web.messages";
+    private static final String _localeLang = Locale.getDefault().getLanguage();
+    private static final Map<String, ResourceBundle> _bundles = new ConcurrentHashMap(2);
+    private static final Set<String> _missing = new ConcurrentHashSet(2);
+
+    /** current locale **/
+    public static String getString(String key) {
+        if (_localeLang.equals("en"))
+            return key;
+        ResourceBundle bundle = findBundle(_localeLang);
+        if (bundle == null)
+            return key;
+        try {
+            return bundle.getString(key);
+        } catch (MissingResourceException e) {
+            return key;
+        }
+    }
+
+    /** lang in routerconsole.lang property, else current locale */
+    public static String getString(String key, I2PAppContext ctx) {
+        String lang = getLanguage(ctx);
+        if (lang.equals("en"))
+            return key;
+        ResourceBundle bundle = findBundle(lang);
+        if (bundle == null)
+            return key;
+        try {
+            return bundle.getString(key);
+        } catch (MissingResourceException e) {
+            return key;
+        }
+    }
+
+    private static String getLanguage(I2PAppContext ctx) {
+        String lang = ctx.getProperty(PROP_LANG);
+        if (lang == null || lang.length() <= 0)
+            lang = _localeLang;
+        return lang;
+    }
+
+    /** cache both found and not found for speed */
+    private static ResourceBundle findBundle(String lang) {
+        ResourceBundle rv = _bundles.get(lang);
+        if (rv == null && !_missing.contains(lang)) {
+            try {
+                // Would it be faster to specify a class loader?
+                // No matter we only do this once per lang.
+                rv = ResourceBundle.getBundle(BUNDLE_NAME, new Locale(lang));
+                if (rv != null)
+                    _bundles.put(lang, rv);
+            } catch (MissingResourceException e) {
+                _missing.add(lang);
+            }
+        }
+        return rv;
+    }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
index cffe6f1a7876838ce8fe5f5761c0d4bb53f2bb9e..fd49aa7ad051240a5ca990f555d60a3f5f73368a 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbHelper.java
@@ -17,23 +17,24 @@ public class NetDbHelper extends HelperBase {
     public void setLease(String l) { _lease = "1".equals(l); }
     
     public String getNetDbSummary() {
+        NetDbRenderer renderer = new NetDbRenderer(_context);
         try {
             if (_out != null) {
                 if (_routerPrefix != null)
-                    _context.netDb().renderRouterInfoHTML(_out, _routerPrefix);
+                    renderer.renderRouterInfoHTML(_out, _routerPrefix);
                 else if (_lease)
-                    _context.netDb().renderLeaseSetHTML(_out);
+                    renderer.renderLeaseSetHTML(_out);
                 else
-                    _context.netDb().renderStatusHTML(_out, _full);
+                    renderer.renderStatusHTML(_out, _full);
                 return "";
             } else {
                 ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
                 if (_routerPrefix != null)
-                    _context.netDb().renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
+                    renderer.renderRouterInfoHTML(new OutputStreamWriter(baos), _routerPrefix);
                 else if (_lease)
-                    _context.netDb().renderLeaseSetHTML(new OutputStreamWriter(baos));
+                    renderer.renderLeaseSetHTML(new OutputStreamWriter(baos));
                 else
-                    _context.netDb().renderStatusHTML(new OutputStreamWriter(baos), _full);
+                    renderer.renderStatusHTML(new OutputStreamWriter(baos), _full);
                 return new String(baos.toByteArray());
             }
         } catch (IOException ioe) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e9f76fa84868a835cfe92c09c0f823e37dd7c86
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java
@@ -0,0 +1,275 @@
+package net.i2p.router.web;
+/*
+ * free (adj.): unencumbered; not under the control of others
+ * Written by jrandom in 2003 and released into the public domain
+ * with no warranty of any kind, either expressed or implied.
+ * It probably won't make your computer catch on fire, or eat
+ * your children, but it might.  Use at your own risk.
+ *
+ */
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.RouterAddress;
+import net.i2p.data.RouterInfo;
+import net.i2p.router.RouterContext;
+import net.i2p.router.TunnelPoolSettings;
+import net.i2p.util.ObjectCounter;
+
+public class NetDbRenderer {
+    private RouterContext _context;
+
+    public NetDbRenderer (RouterContext ctx) {
+        _context = ctx;
+    }
+
+    private class LeaseSetComparator implements Comparator {
+         public int compare(Object l, Object r) {
+             Destination dl = ((LeaseSet)l).getDestination();
+             Destination dr = ((LeaseSet)r).getDestination();
+             boolean locall = _context.clientManager().isLocal(dl);
+             boolean localr = _context.clientManager().isLocal(dr);
+             if (locall && !localr) return -1;
+             if (localr && !locall) return 1;
+             return dl.calculateHash().toBase64().compareTo(dr.calculateHash().toBase64());
+        }
+    }
+
+    private static class RouterInfoComparator implements Comparator {
+         public int compare(Object l, Object r) {
+             return ((RouterInfo)l).getIdentity().getHash().toBase64().compareTo(((RouterInfo)r).getIdentity().getHash().toBase64());
+        }
+    }
+
+    public void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException {
+        StringBuilder buf = new StringBuilder(4*1024);
+        buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
+        if (".".equals(routerPrefix)) {
+            renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
+        } else {
+            boolean notFound = true;
+            Set routers = _context.netDb().getRouters();
+            for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
+                RouterInfo ri = (RouterInfo)iter.next();
+                Hash key = ri.getIdentity().getHash();
+                if (key.toBase64().startsWith(routerPrefix)) {
+                    renderRouterInfo(buf, ri, false, true);
+                    notFound = false;
+                }
+            }
+            if (notFound)
+                buf.append("Router ").append(routerPrefix).append(" not found in network database");
+        }
+        out.write(buf.toString());
+        out.flush();
+    }
+
+    public void renderStatusHTML(Writer out) throws IOException {
+        renderStatusHTML(out, true);
+    }
+
+    public void renderLeaseSetHTML(Writer out) throws IOException {
+        StringBuilder buf = new StringBuilder(4*1024);
+        buf.append("<h2>Network Database Contents</h2>\n");
+        buf.append("<a href=\"netdb.jsp\">View RouterInfo</a>");
+        buf.append("<h3>LeaseSets</h3>\n");
+        Set leases = new TreeSet(new LeaseSetComparator());
+        leases.addAll(_context.netDb().getLeases());
+        long now = _context.clock().now();
+        for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
+            LeaseSet ls = (LeaseSet)iter.next();
+            Destination dest = ls.getDestination();
+            Hash key = dest.calculateHash();
+            buf.append("<b>LeaseSet: ").append(key.toBase64());
+            if (_context.clientManager().isLocal(dest)) {
+                buf.append(" (<a href=\"tunnels.jsp#" + key.toBase64().substring(0,4) + "\">Local</a> ");
+                if (! _context.clientManager().shouldPublishLeaseSet(key))
+                    buf.append("Unpublished ");
+                buf.append("Destination ");
+                TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(key);
+                if (in != null && in.getDestinationNickname() != null)
+                    buf.append(in.getDestinationNickname());
+                else
+                    buf.append(dest.toBase64().substring(0, 6));
+            } else {
+                buf.append(" (Destination ");
+                String host = _context.namingService().reverseLookup(dest);
+                if (host != null)
+                    buf.append(host);
+                else
+                    buf.append(dest.toBase64().substring(0, 6));
+            }
+            buf.append(")</b><br>\n");
+            long exp = ls.getEarliestLeaseDate()-now;
+            if (exp > 0)
+                buf.append("Expires in ").append(DataHelper.formatDuration(exp)).append("<br>\n");
+            else
+                buf.append("Expired ").append(DataHelper.formatDuration(0-exp)).append(" ago<br>\n");
+            for (int i = 0; i < ls.getLeaseCount(); i++) {
+                buf.append("Lease ").append(i + 1).append(": Gateway ");
+                buf.append(_context.commSystem().renderPeerHTML(ls.getLease(i).getGateway()));
+                buf.append(" Tunnel ").append(ls.getLease(i).getTunnelId().getTunnelId()).append("<br>\n");
+            }
+            buf.append("<hr>\n");
+            out.write(buf.toString());
+            buf.setLength(0);
+        }
+        out.write(buf.toString());
+        out.flush();
+    }
+
+    public void renderStatusHTML(Writer out, boolean full) throws IOException {
+        int size = _context.netDb().getKnownRouters() * 512;
+        if (full)
+            size *= 4;
+        StringBuilder buf = new StringBuilder(size);
+        out.write("<h2>Network Database Contents (<a href=\"netdb.jsp?l=1\">View LeaseSets</a>)</h2>\n");
+        if (!_context.netDb().isInitialized()) {
+            buf.append("Not initialized\n");
+            out.write(buf.toString());
+            out.flush();
+            return;
+        }
+        
+        Hash us = _context.routerHash();
+        out.write("<a name=\"routers\" ></a><h3>Routers (<a href=\"netdb.jsp");
+        if (full)
+            out.write("#routers\" >view without");
+        else
+            out.write("?f=1#routers\" >view with");
+        out.write(" stats</a>)</h3>\n");
+        
+        RouterInfo ourInfo = _context.router().getRouterInfo();
+        renderRouterInfo(buf, ourInfo, true, true);
+        out.write(buf.toString());
+        buf.setLength(0);
+        
+        ObjectCounter<String> versions = new ObjectCounter();
+        ObjectCounter<String> countries = new ObjectCounter();
+        
+        Set routers = new TreeSet(new RouterInfoComparator());
+        routers.addAll(_context.netDb().getRouters());
+        for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
+            RouterInfo ri = (RouterInfo)iter.next();
+            Hash key = ri.getIdentity().getHash();
+            boolean isUs = key.equals(us);
+            if (!isUs) {
+                renderRouterInfo(buf, ri, false, full);
+                out.write(buf.toString());
+                buf.setLength(0);
+                String routerVersion = ri.getOption("router.version");
+                if (routerVersion != null)
+                    versions.increment(routerVersion);
+                String country = _context.commSystem().getCountry(key);
+                if(country != null)
+                    countries.increment(country);
+            }
+        }
+            
+        buf.append("<table border=\"0\" cellspacing=\"30\"><tr><td>");
+        List<String> versionList = new ArrayList(versions.objects());
+        if (versionList.size() > 0) {
+            Collections.sort(versionList, Collections.reverseOrder());
+            buf.append("<table>\n");
+            buf.append("<tr><th>Version</th><th>Count</th></tr>\n");
+            for (String routerVersion : versionList) {
+                int num = versions.count(routerVersion);
+                buf.append("<tr><td align=\"center\">").append(DataHelper.stripHTML(routerVersion));
+                buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
+            }
+            buf.append("</table>\n");
+        }
+        buf.append("</td><td>");
+        out.write(buf.toString());
+        buf.setLength(0);
+            
+        List<String> countryList = new ArrayList(countries.objects());
+        if (countryList.size() > 0) {
+            Collections.sort(countryList);
+            buf.append("<table>\n");
+            buf.append("<tr><th align=\"left\">Country</th><th>Count</th></tr>\n");
+            for (String country : countryList) {
+                int num = countries.count(country);
+                buf.append("<tr><td><img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
+                buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
+                buf.append(_context.commSystem().getCountryName(country));
+                buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
+            }
+            buf.append("</table>\n");
+        }
+        buf.append("</td></tr></table>");
+        out.write(buf.toString());
+        out.flush();
+    }
+    
+    /**
+     *  Be careful to use stripHTML for any displayed routerInfo data
+     *  to prevent vulnerabilities
+     */
+    private void renderRouterInfo(StringBuilder buf, RouterInfo info, boolean isUs, boolean full) {
+        String hash = info.getIdentity().getHash().toBase64();
+        buf.append("<table><tr><th><a name=\"").append(hash.substring(0, 6)).append("\" ></a>");
+        if (isUs) {
+            buf.append("<a name=\"our-info\" ></a><b>Our info: ").append(hash).append("</b></th></tr><tr><td>\n");
+        } else {
+            buf.append("<b>Peer info for:</b> ").append(hash).append("\n");
+            if (full) {
+                buf.append("[<a href=\"netdb.jsp\" >Back</a>]</th></tr><td>\n");
+            } else {
+                buf.append("[<a href=\"netdb.jsp?r=").append(hash.substring(0, 6)).append("\" >Full entry</a>]</th></tr><td>\n");
+            }
+        }
+        
+        long age = _context.clock().now() - info.getPublished();
+        if (isUs && _context.router().isHidden())
+            buf.append("<b>Hidden, Updated:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
+        else if (age > 0)
+            buf.append("<b>Published:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
+        else
+            buf.append("<b>Published:</b> in ").append(DataHelper.formatDuration(0-age)).append("???<br>\n");
+        buf.append("<b>Address(es):</b> ");
+        String country = _context.commSystem().getCountry(info.getIdentity().getHash());
+        if(country != null) {
+            buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
+            buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
+        }
+        for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) {
+            RouterAddress addr = (RouterAddress)iter.next();
+            buf.append(DataHelper.stripHTML(addr.getTransportStyle())).append(": ");
+            for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
+                String name = (String)optIter.next();
+                String val = addr.getOptions().getProperty(name);
+                buf.append('[').append(DataHelper.stripHTML(name)).append('=').append(DataHelper.stripHTML(val)).append("] ");
+            }
+        }
+        buf.append("</td></tr>\n");
+        if (full) {
+            buf.append("<tr><td>Stats: <br><code>\n");
+            for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
+                String key = (String)iter.next();
+                String val = info.getOption(key);
+                buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n");
+            }
+            buf.append("</code></td></tr>\n");
+        } else {
+        }
+        buf.append("</td></tr>\n");
+    }
+
+    /** translate a string */
+    private String _(String s) {
+        return Messages.getString(s, _context);
+    }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
index 957cafc108c6184e3b192c203a2c91b93fe704b8..29fb47ada75b13745f55e5ca2c4c170cd19cad2f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ProfileOrganizerRenderer.java
@@ -319,4 +319,9 @@ class ProfileOrganizerRenderer {
             long c = r.getCurrentEventCount() + r.getLastEventCount();
             return "" + c;
     }
+
+    /** translate a string */
+    private String _(String s) {
+        return Messages.getString(s, _context);
+    }
 }
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 8529f13ff9ef5ed3d92bd0a83f9631aeef7c6120..ec2b10c244dddcbab29d80e013cbb2015531ab81 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java
@@ -111,6 +111,7 @@ public class RouterConsoleRunner {
             File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort);
             tmpdir.mkdir();
             wac.setTempDirectory(tmpdir);
+            wac.addHandler(0, new LocaleWebAppHandler(I2PAppContext.getGlobalContext()));
             initialize(wac);
             File dir = new File(_webAppsDir);
             String fileNames[] = dir.list(WarFilenameFilter.instance());
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java
index 1f450239b4af533c308fa00b291b2f4945ed3743..15322edf4b89d5245c9a0c74f89d5ed41b4c4e3f 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/StatsGenerator.java
@@ -235,4 +235,9 @@ public class StatsGenerator {
     
     private final static DecimalFormat _pct = new DecimalFormat("#0.00%");
     private final static String pct(double num) { synchronized (_pct) { return _pct.format(num); } }
+
+    /** translate a string */
+    private String _(String s) {
+        return Messages.getString(s, _context);
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryBarRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryBarRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..e7d6ee4126a09fce625028a2508f633d063d2276
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryBarRenderer.java
@@ -0,0 +1,417 @@
+package net.i2p.router.web;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+
+import net.i2p.router.RouterContext;
+
+/**
+ *  Refactored from summarynoframe.jsp to save ~100KB
+ *
+ */
+public class SummaryBarRenderer {
+    private RouterContext _context;
+    private SummaryHelper _helper;
+
+    public SummaryBarRenderer(RouterContext context, SummaryHelper helper) {
+        _context = context;
+        _helper = helper;
+    }
+
+    public void renderSummaryHTML(Writer out) throws IOException {
+        StringBuilder buf = new StringBuilder(8*1024);
+
+        buf.append("<a href=\"index.jsp\" target=\"_top\"><img src=\"/themes/console/images/i2plogo.png\" alt=\"")
+           .append(_("I2P Router Console"))
+           .append("\" title=\"")
+           .append(_("I2P Router Console"))
+           .append("\"></a><hr>");
+
+        File lpath = new File(_context.getBaseDir(), "docs/toolbar.html");
+        // you better have target="_top" for the links in there...
+        if (lpath.exists()) {
+            ContentHelper linkhelper = new ContentHelper();
+            linkhelper.setPage(lpath.getAbsolutePath());
+            linkhelper.setMaxLines("100");
+            buf.append(linkhelper.getContent());
+        } else {
+            buf.append("<h3><a href=\"/configclients.jsp\" target=\"_top\" title=\"")
+               .append("Configure startup of clients and webapps (services); manually start dormant services")
+               .append("\">")
+               .append(_("I2P Services"))
+               .append("</a></h3>\n" +
+
+                       "<hr><table>" +
+
+                       "<tr><td><a href=\"susidns/index.jsp\" target=\"_blank\" title=\"")
+               .append(_("Manage your I2P hosts file here (I2P domain name resolution)"))
+               .append("\">")
+               .append(_("Addressbook"))
+               .append("</a>\n" +
+
+                       "<a href=\"i2psnark/\" target=\"_blank\" title=\"")
+               .append(_("Built-in anonymous BitTorrent Client"))
+               .append("\">")
+               .append(_("Torrents"))
+               .append("</a>\n" +
+
+                       "<a href=\"susimail/susimail\" target=\"blank\" title=\"")
+               .append(_("Anonymous webmail client"))
+               .append("\">")
+               .append(_("Webmail"))
+               .append("</a>\n" +
+
+                       "<a href=\"http://127.0.0.1:7658/\" target=\"_blank\" title=\"")
+               .append(_("Anonymous resident webserver"))
+               .append("\">")
+               .append(_("Webserver"))
+               .append("</a></td></tr></table>\n" +
+
+                       "<hr><h3><a href=\"config.jsp\" target=\"_top\" title=\"")
+               .append(_("Configure I2P Router"))
+               .append("\">")
+               .append(_("I2P Internals"))
+               .append("</a></h3><hr>\n" +
+
+                       "<table><tr><td>\n" +
+
+                       "<a href=\"tunnels.jsp\" target=\"_top\" title=\"")
+               .append(_("View existing tunnels and tunnel build status"))
+               .append("\">")
+               .append(_("Tunnels"))
+               .append("</a>\n" +
+
+                       "<a href=\"peers.jsp\" target=\"_top\" title=\"")
+               .append(_("Show all current peer connections"))
+               .append("\">")
+               .append(_("Peers"))
+               .append("</a>\n" +
+
+                       "<a href=\"profiles.jsp\" target=\"_top\" title=\"")
+               .append(_("Show recent peer performance profiles"))
+               .append("\">")
+               .append(_("Profiles"))
+               .append("</a>\n" +
+
+                       "<a href=\"netdb.jsp\" target=\"_top\" title=\"")
+               .append(_("Show list of all known I2P routers"))
+               .append("\">")
+               .append(_("NetDB"))
+               .append("</a>\n" +
+
+                       "<a href=\"logs.jsp\" target=\"_top\" title=\"")
+               .append(_("Health Report"))
+               .append("\">")
+               .append(_("Logs"))
+               .append("</a>\n" +
+
+                       "<a href=\"jobs.jsp\" target=\"_top\" title=\"")
+               .append(_("Show the router's workload, and how it's performing"))
+               .append("\">")
+               .append(_("Jobs"))
+               .append("</a>\n" +
+
+                       "<a href=\"graphs.jsp\" target=\"_top\" title=\"")
+               .append(_("Graph router performance"))
+               .append("\">")
+               .append(_("Graphs"))
+               .append("</a>\n" +
+
+                       "<a href=\"oldstats.jsp\" target=\"_top\" title=\"")
+               .append(_("Textual router performance statistics"))
+               .append("\">")
+               .append(_("Stats"))
+               .append("</a></td></tr></table>\n");
+
+            out.write(buf.toString());
+            buf.setLength(0);
+        }
+
+
+
+        buf.append("<hr><h3><a href=\"help.jsp\" target=\"_top\" title=\"")
+           .append(_("I2P Router Help"))
+           .append("\">")
+           .append(_("General"))
+           .append("</a></h3><hr>" +
+                   "<h4><a title=\"")
+           .append(_("Your unique I2P router identity is"))
+           .append(' ')
+           .append(_helper.getIdent())
+           .append(", ")
+           .append(_("never reveal it to anyone"))
+           .append("\" href=\"netdb.jsp?r=.\" target=\"_top\">")
+           .append(_("Local Identity"))
+           .append("<a></h4><hr>\n" +
+
+                   "<table><tr><td align=\"left\">" +
+                   "<b>")
+           .append(_("Version"))
+           .append(":</b></td>" +
+                   "<td align=\"right\">")
+           .append(_helper.getVersion())
+           .append("</td></tr>\n" +
+
+                   "<tr title=\"")
+           .append(_("How long we've been running for this session"))
+           .append("\">" +
+                   "<td align=\"left\"><b>")
+           .append(_("Uptime"))
+           .append(":</b></td>" +
+                   "<td align=\"right\">")
+           .append(_helper.getUptime())
+           .append("</td></tr></table>\n" +
+
+                   "<hr><h4><a href=\"config.jsp#help\" target=\"_top\" title=\"")
+           .append(_("Help with configuring your firewall and router for optimal I2P performance"))
+           .append("\">")
+           .append(_helper.getReachability())
+           .append("</a></h4><hr>\n");
+
+
+        if (_helper.updateAvailable() || _helper.unsignedUpdateAvailable()) {
+            // display all the time so we display the final failure message
+            buf.append("<br>").append(UpdateHandler.getStatus());
+            if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
+                // nothing
+            } else if(
+                      // isDone() is always false for now, see UpdateHandler
+                      // ((!update.isDone()) &&
+                      _helper.getAction() == null &&
+                      _helper.getUpdateNonce() == null &&
+                      ConfigRestartBean.getRestartTimeRemaining() > 12*60*1000) {
+                long nonce = _context.random().nextLong();
+                String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
+                if (prev != null)
+                    System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
+                System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
+                String uri = _helper.getRequestURI();
+                buf.append("<form action=\"").append(uri).append("\" method=\"GET\">\n");
+                buf.append("<input type=\"hidden\" name=\"updateNonce\" value=\"").append(nonce).append("\" >\n");
+                if (_helper.updateAvailable()) {
+                    buf.append("<button type=\"submit\" name=\"updateAction\" value=\"signed\" >")
+                       .append(_("Download"))
+                       .append(' ')
+                       .append(_helper.getUpdateVersion())
+                       .append(' ')
+                       .append(_("Update"))
+                       .append("</button>\n");
+                }
+                if (_helper.unsignedUpdateAvailable()) {
+                    buf.append("<button type=\"submit\" name=\"updateAction\" value=\"Unsigned\" >")
+                       .append(_("Download Unsigned"))
+                       .append("<br>")
+                       .append(_helper.getUnsignedUpdateVersion())
+                       .append(' ')
+                       .append(_("Update"))
+                       .append("</button>\n");
+                }
+                buf.append("</form>\n");
+            }
+        }
+
+
+
+
+        buf.append("<p>")
+           .append(ConfigRestartBean.renderStatus(_helper.getRequestURI(), _helper.getAction(), _helper.getConsoleNonce()))
+
+           .append("</p><hr><h3><a href=\"peers.jsp\" target=\"_top\" title=\"")
+           .append(_("Show all current peer connections"))
+           .append("\">")
+           .append(_("Peers"))
+           .append("</a></h3><hr>\n" +
+
+                   "<table>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Active"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getActivePeers())
+           .append('/')
+           .append(_helper.getActiveProfiles())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Fast"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getFastPeers())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("High capacity"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getHighCapacityPeers())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Integrated"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getWellIntegratedPeers())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Known"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getAllPeers())
+           .append("</td></tr>\n" +
+
+                   "</table><hr>\n");
+
+
+        out.write(buf.toString());
+        buf.setLength(0);
+
+
+        boolean anotherLine = false;
+        if (_helper.showFirewallWarning()) {
+            buf.append("<h4><a href=\"config.jsp\" target=\"_top\" title=\"")
+               .append(_("Help with firewall configuration"))
+               .append("\">")
+               .append(_("Check NAT/firewall"))
+               .append("</a></h4>");
+            anotherLine = true;
+        }
+
+        boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue();
+        // If showing the reseed link is allowed
+        if (_helper.allowReseed()) {
+            if (reseedInProgress) {
+                // While reseed occurring, show status message instead
+                buf.append("<i>").append(System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","")).append("</i><br>");
+            } else {
+                // While no reseed occurring, show reseed link
+                long nonce = _context.random().nextLong();
+                String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
+                if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
+                System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
+                String uri = _helper.getRequestURI();
+                buf.append("<form action=\"").append(uri).append("\" method=\"GET\">\n");
+                buf.append("<input type=\"hidden\" name=\"reseedNonce\" value=\"").append(nonce).append("\" >\n");
+                buf.append("<button type=\"submit\" >Reseed</button></form>\n");
+            }
+            anotherLine = true;
+        }
+        // If a new reseed ain't running, and the last reseed had errors, show error message
+        if (!reseedInProgress) {
+            String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
+            if (reseedErrorMessage.length() > 0) {
+                buf.append("<i>").append(reseedErrorMessage).append("</i><br>");
+                anotherLine = true;
+            }
+        }
+        if (anotherLine)
+            buf.append("<hr>");
+
+
+        buf.append("<h3><a href=\"config.jsp\" title=\"")
+           .append(_("Configure router bandwidth allocation"))
+           .append("\" target=\"_top\">")
+           .append(_("Bandwidth in/out"))
+           .append("</a></h3><hr>" +
+                   "<table>\n" +
+
+                   "<tr><td align=\"left\"><b>1s:</b></td><td align=\"right\">")
+           .append(_helper.getInboundSecondKBps())
+           .append('/')
+           .append(_helper.getOutboundSecondKBps())
+           .append("K/s</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>5m:</b></td><td align=\"right\">")
+           .append(_helper.getInboundFiveMinuteKBps())
+           .append('/')
+           .append(_helper.getOutboundFiveMinuteKBps())
+           .append("K/s</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Total"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getInboundLifetimeKBps())
+           .append('/')
+           .append(_helper.getOutboundLifetimeKBps())
+           .append("K/s</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Used"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getInboundTransferred())
+           .append('/')
+           .append(_helper.getOutboundTransferred())
+           .append("</td></tr></table>\n" +
+
+                   "<hr><h3><a href=\"tunnels.jsp\" target=\"_top\" title=\"")
+           .append(_("View existing tunnels and tunnel build status"))
+           .append("\">")
+           .append(_("Tunnels in/out"))
+           .append("</a></h3><hr>" +
+                   "<table>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Exploratory"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getInboundTunnels())
+           .append('/')
+           .append(_helper.getOutboundTunnels())
+           .append("</td></tr>\n" +
+
+                  "<tr><td align=\"left\"><b>")
+           .append(_("Client"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getInboundClientTunnels())
+           .append('/')
+           .append(_helper.getOutboundClientTunnels())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Participating"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getParticipatingTunnels())
+           .append("</td></tr>\n" +
+
+                   "</table><hr><h3><a href=\"/jobs.jsp\" target=\"_top\" title=\"")
+           .append(_("What's in the router's job queue?"))
+           .append("\">")
+           .append(_("Congestion"))
+           .append("</a></h3><hr>" +
+                   "<table>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Job lag"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getJobLag())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Message delay"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getMessageDelay())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Tunnel lag"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getTunnelLag())
+           .append("</td></tr>\n" +
+
+                   "<tr><td align=\"left\"><b>")
+           .append(_("Backlog"))
+           .append(":</b></td><td align=\"right\">")
+           .append(_helper.getInboundBacklog())
+           .append("</td></tr>\n" +
+
+                   "</table><hr><h4>")
+           .append(_helper.getTunnelStatus())
+           .append("</h4><hr>\n")
+           .append(_helper.getDestinations());
+
+
+
+        out.write(buf.toString());
+    }
+
+    /** translate a string */
+    private String _(String s) {
+        return Messages.getString(s, _context);
+    }
+}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
index 5c7779cf5d4e62cc207ef7b57d34622b43db4e7c..47468e3deaa916588a0c8001467e1adc6aebbd01 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java
@@ -1,5 +1,6 @@
 package net.i2p.router.web;
 
+import java.io.IOException;
 import java.text.Collator;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
@@ -563,4 +564,28 @@ public class SummaryHelper extends HelperBase {
     public String getUnsignedUpdateVersion() { 
         return NewsFetcher.getInstance(_context).unsignedUpdateVersion();
     }
+
+    /** output the summary bar to _out */
+    public void renderSummaryBar() throws IOException {
+        SummaryBarRenderer renderer = new SummaryBarRenderer(_context, this);
+        renderer.renderSummaryHTML(_out);
+    }
+
+    /* below here is stuff we need to get from summarynoframe.jsp to SummaryBarRenderer */
+
+    private String _action;
+    public void setAction(String s) { _action = s; }
+    public String getAction() { return _action; }
+
+    private String _consoleNonce;
+    public void setConsoleNonce(String s) { _consoleNonce = s; }
+    public String getConsoleNonce() { return _consoleNonce; }
+
+    private String _updateNonce;
+    public void setUpdateNonce(String s) { _updateNonce = s; }
+    public String getUpdateNonce() { return _updateNonce; }
+
+    private String _requestURI;
+    public void setRequestURI(String s) { _requestURI = s; }
+    public String getRequestURI() { return _requestURI; }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java
index b605cbf361bc1efd8da3485395cc69d39bbd8f4c..db64f77c2588763efd7ba9f52de273bdbb68875d 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelHelper.java
@@ -9,13 +9,14 @@ public class TunnelHelper extends HelperBase {
     public TunnelHelper() {}
     
     public String getTunnelSummary() {
+        TunnelRenderer renderer = new TunnelRenderer(_context);
         try {
             if (_out != null) {
-                _context.tunnelManager().renderStatusHTML(_out);
+                renderer.renderStatusHTML(_out);
                 return "";
             } else {
                 ByteArrayOutputStream baos = new ByteArrayOutputStream(32*1024);
-                _context.tunnelManager().renderStatusHTML(new OutputStreamWriter(baos));
+                renderer.renderStatusHTML(new OutputStreamWriter(baos));
                 return new String(baos.toByteArray());
             }
         } catch (IOException ioe) {
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..b1536c911d2c660b86e2d75dca21499fa8710797
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/TunnelRenderer.java
@@ -0,0 +1,318 @@
+package net.i2p.router.web;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
+import net.i2p.data.Hash;
+import net.i2p.data.RouterInfo;
+import net.i2p.data.TunnelId;
+import net.i2p.router.Router;
+import net.i2p.router.RouterContext;
+import net.i2p.router.TunnelInfo;
+import net.i2p.router.TunnelPoolSettings;
+import net.i2p.router.tunnel.HopConfig;
+import net.i2p.router.tunnel.pool.TunnelPool;
+import net.i2p.stat.RateStat;
+import net.i2p.util.ObjectCounter;
+
+/**
+ * 
+ */
+public class TunnelRenderer {
+    private RouterContext _context;
+    
+    public TunnelRenderer(RouterContext ctx) {
+        _context = ctx;
+    }
+    
+    public void renderStatusHTML(Writer out) throws IOException {
+        out.write("<div class=\"wideload\"><h2><a name=\"exploratory\" ></a>Exploratory tunnels (<a href=\"/configtunnels.jsp#exploratory\">config</a>):</h2>\n");
+        renderPool(out, _context.tunnelManager().getInboundExploratoryPool(), _context.tunnelManager().getOutboundExploratoryPool());
+        
+        List<Hash> destinations = null;
+        Map<Hash, TunnelPool> clientInboundPools = _context.tunnelManager().getInboundClientPools();
+        Map<Hash, TunnelPool> clientOutboundPools = _context.tunnelManager().getOutboundClientPools();
+        destinations = new ArrayList(clientInboundPools.keySet());
+        for (int i = 0; i < destinations.size(); i++) {
+            Hash client = destinations.get(i);
+            TunnelPool in = null;
+            TunnelPool outPool = null;
+            in = clientInboundPools.get(client);
+            outPool = clientOutboundPools.get(client);
+            String name = (in != null ? in.getSettings().getDestinationNickname() : null);
+            if ( (name == null) && (outPool != null) )
+                name = outPool.getSettings().getDestinationNickname();
+            if (name == null)
+                name = client.toBase64().substring(0,4);
+            out.write("<h2><a name=\"" + client.toBase64().substring(0,4)
+                      + "\" ></a>Client tunnels for " + name);
+            if (_context.clientManager().isLocal(client))
+                out.write(" (<a href=\"/configtunnels.jsp#" + client.toBase64().substring(0,4) +"\">config</a>):</h2>\n");
+            else
+                out.write(" (dead):</h2>\n");
+            renderPool(out, in, outPool);
+        }
+        
+        List participating = _context.tunnelDispatcher().listParticipatingTunnels();
+        Collections.sort(participating, new TunnelComparator());
+        out.write("<h2><a name=\"participating\"></a>Participating tunnels:</h2><table>\n");
+        out.write("<tr><th>Receive on</th><th>From</th><th>"
+                  + "Send on</th><th>To</th><th>Expiration</th>"
+                  + "<th>Usage</th><th>Rate</th><th>Role</th></tr>\n");
+        long processed = 0;
+        RateStat rs = _context.statManager().getRate("tunnel.participatingMessageCount");
+        if (rs != null)
+            processed = (long)rs.getRate(10*60*1000).getLifetimeTotalValue();
+        int inactive = 0;
+        for (int i = 0; i < participating.size(); i++) {
+            HopConfig cfg = (HopConfig)participating.get(i);
+            if (cfg.getProcessedMessagesCount() <= 0) {
+                inactive++;
+                continue;
+            }
+            out.write("<tr>");
+            if (cfg.getReceiveTunnel() != null)
+                out.write(" <td class=\"cells\" align=\"center\">" + cfg.getReceiveTunnel().getTunnelId() +"</td>");
+            else
+                out.write(" <td class=\"cells\" align=\"center\">n/a</td>");
+            if (cfg.getReceiveFrom() != null)
+                out.write(" <td class=\"cells\" align=\"right\">" + netDbLink(cfg.getReceiveFrom()) +"</td>");
+            else
+                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
+            if (cfg.getSendTunnel() != null)
+                out.write(" <td class=\"cells\" align=\"center\">" + cfg.getSendTunnel().getTunnelId() +"</td>");
+            else
+                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
+            if (cfg.getSendTo() != null)
+                out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(cfg.getSendTo()) +"</td>");
+            else
+//                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
+                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
+            long timeLeft = cfg.getExpiration()-_context.clock().now();
+            if (timeLeft > 0)
+                out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>");
+            else
+                out.write(" <td class=\"cells\" align=\"center\">(grace period)</td>");
+            out.write(" <td class=\"cells\" align=\"center\">" + cfg.getProcessedMessagesCount() + "KB</td>");
+            int lifetime = (int) ((_context.clock().now() - cfg.getCreation()) / 1000);
+            if (lifetime <= 0)
+                lifetime = 1;
+            if (lifetime > 10*60)
+                lifetime = 10*60;
+            int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime;
+            out.write(" <td class=\"cells\" align=\"center\">" + bps + "Bps</td>");
+            if (cfg.getSendTo() == null)
+                out.write(" <td class=\"cells\" align=\"center\">Outbound Endpoint</td>");
+            else if (cfg.getReceiveFrom() == null)
+                out.write(" <td class=\"cells\" align=\"center\">Inbound Gateway</td>");
+            else
+                out.write(" <td class=\"cells\" align=\"center\">Participant</td>");
+            out.write("</tr>\n");
+            processed += cfg.getProcessedMessagesCount();
+        }
+        out.write("</table>\n");
+        out.write("<div class=\"statusnotes\"><b>Inactive participating tunnels: " + inactive + "</b></div>\n");
+        out.write("<div class=\"statusnotes\"><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B</b></div>\n");
+        renderPeers(out);
+    }
+    
+    private static class TunnelComparator implements Comparator {
+         public int compare(Object l, Object r) {
+             return (int) (((HopConfig)r).getProcessedMessagesCount() - ((HopConfig)l).getProcessedMessagesCount());
+        }
+    }
+
+    private void renderPool(Writer out, TunnelPool in, TunnelPool outPool) throws IOException {
+        List<TunnelInfo> tunnels = null;
+        if (in == null)
+            tunnels = new ArrayList();
+        else
+            tunnels = in.listTunnels();
+        if (outPool != null)
+            tunnels.addAll(outPool.listTunnels());
+        
+        long processedIn = (in != null ? in.getLifetimeProcessed() : 0);
+        long processedOut = (outPool != null ? outPool.getLifetimeProcessed() : 0);
+        
+        int live = 0;
+        int maxLength = 1;
+        for (int i = 0; i < tunnels.size(); i++) {
+            TunnelInfo info = tunnels.get(i);
+            if (info.getLength() > maxLength)
+                maxLength = info.getLength();
+        }
+        out.write("<table><tr><th>In/Out</th><th>Expiry</th><th>Usage</th><th>Gateway</th>");
+        if (maxLength > 3) {
+            out.write("<th align=\"center\" colspan=\"" + (maxLength - 2));
+            out.write("\">Participants</th>");
+        }
+        else if (maxLength == 3) {
+            out.write("<th>Participant</th>");
+        }
+        if (maxLength > 1) {
+            out.write("<th>Endpoint</th>");
+        }
+        out.write("</tr>\n");
+        for (int i = 0; i < tunnels.size(); i++) {
+            TunnelInfo info = tunnels.get(i);
+            long timeLeft = info.getExpiration()-_context.clock().now();
+            if (timeLeft <= 0)
+                continue; // don't display tunnels in their grace period
+            live++;
+            if (info.isInbound())
+                out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/inbound.png\" alt=\"Inbound\" title=\"Inbound\"></td>");
+            else
+                out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/outbound.png\" alt=\"Outbound\" title=\"Outbound\"></td>");
+            out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>\n");
+            out.write(" <td class=\"cells\" align=\"center\">" + info.getProcessedMessagesCount() + "KB</td>\n");
+            for (int j = 0; j < info.getLength(); j++) {
+                Hash peer = info.getPeer(j);
+                TunnelId id = (info.isInbound() ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j));
+                if (_context.routerHash().equals(peer)) {
+                    out.write(" <td class=\"cells\" align=\"center\">" + (id == null ? "" : "" + id) + "</td>");
+                } else {
+                    String cap = getCapacity(peer);
+                    out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(peer) + (id == null ? "" : " " + id) + cap + "</td>");                
+                }
+                if (info.getLength() < maxLength && (info.getLength() == 1 || j == info.getLength() - 2)) {
+                    for (int k = info.getLength(); k < maxLength; k++)
+                        out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
+                }
+            }
+            out.write("</tr>\n");
+            
+            if (info.isInbound()) 
+                processedIn += info.getProcessedMessagesCount();
+            else
+                processedOut += info.getProcessedMessagesCount();
+        }
+        out.write("</table>\n");
+        if (in != null) {
+            List pending = in.listPending();
+            if (pending.size() > 0)
+                out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " inbound</b></center></div>\n");
+            live += pending.size();
+        }
+        if (outPool != null) {
+            List pending = outPool.listPending();
+            if (pending.size() > 0)
+                out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " outbound</b></center></div>\n");
+            live += pending.size();
+        }
+        if (live <= 0)
+            out.write("<div class=\"statusnotes\"><center><b>No tunnels; waiting for the grace period to end.</center></b></div>\n");
+        out.write("<div class=\"statusnotes\"><center><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processedIn*1024) + "B in, " +
+                  DataHelper.formatSize(processedOut*1024) + "B out</b></center></div>");
+    }
+    
+    private void renderPeers(Writer out) throws IOException {
+        // count up the peers in the local pools
+        ObjectCounter<Hash> lc = new ObjectCounter();
+        int tunnelCount = countTunnelsPerPeer(lc);
+
+        // count up the peers in the participating tunnels
+        ObjectCounter<Hash> pc = new ObjectCounter();
+        int partCount = countParticipatingPerPeer(pc);
+
+        Set<Hash> peers = new HashSet(lc.objects());
+        peers.addAll(pc.objects());
+        List<Hash> peerList = new ArrayList(peers);
+        Collections.sort(peerList, new HashComparator());
+
+        out.write("<h2><a name=\"peers\"></a>Tunnel Counts By Peer:</h2>\n");
+        out.write("<table><tr><th>Peer</th><th>Expl. + Client</th><th>% of total</th><th>Part. from + to</th><th>% of total</th></tr>\n");
+        for (Hash h : peerList) {
+             out.write("<tr> <td class=\"cells\" align=\"center\">");
+             out.write(netDbLink(h));
+             out.write(" <td class=\"cells\" align=\"center\">" + lc.count(h));
+             out.write(" <td class=\"cells\" align=\"center\">");
+             if (tunnelCount > 0)
+                 out.write("" + (lc.count(h) * 100 / tunnelCount));
+             else
+                 out.write('0');
+             out.write(" <td class=\"cells\" align=\"center\">" + pc.count(h));
+             out.write(" <td class=\"cells\" align=\"center\">");
+             if (partCount > 0)
+                 out.write("" + (pc.count(h) * 100 / partCount));
+             else
+                 out.write('0');
+             out.write('\n');
+        }
+        out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>Tunnels</b> <td align=\"center\"><b>" + tunnelCount);
+        out.write("</b> <td>&nbsp;</td> <td align=\"center\"><b>" + partCount);
+        out.write("</b> <td>&nbsp;</td></tr></table></div>\n");
+    }
+
+    /* duplicate of that in tunnelPoolManager for now */
+    /** @return total number of non-fallback expl. + client tunnels */
+    private int countTunnelsPerPeer(ObjectCounter<Hash> lc) {
+        List<TunnelPool> pools = new ArrayList();
+        _context.tunnelManager().listPools(pools);
+        int tunnelCount = 0;
+        for (TunnelPool tp : pools) {
+            for (TunnelInfo info : tp.listTunnels()) {
+                if (info.getLength() > 1) {
+                    tunnelCount++;
+                    for (int j = 0; j < info.getLength(); j++) {
+                        Hash peer = info.getPeer(j);
+                        if (!_context.routerHash().equals(peer))
+                            lc.increment(peer);
+                    }
+                }
+            }
+        }
+        return tunnelCount;
+    }
+
+    /** @return total number of part. tunnels */
+    private int countParticipatingPerPeer(ObjectCounter<Hash> pc) {
+        List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels();
+        for (HopConfig cfg : participating) {
+            Hash from = cfg.getReceiveFrom();
+            if (from != null)
+                pc.increment(from);
+            Hash to = cfg.getSendTo();
+            if (to != null)
+                pc.increment(to);
+        }
+        return participating.size();
+    }
+
+    private static class HashComparator implements Comparator {
+         public int compare(Object l, Object r) {
+             return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64());
+        }
+    }
+
+    private String getCapacity(Hash peer) {
+        RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
+        if (info != null) {
+            String caps = info.getCapabilities();
+            for (char c = Router.CAPABILITY_BW12; c <= Router.CAPABILITY_BW256; c++) {
+                if (caps.indexOf(c) >= 0)
+                    return " " + c;
+            }
+        }
+        return "";
+    }
+
+    private String netDbLink(Hash peer) {
+        return _context.commSystem().renderPeerHTML(peer);
+    }
+
+    /** translate a string */
+    private String _(String s) {
+        return Messages.getString(s, _context);
+    }
+}
diff --git a/apps/routerconsole/jsp/configupdate.jsp b/apps/routerconsole/jsp/configupdate.jsp
index 62677bbb041fdd3cd5d23229efbfe98a7690e8cd..e7562de9f7106e1d9eec4cb853f6ec5cf6da6c85 100644
--- a/apps/routerconsole/jsp/configupdate.jsp
+++ b/apps/routerconsole/jsp/configupdate.jsp
@@ -2,8 +2,9 @@
 <%@page pageEncoding="UTF-8"%>
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 
-<html><head><title>I2P Router Console - config update</title>
+<html><head>
 <%@include file="css.jsp" %>
+<%=cssHelper.title("config update")%>
 </head><body>
 
 <%@include file="summary.jsp" %>
@@ -36,7 +37,7 @@
           <td><input type="text" size="60" name="newsURL" value="<jsp:getProperty name="updatehelper" property="newsURL" />"></td>
         </tr><tr><td class= "mediumtags" align="right"><b>Refresh frequency:</b>
           <td><jsp:getProperty name="updatehelper" property="refreshFrequencySelectBox" /></td><tr>
-          <td class= "mediumtags" align="right"><b>Update policy:</b></td>
+          <td class= "mediumtags" align="right"><b><%=formhandler._("Update policy")%>:</b></td>
           <td><jsp:getProperty name="updatehelper" property="updatePolicySelectBox" /></td>
         <tr><td class= "mediumtags" align="right"><b>Update through the eepProxy?</b></td>
           <td><jsp:getProperty name="updatehelper" property="updateThroughProxy" /></td>
diff --git a/apps/routerconsole/jsp/css.jsp b/apps/routerconsole/jsp/css.jsp
index ed86750308884b6e4698a974b256ab53e0d48cc1..865e907235f5565ec35b8b0aef506d0c8a36a283 100644
--- a/apps/routerconsole/jsp/css.jsp
+++ b/apps/routerconsole/jsp/css.jsp
@@ -25,5 +25,8 @@
 <link rel="shortcut icon" href="/themes/console/images/favicon.ico">
 <jsp:useBean class="net.i2p.router.web.CSSHelper" id="cssHelper" scope="request" />
 <jsp:setProperty name="cssHelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
+<%
+   cssHelper.setLang(request.getParameter("lang"));
+%>
 <link href="<%=cssHelper.getTheme(request.getHeader("User-Agent"))%>console.css" rel="stylesheet" type="text/css">
 <!--[if IE]><link href="/themes/console/classic/ieshim.css" rel="stylesheet" type="text/css" /><![endif]-->
diff --git a/apps/routerconsole/jsp/index.jsp b/apps/routerconsole/jsp/index.jsp
index b14f8e352549d39646ce4a35bb0ecc5737e8e9c7..d2ce8ffc2d36517c0ca03aec0826c66e79c7bba4 100644
--- a/apps/routerconsole/jsp/index.jsp
+++ b/apps/routerconsole/jsp/index.jsp
@@ -29,5 +29,6 @@ if (System.getProperty("router.consoleNonce") == null) {
  <jsp:setProperty name="contenthelper" property="page" value="<%=fpath.getAbsolutePath()%>" />
  <jsp:setProperty name="contenthelper" property="maxLines" value="300" />
  <jsp:setProperty name="contenthelper" property="lang" value="<%=request.getParameter("lang")%>" />
+ <jsp:setProperty name="contenthelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
  <jsp:getProperty name="contenthelper" property="content" />
 </div></body></html>
diff --git a/apps/routerconsole/jsp/summarynoframe.jsp b/apps/routerconsole/jsp/summarynoframe.jsp
index c772a8ffc6ae0e09ca5c998717b6598177ab84dc..33a029e7657d5bd04a8520b7d027bc8dec89b5e7 100644
--- a/apps/routerconsole/jsp/summarynoframe.jsp
+++ b/apps/routerconsole/jsp/summarynoframe.jsp
@@ -7,126 +7,29 @@
 %>
 <jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
 <jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
+<jsp:setProperty name="helper" property="action" value="<%=request.getParameter("action")%>" />
+<jsp:setProperty name="helper" property="updateNonce" value="<%=request.getParameter("updateNonce")%>" />
+<jsp:setProperty name="helper" property="consoleNonce" value="<%=request.getParameter("consoleNonce")%>" />
+<jsp:setProperty name="helper" property="requestURI" value="<%=request.getRequestURI()%>" />
+<jsp:setProperty name="helper" property="writer" value="<%=out%>" />
+<%
+/*
+ * The following is required for the reseed button to work, although we probably
+ * only need the reseedNonce property.
+ */
+%>
 <jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
 <jsp:setProperty name="reseed" property="*" />
+<%
+/*
+ * The following is required for the update buttons to work, although we probably
+ * only need the updateNonce property.
+ */
+%>
 <jsp:useBean class="net.i2p.router.web.UpdateHandler" id="update" scope="request" />
 <jsp:setProperty name="update" property="*" />
 <jsp:setProperty name="update" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
-<a href="index.jsp" target="_top"><img src="/themes/console/images/i2plogo.png" alt="I2P Router Console" title="I2P Router Console"></a><hr>
-<% java.io.File lpath = new java.io.File(net.i2p.I2PAppContext.getGlobalContext().getBaseDir(), "docs/toolbar.html");
-    // you better have target="_top" for the links in there...
-    if (lpath.exists()) { %>
-<jsp:useBean class="net.i2p.router.web.ContentHelper" id="linkhelper" scope="request" />
-<jsp:setProperty name="linkhelper" property="page" value="<%=lpath.getAbsolutePath()%>" />
-<jsp:setProperty name="linkhelper" property="maxLines" value="100" />
-<jsp:getProperty name="linkhelper" property="content" />
-<% } else { %>
-<h3><a href="/configclients.jsp" target="_top" title="Configure startup of clients and webapps (services); manually start dormant services.">I2P Services</a></h3>
-<hr><table>
-<tr><td><a href="susidns/index.jsp" target="_blank" title="Manage your I2P hosts file here (I2P domain name resolution).">Addressbook</a>
-<a href="i2psnark/" target="_blank" title="Built-in anonymous BitTorrent Client">Torrents</a>
-<a href="susimail/susimail" target="blank" title="Anonymous webmail client.">Webmail</a>
-<a href="http://127.0.0.1:7658/" target="_blank" title="Anonymous resident webserver.">Webserver</a></td></tr></table>
-<hr><h3><a href="config.jsp" target="_top" title="Configure I2P Router.">I2P Internals</a></h3><hr>
-<table><tr><td>
-<a href="tunnels.jsp" target="_top" title="View existing tunnels and tunnel build status.">Tunnels</a>
-<a href="peers.jsp" target="_top" title="Show all current peer connections.">Peers</a>
-<a href="profiles.jsp" target="_top" title="Show recent peer performance profiles.">Profiles</a>
-<a href="netdb.jsp" target="_top" title="Show list of all known I2P routers.">NetDB</a>
-<a href="logs.jsp" target="_top" title="Health Report.">Logs</a>
-<a href="jobs.jsp" target="_top" title="Show the router's workload, and how it's performing.">Jobs</a>
-<a href="graphs.jsp" target="_top" title="Graph router performance.">Graphs</a>
-<a href="oldstats.jsp" target="_top" title="Textual router performance statistics.">Stats</a></td></tr></table>
-<% } %>
-<hr><h3><a href="help.jsp" target="_top" title="I2P Router Help.">General</a></h3><hr>
-<h4><a title="Your unique I2P router identity is <jsp:getProperty name="helper" property="ident" />, never reveal it to anyone" href="netdb.jsp?r=." target="_top">Local Identity</a></h4><hr>
-<table><tr><td align="left">
-<b>Version:</b></td>
-<td align="right"><jsp:getProperty name="helper" property="version" /></td></tr>
-<tr title="How long we've been running for this session.">
-<td align="left"><b>Uptime:</b></td>
-<td align="right"><jsp:getProperty name="helper" property="uptime" />
-</td></tr></table>
-<hr><h4><a href="config.jsp#help" target="_top" title="Help with configuring your firewall and router for optimal I2P performance."><jsp:getProperty name="helper" property="reachability" /></a></h4><hr>
 <%
-    if (helper.updateAvailable() || helper.unsignedUpdateAvailable()) {
-        // display all the time so we display the final failure message
-        out.print("<br>" + net.i2p.router.web.UpdateHandler.getStatus());
-        if ("true".equals(System.getProperty("net.i2p.router.web.UpdateHandler.updateInProgress"))) {
-        } else if((!update.isDone()) &&
-                  request.getParameter("action") == null &&
-                  request.getParameter("updateNonce") == null &&
-                  net.i2p.router.web.ConfigRestartBean.getRestartTimeRemaining() > 12*60*1000) {
-            long nonce = new java.util.Random().nextLong();
-            String prev = System.getProperty("net.i2p.router.web.UpdateHandler.nonce");
-            if (prev != null) System.setProperty("net.i2p.router.web.UpdateHandler.noncePrev", prev);
-            System.setProperty("net.i2p.router.web.UpdateHandler.nonce", nonce+"");
-            String uri = request.getRequestURI();
-            out.print("<form action=\"" + uri + "\" method=\"GET\">\n");
-            out.print("<input type=\"hidden\" name=\"updateNonce\" value=\"" + nonce + "\" />\n");
-            if (helper.updateAvailable())
-                out.print("<button type=\"submit\" name=\"updateAction\" value=\"signed\" >Download " + helper.getUpdateVersion() + " Update</button>\n");
-            if (helper.unsignedUpdateAvailable())
-                out.print("<button type=\"submit\" name=\"updateAction\" value=\"Unsigned\" >Download Unsigned<br>Update " + helper.getUnsignedUpdateVersion() + "</button>\n");
-            out.print("</form>\n");
-        }
-    }
+    // moved to java for ease of translation and to avoid 30 copies
+    helper.renderSummaryBar();
 %>
-<p>
-<%=net.i2p.router.web.ConfigRestartBean.renderStatus(request.getRequestURI(), request.getParameter("action"), request.getParameter("consoleNonce"))%>
-</p><hr><h3><a href="peers.jsp" target="_top" title="Show all current peer connections.">Peers</a></h3><hr>
-<table>
-<tr><td align="left"><b>Active:</b></td><td align="right"><jsp:getProperty name="helper" property="activePeers" />/<jsp:getProperty name="helper" property="activeProfiles" /></td></tr>
-<tr><td align="left"><b>Fast:</b></td><td align="right"><jsp:getProperty name="helper" property="fastPeers" /></td></tr>
-<tr><td align="left"><b>High capacity:</b></td><td align="right"><jsp:getProperty name="helper" property="highCapacityPeers" /></td></tr>
-<tr><td align="left"><b>Integrated:</b></td><td align="right"><jsp:getProperty name="helper" property="wellIntegratedPeers" /></td></tr>
-<tr><td align="left"><b>Known:</b></td><td align="right"><jsp:getProperty name="helper" property="allPeers" /></td></tr>
-</table><hr>
-<%
-    if (helper.showFirewallWarning()) {
-        %><h4><a href="config.jsp" target="_top" title="Help with firewall configuration.">Check NAT/firewall</a></h4><%
-    }
-    boolean reseedInProgress = Boolean.valueOf(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress")).booleanValue();
-    // If showing the reseed link is allowed
-    if (helper.allowReseed()) {
-        if (reseedInProgress) {
-            // While reseed occurring, show status message instead
-            out.print("<i>" + System.getProperty("net.i2p.router.web.ReseedHandler.statusMessage","") + "</i><br>");
-        } else {
-            // While no reseed occurring, show reseed link
-            long nonce = new java.util.Random().nextLong();
-            String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
-            if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
-            System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
-            String uri = request.getRequestURI();
-            out.print("<form action=\"" + uri + "\" method=\"GET\">\n");
-            out.print("<input type=\"hidden\" name=\"reseedNonce\" value=\"" + nonce + "\" />\n");
-            out.print("<button type=\"submit\" >Reseed</button></form>\n");
-        }
-    }
-    // If a new reseed ain't running, and the last reseed had errors, show error message
-    if (!reseedInProgress) {
-        String reseedErrorMessage = System.getProperty("net.i2p.router.web.ReseedHandler.errorMessage","");
-        if (reseedErrorMessage.length() > 0) {
-            out.print("<i>" + reseedErrorMessage + "</i><br>");
-        }
-    }
- %>
-<hr><h3><a href="config.jsp" title="Configure router bandwidth allocation." target="_top">Bandwidth in/out</a></h3><hr>
-<table>
-<tr><td align="left"><b>1s:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundSecondKBps" />/<jsp:getProperty name="helper" property="outboundSecondKBps" />K/s</td></tr>
-<tr><td align="left"><b>5m:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundFiveMinuteKBps" />/<jsp:getProperty name="helper" property="outboundFiveMinuteKBps" />K/s</td></tr>
-<tr><td align="left"><b>Total:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundLifetimeKBps" />/<jsp:getProperty name="helper" property="outboundLifetimeKBps" />K/s</td></tr>
-<tr><td align="left"><b>Used:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundTransferred" />/<jsp:getProperty name="helper" property="outboundTransferred" /></td></tr></table>
-<hr><h3><a href="tunnels.jsp" target="_top" title="View existing tunnels and tunnel build status.">Tunnels in/out</a></h3><hr>
-<table>
-<tr><td align="left"><b>Exploratory:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundTunnels" />/<jsp:getProperty name="helper" property="outboundTunnels" /></td></tr>
-<tr><td align="left"><b>Client:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundClientTunnels" />/<jsp:getProperty name="helper" property="outboundClientTunnels" /></td></tr>
-<tr><td align="left"><b>Participating:</b></td><td align="right"><jsp:getProperty name="helper" property="participatingTunnels" /></td></tr>
-</table><hr><h3><a href="/jobs.jsp" target="_top" title="What's in the router's job queue?">Congestion</a></h3><hr>
-<table>
-<tr><td align="left"><b>Job lag:</b></td><td align="right"><jsp:getProperty name="helper" property="jobLag" /></td></tr>
-<tr><td align="left"><b>Message delay:</b></td><td align="right"><jsp:getProperty name="helper" property="messageDelay" /></td></tr>
-<tr><td align="left"><b>Tunnel lag:</b></td><td align="right"><jsp:getProperty name="helper" property="tunnelLag" /></td></tr>
-<tr><td align="left"><b>Backlog:</b></td><td align="right"><jsp:getProperty name="helper" property="inboundBacklog" /></td></tr>
-</table><hr><h4><jsp:getProperty name="helper" property="tunnelStatus" /></h4><hr><jsp:getProperty name="helper" property="destinations" />
diff --git a/apps/routerconsole/locale/messages_de.po b/apps/routerconsole/locale/messages_de.po
new file mode 100644
index 0000000000000000000000000000000000000000..905828c4d097225a891fb0a58699c6c90033d429
--- /dev/null
+++ b/apps/routerconsole/locale/messages_de.po
@@ -0,0 +1,281 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo <foo@bar>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P routerconsole\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-10-20 11:55+0000\n"
+"PO-Revision-Date: 2009-10-19 12:50+0000\n"
+"Last-Translator: foo <foo@bar>\n"
+"Language-Team: foo <foo@bar>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: German\n"
+
+#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configupdate_jsp.java:95
+msgid "config update"
+msgstr "config update in german test test test"
+
+#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configupdate_jsp.java:334
+msgid "Update policy"
+msgstr "Update policy in german foobarbaz"
+
+#: src/net/i2p/router/web/CSSHelper.java:36
+#: src/net/i2p/router/web/SummaryBarRenderer.java:26
+#: src/net/i2p/router/web/SummaryBarRenderer.java:28
+msgid "I2P Router Console"
+msgstr ""
+
+#: src/net/i2p/router/web/ConfigUpdateHelper.java:90
+msgid "Notify only"
+msgstr "Notify only in german"
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:42
+msgid "I2P Services"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:48
+msgid "Manage your I2P hosts file here (I2P domain name resolution)"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:50
+msgid "Addressbook"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:54
+msgid "Built-in anonymous BitTorrent Client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:56
+msgid "Torrents"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:60
+msgid "Anonymous webmail client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:62
+msgid "Webmail"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:66
+msgid "Anonymous resident webserver"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:68
+msgid "Webserver"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:72
+msgid "Configure I2P Router"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:74
+msgid "I2P Internals"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:80
+#: src/net/i2p/router/web/SummaryBarRenderer.java:344
+msgid "View existing tunnels and tunnel build status"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:82
+msgid "Tunnels"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:86
+#: src/net/i2p/router/web/SummaryBarRenderer.java:221
+msgid "Show all current peer connections"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:88
+#: src/net/i2p/router/web/SummaryBarRenderer.java:223
+msgid "Peers"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:92
+msgid "Show recent peer performance profiles"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:94
+msgid "Profiles"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:98
+msgid "Show list of all known I2P routers"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:100
+msgid "NetDB"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:104
+msgid "Health Report"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:106
+msgid "Logs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:110
+msgid "Show the router's workload, and how it's performing"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:112
+msgid "Jobs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:116
+msgid "Graph router performance"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:118
+msgid "Graphs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:122
+msgid "Textual router performance statistics"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:124
+msgid "Stats"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:134
+msgid "I2P Router Help"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:136
+msgid "General"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:139
+msgid "Your unique I2P router identity is"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:143
+msgid "never reveal it to anyone"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:145
+msgid "Local Identity"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:150
+msgid "Version"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:157
+msgid "How long we've been running for this session"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:160
+msgid "Uptime"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:167
+msgid ""
+"Help with configuring your firewall and router for optimal I2P performance"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:194
+msgid "Download"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:198
+#: src/net/i2p/router/web/SummaryBarRenderer.java:207
+msgid "Update"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:203
+msgid "Download Unsigned"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:229
+msgid "Active"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:237
+msgid "Fast"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:243
+msgid "High capacity"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:249
+msgid "Integrated"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:255
+msgid "Known"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:270
+msgid "Help with firewall configuration"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:272
+msgid "Check NAT/firewall"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:309
+msgid "Configure router bandwidth allocation"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:311
+msgid "Bandwidth in/out"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:328
+msgid "Total"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:336
+msgid "Used"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:346
+msgid "Tunnels in/out"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:351
+msgid "Exploratory"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:359
+msgid "Client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:367
+msgid "Participating"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:373
+msgid "What's in the router's job queue?"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:375
+msgid "Congestion"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:380
+msgid "Job lag"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:386
+msgid "Message delay"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:392
+msgid "Tunnel lag"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:398
+msgid "Backlog"
+msgstr ""
diff --git a/apps/routerconsole/locale/messages_zh.po b/apps/routerconsole/locale/messages_zh.po
new file mode 100644
index 0000000000000000000000000000000000000000..8456438ca91dd9b24cbc25242d045c425a1179b0
--- /dev/null
+++ b/apps/routerconsole/locale/messages_zh.po
@@ -0,0 +1,281 @@
+# I2P
+# Copyright (C) 2009 The I2P Project
+# This file is distributed under the same license as the routerconsole package.
+# To contribute translations, see http://www.i2p2.de/newdevelopers
+# foo <foo@bar>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: I2P routerconsole\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-10-20 11:55+0000\n"
+"PO-Revision-Date: 2009-10-19 12:59+0000\n"
+"Last-Translator: foo <foo@bar>\n"
+"Language-Team: foo <foo@par>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-Language: Chinese\n"
+
+#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configupdate_jsp.java:95
+msgid "config update"
+msgstr ""
+
+#: ../jsp/WEB-INF/classes/net/i2p/router/web/jsp/configupdate_jsp.java:334
+msgid "Update policy"
+msgstr ""
+
+#: src/net/i2p/router/web/CSSHelper.java:36
+#: src/net/i2p/router/web/SummaryBarRenderer.java:26
+#: src/net/i2p/router/web/SummaryBarRenderer.java:28
+msgid "I2P Router Console"
+msgstr ""
+
+#: src/net/i2p/router/web/ConfigUpdateHelper.java:90
+msgid "Notify only"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:42
+msgid "I2P Services"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:48
+msgid "Manage your I2P hosts file here (I2P domain name resolution)"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:50
+msgid "Addressbook"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:54
+msgid "Built-in anonymous BitTorrent Client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:56
+msgid "Torrents"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:60
+msgid "Anonymous webmail client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:62
+msgid "Webmail"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:66
+msgid "Anonymous resident webserver"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:68
+msgid "Webserver"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:72
+msgid "Configure I2P Router"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:74
+msgid "I2P Internals"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:80
+#: src/net/i2p/router/web/SummaryBarRenderer.java:344
+msgid "View existing tunnels and tunnel build status"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:82
+msgid "Tunnels"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:86
+#: src/net/i2p/router/web/SummaryBarRenderer.java:221
+msgid "Show all current peer connections"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:88
+#: src/net/i2p/router/web/SummaryBarRenderer.java:223
+msgid "Peers"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:92
+msgid "Show recent peer performance profiles"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:94
+msgid "Profiles"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:98
+msgid "Show list of all known I2P routers"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:100
+msgid "NetDB"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:104
+msgid "Health Report"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:106
+msgid "Logs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:110
+msgid "Show the router's workload, and how it's performing"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:112
+msgid "Jobs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:116
+msgid "Graph router performance"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:118
+msgid "Graphs"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:122
+msgid "Textual router performance statistics"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:124
+msgid "Stats"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:134
+msgid "I2P Router Help"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:136
+msgid "General"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:139
+msgid "Your unique I2P router identity is"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:143
+msgid "never reveal it to anyone"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:145
+msgid "Local Identity"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:150
+msgid "Version"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:157
+msgid "How long we've been running for this session"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:160
+msgid "Uptime"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:167
+msgid ""
+"Help with configuring your firewall and router for optimal I2P performance"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:194
+msgid "Download"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:198
+#: src/net/i2p/router/web/SummaryBarRenderer.java:207
+msgid "Update"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:203
+msgid "Download Unsigned"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:229
+msgid "Active"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:237
+msgid "Fast"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:243
+msgid "High capacity"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:249
+msgid "Integrated"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:255
+msgid "Known"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:270
+msgid "Help with firewall configuration"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:272
+msgid "Check NAT/firewall"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:309
+msgid "Configure router bandwidth allocation"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:311
+msgid "Bandwidth in/out"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:328
+msgid "Total"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:336
+msgid "Used"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:346
+msgid "Tunnels in/out"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:351
+msgid "Exploratory"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:359
+msgid "Client"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:367
+msgid "Participating"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:373
+msgid "What's in the router's job queue?"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:375
+msgid "Congestion"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:380
+msgid "Job lag"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:386
+msgid "Message delay"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:392
+msgid "Tunnel lag"
+msgstr ""
+
+#: src/net/i2p/router/web/SummaryBarRenderer.java:398
+msgid "Backlog"
+msgstr ""
diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java
index 1437b9f4aebbadca5caa177bc43a6c58edb043b1..829b91d7b2b2b915fd29cbe2615dc323f007fb1b 100644
--- a/core/java/src/net/i2p/util/LogWriter.java
+++ b/core/java/src/net/i2p/util/LogWriter.java
@@ -153,13 +153,13 @@ class LogWriter implements Runnable {
             if (!parent.exists()) {
                 boolean ok = parent.mkdirs();
                 if (!ok) {
-                    System.err.println("Unable to create the parent directy: " + parent.getAbsolutePath());
-                    System.exit(0);
+                    System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath());
+                    //System.exit(0);
                 }
             }
             if (!parent.isDirectory()) {
                 System.err.println("wtf, we cannot put the logs in a subdirectory of a plain file!  we want to stre the log as " + f.getAbsolutePath());
-                System.exit(0);
+                //System.exit(0);
             }
         }
         try {
diff --git a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
index e241ff1f4a349b294d8cdbad5ad94939faef186f..757549e49a7fe86699d932374f9071c7846f9a23 100644
--- a/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
+++ b/router/java/src/net/i2p/data/i2np/I2NPMessageImpl.java
@@ -362,10 +362,10 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
                 return new TunnelGatewayMessage(context);
             case DataMessage.MESSAGE_TYPE:
                 return new DataMessage(context);
-            case TunnelCreateMessage.MESSAGE_TYPE:
-                return new TunnelCreateMessage(context);
-            case TunnelCreateStatusMessage.MESSAGE_TYPE:
-                return new TunnelCreateStatusMessage(context);
+            //case TunnelCreateMessage.MESSAGE_TYPE:
+            //    return new TunnelCreateMessage(context);
+            //case TunnelCreateStatusMessage.MESSAGE_TYPE:
+            //    return new TunnelCreateStatusMessage(context);
             case TunnelBuildMessage.MESSAGE_TYPE:
                 return new TunnelBuildMessage(context);
             case TunnelBuildReplyMessage.MESSAGE_TYPE:
diff --git a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java
index e192e7752ca3b5638a38c2517439e516b193e1ce..5c5e5a0a45336a919124697812a2dac61a3d4ee9 100644
--- a/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/DummyNetworkDatabaseFacade.java
@@ -60,6 +60,4 @@ class DummyNetworkDatabaseFacade extends NetworkDatabaseFacade {
     
     public Set<Hash> getAllRouters() { return new HashSet(_routers.keySet()); }
     public Set findNearestRouters(Hash key, int maxNumRouters, Set peersToIgnore) { return new HashSet(_routers.values()); }
-
-    public void renderStatusHTML(Writer out) throws IOException {}
 }
diff --git a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java
index 3a19cd1178946638d7023e10b9e466719663bfe0..4b8fb486e45f7e395c333fdb0cf0309319ad6ef3 100644
--- a/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java
+++ b/router/java/src/net/i2p/router/DummyTunnelManagerFacade.java
@@ -10,11 +10,14 @@ package net.i2p.router;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.data.TunnelId;
+import net.i2p.router.tunnel.pool.TunnelPool;
 
 /**
  * Build and maintain tunnels throughout the network.
@@ -50,4 +53,10 @@ class DummyTunnelManagerFacade implements TunnelManagerFacade {
     public void restart() {}
     public void shutdown() {}
     public void startup() {}
+
+    public void listPools(List<TunnelPool> out) {}
+    public Map<Hash, TunnelPool> getInboundClientPools() { return null; }
+    public Map<Hash, TunnelPool> getOutboundClientPools() { return null; }
+    public TunnelPool getInboundExploratoryPool() { return null; }
+    public TunnelPool getOutboundExploratoryPool() { return null; }
 }
diff --git a/router/java/src/net/i2p/router/InNetMessagePool.java b/router/java/src/net/i2p/router/InNetMessagePool.java
index 802aedec0a6903aa2b15dc2fbd837073a00cdb62..1d3cac39ddf59b46fa716e53934b1e12549fe374 100644
--- a/router/java/src/net/i2p/router/InNetMessagePool.java
+++ b/router/java/src/net/i2p/router/InNetMessagePool.java
@@ -18,8 +18,8 @@ import net.i2p.data.i2np.DatabaseLookupMessage;
 import net.i2p.data.i2np.DatabaseSearchReplyMessage;
 import net.i2p.data.i2np.DeliveryStatusMessage;
 import net.i2p.data.i2np.I2NPMessage;
-import net.i2p.data.i2np.TunnelCreateMessage;
-import net.i2p.data.i2np.TunnelCreateStatusMessage;
+//import net.i2p.data.i2np.TunnelCreateMessage;
+//import net.i2p.data.i2np.TunnelCreateStatusMessage;
 import net.i2p.data.i2np.TunnelDataMessage;
 import net.i2p.data.i2np.TunnelGatewayMessage;
 import net.i2p.util.I2PThread;
@@ -74,7 +74,7 @@ public class InNetMessagePool implements Service {
         _context.statManager().createRateStat("inNetPool.dropped", "How often do we drop a message", "InNetPool", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
         _context.statManager().createRateStat("inNetPool.droppedDeliveryStatusDelay", "How long after a delivery status message is created do we receive it back again (for messages that are too slow to be handled)", "InNetPool", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
         _context.statManager().createRateStat("inNetPool.duplicate", "How often do we receive a duplicate message", "InNetPool", new long[] { 60*1000l, 60*60*1000l, 24*60*60*1000l });
-        _context.statManager().createRateStat("inNetPool.droppedTunnelCreateStatusMessage", "How often we drop a slow-to-arrive tunnel request response", "InNetPool", new long[] { 60*60*1000l, 24*60*60*1000l });
+        //_context.statManager().createRateStat("inNetPool.droppedTunnelCreateStatusMessage", "How often we drop a slow-to-arrive tunnel request response", "InNetPool", new long[] { 60*60*1000l, 24*60*60*1000l });
         _context.statManager().createRateStat("inNetPool.droppedDbLookupResponseMessage", "How often we drop a slow-to-arrive db search response", "InNetPool", new long[] { 60*60*1000l, 24*60*60*1000l });
         _context.statManager().createRateStat("pool.dispatchDataTime", "How long a tunnel dispatch takes", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
         _context.statManager().createRateStat("pool.dispatchGatewayTime", "How long a tunnel gateway dispatch takes", "Tunnels", new long[] { 10*60*1000l, 60*60*1000l, 24*60*60*1000l });
@@ -125,8 +125,8 @@ public class InNetMessagePool implements Service {
         
         if (invalidReason != null) {
             int level = Log.WARN;
-            if (messageBody instanceof TunnelCreateMessage)
-                level = Log.INFO;
+            //if (messageBody instanceof TunnelCreateMessage)
+            //    level = Log.INFO;
             if (_log.shouldLog(level))
                 _log.log(level, "Duplicate message received [" + messageBody.getUniqueId() 
                           + " expiring on " + exp + "]: " + messageBody.getClass().getName() + ": " + invalidReason 
@@ -195,10 +195,10 @@ public class InNetMessagePool implements Service {
                                 _log.warn("Dropping unhandled delivery status message created " + timeSinceSent + "ms ago: " + messageBody);
                             _context.statManager().addRateData("inNetPool.droppedDeliveryStatusDelay", timeSinceSent, timeSinceSent);
                         }
-                    } else if (type == TunnelCreateStatusMessage.MESSAGE_TYPE) {
-                        if (_log.shouldLog(Log.INFO))
-                            _log.info("Dropping slow tunnel create request response: " + messageBody);
-                        _context.statManager().addRateData("inNetPool.droppedTunnelCreateStatusMessage", 1, 0);
+                    //} else if (type == TunnelCreateStatusMessage.MESSAGE_TYPE) {
+                    //    if (_log.shouldLog(Log.INFO))
+                    //        _log.info("Dropping slow tunnel create request response: " + messageBody);
+                    //    _context.statManager().addRateData("inNetPool.droppedTunnelCreateStatusMessage", 1, 0);
                     } else if (type == DatabaseSearchReplyMessage.MESSAGE_TYPE) {
                         if (_log.shouldLog(Log.INFO))
                             _log.info("Dropping slow db lookup response: " + messageBody);
diff --git a/router/java/src/net/i2p/router/MessageHistory.java b/router/java/src/net/i2p/router/MessageHistory.java
index a116aaca7cc209cc889ad894d9d31dc1af2522a3..4db6c47c68127f6b7752b00d8f5295f5661eb136 100644
--- a/router/java/src/net/i2p/router/MessageHistory.java
+++ b/router/java/src/net/i2p/router/MessageHistory.java
@@ -39,9 +39,8 @@ public class MessageHistory {
     private final static byte[] NL = System.getProperty("line.separator").getBytes();
     private final static int FLUSH_SIZE = 1000; // write out at least once every 1000 entries
         
-    /** config property determining whether we want to debug with the message history */
+    /** config property determining whether we want to debug with the message history - default false */
     public final static String PROP_KEEP_MESSAGE_HISTORY = "router.keepHistory";
-    public final static boolean DEFAULT_KEEP_MESSAGE_HISTORY = false;
     /** config property determining where we want to log the message history, if we're keeping one */
     public final static String PROP_MESSAGE_HISTORY_FILENAME = "router.historyFilename";
     public final static String DEFAULT_MESSAGE_HISTORY_FILENAME = "messageHistory.txt";
@@ -67,19 +66,8 @@ public class MessageHistory {
     String getFilename() { return _historyFile; }
     
     private void updateSettings() {
-        String keepHistory = _context.router().getConfigSetting(PROP_KEEP_MESSAGE_HISTORY);
-        if (keepHistory != null) {
-            _doLog = Boolean.TRUE.toString().equalsIgnoreCase(keepHistory);
-        } else {
-            _doLog = DEFAULT_KEEP_MESSAGE_HISTORY;
-        }
-
-        String filename = null;
-        if (_doLog) {
-            filename = _context.router().getConfigSetting(PROP_MESSAGE_HISTORY_FILENAME);
-            if ( (filename == null) || (filename.trim().length() <= 0) )
-                filename = DEFAULT_MESSAGE_HISTORY_FILENAME;
-        }
+        _doLog = Boolean.valueOf(_context.getProperty(PROP_KEEP_MESSAGE_HISTORY)).booleanValue();
+        _historyFile = _context.getProperty(PROP_MESSAGE_HISTORY_FILENAME, DEFAULT_MESSAGE_HISTORY_FILENAME);
     }
     
     /**
@@ -96,13 +84,6 @@ public class MessageHistory {
             _reinitializeJob.getTiming().setStartAfter(_context.clock().now()+5000);
             _context.jobQueue().addJob(_reinitializeJob);
         } else {
-            String filename = null;
-            filename = _context.router().getConfigSetting(PROP_MESSAGE_HISTORY_FILENAME);
-            if ( (filename == null) || (filename.trim().length() <= 0) )
-                filename = DEFAULT_MESSAGE_HISTORY_FILENAME;
-
-            _doLog = DEFAULT_KEEP_MESSAGE_HISTORY;
-            _historyFile = filename;
             _localIdent = getName(_context.routerHash());
             // _unwrittenEntries = new ArrayList(64);
             updateSettings();
@@ -142,6 +123,7 @@ public class MessageHistory {
      * @param replyTunnel the tunnel sourceRoutePeer should forward the source routed message to
      * @param replyThrough the gateway of the tunnel that the sourceRoutePeer will be sending to
      */
+/********
     public void requestTunnelCreate(TunnelId createTunnel, TunnelId outTunnel, Hash peerRequested, Hash nextPeer, TunnelId replyTunnel, Hash replyThrough) {
         if (!_doLog) return;
         StringBuilder buf = new StringBuilder(128);
@@ -156,6 +138,7 @@ public class MessageHistory {
             buf.append("who forwards it through [").append(replyTunnel.getTunnelId()).append("] on [").append(getName(replyThrough)).append("]");
         addEntry(buf.toString());
     }
+*********/
     
     /**
      * The local router has received a request to join the createTunnel with the next hop being nextPeer,
@@ -167,6 +150,7 @@ public class MessageHistory {
      * @param ok whether we will join the tunnel
      * @param sourceRoutePeer peer through whom we should send our garlic routed ok through
      */
+/*********
     public void receiveTunnelCreate(TunnelId createTunnel, Hash nextPeer, Date expire, boolean ok, Hash sourceRoutePeer) {
         if (!_doLog) return;
         StringBuilder buf = new StringBuilder(128);
@@ -177,6 +161,7 @@ public class MessageHistory {
         buf.append("ok? ").append(ok).append(" expiring on [").append(getTime(expire.getTime())).append("]");
         addEntry(buf.toString());
     }
+*********/
     
     /**
      * The local router has joined the given tunnel operating in the given state.
diff --git a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
index 865fbf8ecc33340159e10ecfd58dd374527ee4e2..3885ea6d5393ba4319da352a82d99920517986f9 100644
--- a/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/NetworkDatabaseFacade.java
@@ -10,6 +10,7 @@ package net.i2p.router;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.util.Collections;
 import java.util.Set;
 
 import net.i2p.data.Hash;
@@ -60,7 +61,10 @@ public abstract class NetworkDatabaseFacade implements Service {
     public int getKnownLeaseSets() { return 0; }
     public boolean isInitialized() { return true; }
     public void rescan() {}
-    public void renderRouterInfoHTML(Writer out, String s) throws IOException {}
-    public void renderLeaseSetHTML(Writer out) throws IOException {}
-    public void renderStatusHTML(Writer out, boolean b) throws IOException {}
+    /** @deprecated moved to router console */
+    public void renderStatusHTML(Writer out) throws IOException {}
+    /** public for NetDbRenderer in routerconsole */
+    public Set<LeaseSet> getLeases() { return Collections.EMPTY_SET; }
+    /** public for NetDbRenderer in routerconsole */
+    public Set<RouterInfo> getRouters() { return Collections.EMPTY_SET; }
 }
diff --git a/router/java/src/net/i2p/router/TunnelManagerFacade.java b/router/java/src/net/i2p/router/TunnelManagerFacade.java
index e8bdc31d380460596c721d9c710207ca6f350770..148499a43d4991ed7bddeba3485837be105c6336 100644
--- a/router/java/src/net/i2p/router/TunnelManagerFacade.java
+++ b/router/java/src/net/i2p/router/TunnelManagerFacade.java
@@ -8,11 +8,14 @@ package net.i2p.router;
  *
  */
 
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.data.TunnelId;
+import net.i2p.router.tunnel.pool.TunnelPool;
 
 /**
  * Build and maintain tunnels throughout the network.
@@ -74,4 +77,14 @@ public interface TunnelManagerFacade extends Service {
     public void setOutboundSettings(TunnelPoolSettings settings);
     public void setInboundSettings(Hash client, TunnelPoolSettings settings);
     public void setOutboundSettings(Hash client, TunnelPoolSettings settings);
+    /** for TunnelRenderer in router console */
+    public void listPools(List<TunnelPool> out);
+    /** for TunnelRenderer in router console */
+    public Map<Hash, TunnelPool> getInboundClientPools();
+    /** for TunnelRenderer in router console */
+    public Map<Hash, TunnelPool> getOutboundClientPools();
+    /** for TunnelRenderer in router console */
+    public TunnelPool getInboundExploratoryPool();
+    /** for TunnelRenderer in router console */
+    public TunnelPool getOutboundExploratoryPool();
 }
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
index 491b823a7ba3e5bd0493c0ec97bb813bdf74b439..be55c3da6dcd10ba2705460f39a4c6cc24076a62 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
@@ -848,7 +848,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         return searchJob;
     }
     
-    private Set getLeases() {
+    /** public for NetDbRenderer in routerconsole */
+    @Override
+    public Set<LeaseSet> getLeases() {
         if (!_initialized) return null;
         Set leases = new HashSet();
         Set keys = getDataStore().getKeys();
@@ -860,7 +862,9 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         }
         return leases;
     }
-    private Set<RouterInfo> getRouters() {
+    /** public for NetDbRenderer in routerconsole */
+    @Override
+    public Set<RouterInfo> getRouters() {
         if (!_initialized) return null;
         Set routers = new HashSet();
         Set keys = getDataStore().getKeys();
@@ -897,241 +901,4 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         }
         _context.jobQueue().addJob(new StoreJob(_context, this, key, ds, onSuccess, onFailure, sendTimeout, toIgnore));
     }
-    
-    class LeaseSetComparator implements Comparator {
-         public int compare(Object l, Object r) {
-             Destination dl = ((LeaseSet)l).getDestination();
-             Destination dr = ((LeaseSet)r).getDestination();
-             boolean locall = _context.clientManager().isLocal(dl);
-             boolean localr = _context.clientManager().isLocal(dr);
-             if (locall && !localr) return -1;
-             if (localr && !locall) return 1;
-             return dl.calculateHash().toBase64().compareTo(dr.calculateHash().toBase64());
-        }
-    }
-
-    class RouterInfoComparator implements Comparator {
-         public int compare(Object l, Object r) {
-             return ((RouterInfo)l).getIdentity().getHash().toBase64().compareTo(((RouterInfo)r).getIdentity().getHash().toBase64());
-        }
-    }
-
-    @Override
-    public void renderRouterInfoHTML(Writer out, String routerPrefix) throws IOException {
-        StringBuilder buf = new StringBuilder(4*1024);
-        buf.append("<h2>Network Database RouterInfo Lookup</h2>\n");
-        if (".".equals(routerPrefix)) {
-            renderRouterInfo(buf, _context.router().getRouterInfo(), true, true);
-        } else {
-            boolean notFound = true;
-            Set routers = getRouters();
-            for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
-                RouterInfo ri = (RouterInfo)iter.next();
-                Hash key = ri.getIdentity().getHash();
-                if (key.toBase64().startsWith(routerPrefix)) {
-                    renderRouterInfo(buf, ri, false, true);
-                    notFound = false;
-                }
-            }
-            if (notFound)
-                buf.append("Router ").append(routerPrefix).append(" not found in network database");
-        }
-        out.write(buf.toString());
-        out.flush();
-    }
-
-    public void renderStatusHTML(Writer out) throws IOException {
-        renderStatusHTML(out, true);
-    }
-
-    @Override
-    public void renderLeaseSetHTML(Writer out) throws IOException {
-        StringBuilder buf = new StringBuilder(4*1024);
-        buf.append("<h2>Network Database Contents</h2>\n");
-        buf.append("<a href=\"netdb.jsp\">View RouterInfo</a>");
-        buf.append("<h3>LeaseSets</h3>\n");
-        Set leases = new TreeSet(new LeaseSetComparator());
-        leases.addAll(getLeases());
-        long now = _context.clock().now();
-        for (Iterator iter = leases.iterator(); iter.hasNext(); ) {
-            LeaseSet ls = (LeaseSet)iter.next();
-            Destination dest = ls.getDestination();
-            Hash key = dest.calculateHash();
-            buf.append("<b>LeaseSet: ").append(key.toBase64());
-            if (_context.clientManager().isLocal(dest)) {
-                buf.append(" (<a href=\"tunnels.jsp#" + key.toBase64().substring(0,4) + "\">Local</a> ");
-                if (! _context.clientManager().shouldPublishLeaseSet(key))
-                    buf.append("Unpublished ");
-                buf.append("Destination ");
-                TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(key);
-                if (in != null && in.getDestinationNickname() != null)
-                    buf.append(in.getDestinationNickname());
-                else
-                    buf.append(dest.toBase64().substring(0, 6));
-            } else {
-                buf.append(" (Destination ");
-                String host = _context.namingService().reverseLookup(dest);
-                if (host != null)
-                    buf.append(host);
-                else
-                    buf.append(dest.toBase64().substring(0, 6));
-            }
-            buf.append(")</b><br>\n");
-            long exp = ls.getEarliestLeaseDate()-now;
-            if (exp > 0)
-                buf.append("Expires in ").append(DataHelper.formatDuration(exp)).append("<br>\n");
-            else
-                buf.append("Expired ").append(DataHelper.formatDuration(0-exp)).append(" ago<br>\n");
-            for (int i = 0; i < ls.getLeaseCount(); i++) {
-                buf.append("Lease ").append(i + 1).append(": Gateway ");
-                buf.append(_context.commSystem().renderPeerHTML(ls.getLease(i).getGateway()));
-                buf.append(" Tunnel ").append(ls.getLease(i).getTunnelId().getTunnelId()).append("<br>\n");
-            }
-            buf.append("<hr>\n");
-            out.write(buf.toString());
-            buf.setLength(0);
-        }
-        out.write(buf.toString());
-        out.flush();
-    }
-
-    @Override
-    public void renderStatusHTML(Writer out, boolean full) throws IOException {
-        int size = getKnownRouters() * 512;
-        if (full)
-            size *= 4;
-        StringBuilder buf = new StringBuilder(size);
-        out.write("<h2>Network Database Contents (<a href=\"netdb.jsp?l=1\">View LeaseSets</a>)</h2>\n");
-        if (!_initialized) {
-            buf.append("Not initialized\n");
-            out.write(buf.toString());
-            out.flush();
-            return;
-        }
-        
-        Hash us = _context.routerHash();
-        out.write("<a name=\"routers\" ></a><h3>Routers (<a href=\"netdb.jsp");
-        if (full)
-            out.write("#routers\" >view without");
-        else
-            out.write("?f=1#routers\" >view with");
-        out.write(" stats</a>)</h3>\n");
-        
-        RouterInfo ourInfo = _context.router().getRouterInfo();
-        renderRouterInfo(buf, ourInfo, true, true);
-        out.write(buf.toString());
-        buf.setLength(0);
-        
-        ObjectCounter<String> versions = new ObjectCounter();
-        ObjectCounter<String> countries = new ObjectCounter();
-        
-        Set routers = new TreeSet(new RouterInfoComparator());
-        routers.addAll(getRouters());
-        for (Iterator iter = routers.iterator(); iter.hasNext(); ) {
-            RouterInfo ri = (RouterInfo)iter.next();
-            Hash key = ri.getIdentity().getHash();
-            boolean isUs = key.equals(us);
-            if (!isUs) {
-                renderRouterInfo(buf, ri, false, full);
-                out.write(buf.toString());
-                buf.setLength(0);
-                String routerVersion = ri.getOption("router.version");
-                if (routerVersion != null)
-                    versions.increment(routerVersion);
-                String country = _context.commSystem().getCountry(key);
-                if(country != null)
-                    countries.increment(country);
-            }
-        }
-            
-        buf.append("<table border=\"0\" cellspacing=\"30\"><tr><td>");
-        List<String> versionList = new ArrayList(versions.objects());
-        if (versionList.size() > 0) {
-            Collections.sort(versionList, Collections.reverseOrder());
-            buf.append("<table>\n");
-            buf.append("<tr><th>Version</th><th>Count</th></tr>\n");
-            for (String routerVersion : versionList) {
-                int num = versions.count(routerVersion);
-                buf.append("<tr><td align=\"center\">").append(DataHelper.stripHTML(routerVersion));
-                buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
-            }
-            buf.append("</table>\n");
-        }
-        buf.append("</td><td>");
-        out.write(buf.toString());
-        buf.setLength(0);
-            
-        List<String> countryList = new ArrayList(countries.objects());
-        if (countryList.size() > 0) {
-            Collections.sort(countryList);
-            buf.append("<table>\n");
-            buf.append("<tr><th align=\"left\">Country</th><th>Count</th></tr>\n");
-            for (String country : countryList) {
-                int num = countries.count(country);
-                buf.append("<tr><td><img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
-                buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
-                buf.append(_context.commSystem().getCountryName(country));
-                buf.append("</td><td align=\"center\">").append(num).append("</td></tr>\n");
-            }
-            buf.append("</table>\n");
-        }
-        buf.append("</td></tr></table>");
-        out.write(buf.toString());
-        out.flush();
-    }
-    
-    /**
-     *  Be careful to use stripHTML for any displayed routerInfo data
-     *  to prevent vulnerabilities
-     */
-    private void renderRouterInfo(StringBuilder buf, RouterInfo info, boolean isUs, boolean full) {
-        String hash = info.getIdentity().getHash().toBase64();
-        buf.append("<table><tr><th><a name=\"").append(hash.substring(0, 6)).append("\" ></a>");
-        if (isUs) {
-            buf.append("<a name=\"our-info\" ></a><b>Our info: ").append(hash).append("</b></th></tr><tr><td>\n");
-        } else {
-            buf.append("<b>Peer info for:</b> ").append(hash).append("\n");
-            if (full) {
-                buf.append("[<a href=\"netdb.jsp\" >Back</a>]</th></tr><td>\n");
-            } else {
-                buf.append("[<a href=\"netdb.jsp?r=").append(hash.substring(0, 6)).append("\" >Full entry</a>]</th></tr><td>\n");
-            }
-        }
-        
-        long age = _context.clock().now() - info.getPublished();
-        if (isUs && _context.router().isHidden())
-            buf.append("<b>Hidden, Updated:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
-        else if (age > 0)
-            buf.append("<b>Published:</b> ").append(DataHelper.formatDuration(age)).append(" ago<br>\n");
-        else
-            buf.append("<b>Published:</b> in ").append(DataHelper.formatDuration(0-age)).append("???<br>\n");
-        buf.append("<b>Address(es):</b> ");
-        String country = _context.commSystem().getCountry(info.getIdentity().getHash());
-        if(country != null) {
-            buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase()).append("\"");
-            buf.append(" src=\"/flags.jsp?c=").append(country).append("\"> ");
-        }
-        for (Iterator iter = info.getAddresses().iterator(); iter.hasNext(); ) {
-            RouterAddress addr = (RouterAddress)iter.next();
-            buf.append(DataHelper.stripHTML(addr.getTransportStyle())).append(": ");
-            for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
-                String name = (String)optIter.next();
-                String val = addr.getOptions().getProperty(name);
-                buf.append('[').append(DataHelper.stripHTML(name)).append('=').append(DataHelper.stripHTML(val)).append("] ");
-            }
-        }
-        buf.append("</td></tr>\n");
-        if (full) {
-            buf.append("<tr><td>Stats: <br><code>\n");
-            for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) {
-                String key = (String)iter.next();
-                String val = info.getOption(key);
-                buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n");
-            }
-            buf.append("</code></td></tr>\n");
-        } else {
-        }
-        buf.append("</td></tr>\n");
-    }
-
 }
diff --git a/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java b/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java
index 14ee5317e2a8b56b8dcffd953246197c6a8c23b8..69d5f11ce583081740ca8d15239b128cf2fb36cc 100644
--- a/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java
+++ b/router/java/src/net/i2p/router/peermanager/PeerManagerFacadeImpl.java
@@ -78,7 +78,7 @@ public class PeerManagerFacadeImpl implements PeerManagerFacade {
         return _manager.getPeersByCapability(capability); 
     }
 
-    /** @deprecated, moved to routerconsole */
+    /** @deprecated moved to routerconsole */
     public void renderStatusHTML(Writer out) throws IOException { 
     }
     
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
index 147dd2f7643c46e35f0f8ae5974c8bcc84e1a886..3cb4fc9ab2f40217f2cf2db1641df65e14c2edb2 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
@@ -374,6 +374,9 @@ public class TunnelDispatcher implements Service {
         _context.statManager().addRateData("tunnel.dispatchDataTime", dispatchTime, dispatchTime);
     }
 
+    /** High for now, just to prevent long-lived-message attacks */
+    private static final long MAX_FUTURE_EXPIRATION = 3*60*1000 + Router.CLOCK_FUDGE_FACTOR;
+
     /**
      * We are the inbound tunnel gateway, so encrypt it as necessary and forward
      * it on.
@@ -385,7 +388,10 @@ public class TunnelDispatcher implements Service {
         if (gw != null) {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("dispatch where we are the inbound gateway: " + gw + ": " + msg);
-            if ( (msg.getMessageExpiration() < before - Router.CLOCK_FUDGE_FACTOR) || (msg.getMessage().getMessageExpiration() < before - Router.CLOCK_FUDGE_FACTOR) ) {
+            long minTime = before - Router.CLOCK_FUDGE_FACTOR;
+            long maxTime = before + MAX_FUTURE_EXPIRATION;
+            if ( (msg.getMessageExpiration() < minTime) || (msg.getMessage().getMessageExpiration() < minTime) ||
+                 (msg.getMessageExpiration() > maxTime) || (msg.getMessage().getMessageExpiration() > maxTime) ) {
                 if (_log.shouldLog(Log.ERROR))
                     _log.error("Not dispatching a gateway message for tunnel " + msg.getTunnelId().getTunnelId()
                                + " as the wrapper's expiration is in " + DataHelper.formatDuration(msg.getMessageExpiration()-before)
@@ -463,6 +469,12 @@ public class TunnelDispatcher implements Service {
                     _log.warn("why are you sending a tunnel message that expired " 
                                + (before-msg.getMessageExpiration()) + "ms ago? " 
                                + msg, new Exception("cause"));
+            } else if (msg.getMessageExpiration() > before + MAX_FUTURE_EXPIRATION) {
+                if (_log.shouldLog(Log.ERROR))
+                    _log.error("why are you sending a tunnel message that expires " 
+                               + (msg.getMessageExpiration() - before) + "ms from now? "
+                               + msg, new Exception("cause"));
+                return;
             }
             long tid1 = outboundTunnel.getTunnelId();
             long tid2 = (targetTunnel != null ? targetTunnel.getTunnelId() : -1);
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
index f7c752c8c981ed1d61f2271888d4d8a2120cc81c..d879750359ba08992cb4effd9c856040c1dea5f4 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
@@ -391,7 +391,7 @@ public class TunnelPoolManager implements TunnelManagerFacade {
     }
     
     /** list of TunnelPool instances currently in play */
-    void listPools(List<TunnelPool> out) {
+    public void listPools(List<TunnelPool> out) {
         synchronized (_clientInboundPools) {
             out.addAll(_clientInboundPools.values());
         }
@@ -409,227 +409,8 @@ public class TunnelPoolManager implements TunnelManagerFacade {
 
     public int getInboundBuildQueueSize() { return _executor.getInboundBuildQueueSize(); }
     
-    
+    /** @deprecated moved to routerconsole */
     public void renderStatusHTML(Writer out) throws IOException {
-        out.write("<div class=\"wideload\"><h2><a name=\"exploratory\" ></a>Exploratory tunnels (<a href=\"/configtunnels.jsp#exploratory\">config</a>):</h2>\n");
-        renderPool(out, _inboundExploratory, _outboundExploratory);
-        
-        List<Hash> destinations = null;
-        synchronized (_clientInboundPools) {
-            destinations = new ArrayList(_clientInboundPools.keySet());
-        }
-        for (int i = 0; i < destinations.size(); i++) {
-            Hash client = destinations.get(i);
-            TunnelPool in = null;
-            TunnelPool outPool = null;
-            synchronized (_clientInboundPools) {
-                in = _clientInboundPools.get(client);
-            }
-            synchronized (_clientOutboundPools) {
-                outPool = _clientOutboundPools.get(client);
-            }
-            String name = (in != null ? in.getSettings().getDestinationNickname() : null);
-            if ( (name == null) && (outPool != null) )
-                name = outPool.getSettings().getDestinationNickname();
-            if (name == null)
-                name = client.toBase64().substring(0,4);
-            out.write("<h2><a name=\"" + client.toBase64().substring(0,4)
-                      + "\" ></a>Client tunnels for " + name);
-            if (_context.clientManager().isLocal(client))
-                out.write(" (<a href=\"/configtunnels.jsp#" + client.toBase64().substring(0,4) +"\">config</a>):</h2>\n");
-            else
-                out.write(" (dead):</h2>\n");
-            renderPool(out, in, outPool);
-        }
-        
-        List participating = _context.tunnelDispatcher().listParticipatingTunnels();
-        Collections.sort(participating, new TunnelComparator());
-        out.write("<h2><a name=\"participating\"></a>Participating tunnels:</h2><table>\n");
-        out.write("<tr><th>Receive on</th><th>From</th><th>"
-                  + "Send on</th><th>To</th><th>Expiration</th>"
-                  + "<th>Usage</th><th>Rate</th><th>Role</th></tr>\n");
-        long processed = 0;
-        RateStat rs = _context.statManager().getRate("tunnel.participatingMessageCount");
-        if (rs != null)
-            processed = (long)rs.getRate(10*60*1000).getLifetimeTotalValue();
-        int inactive = 0;
-        for (int i = 0; i < participating.size(); i++) {
-            HopConfig cfg = (HopConfig)participating.get(i);
-            if (cfg.getProcessedMessagesCount() <= 0) {
-                inactive++;
-                continue;
-            }
-            out.write("<tr>");
-            if (cfg.getReceiveTunnel() != null)
-                out.write(" <td class=\"cells\" align=\"center\">" + cfg.getReceiveTunnel().getTunnelId() +"</td>");
-            else
-                out.write(" <td class=\"cells\" align=\"center\">n/a</td>");
-            if (cfg.getReceiveFrom() != null)
-                out.write(" <td class=\"cells\" align=\"right\">" + netDbLink(cfg.getReceiveFrom()) +"</td>");
-            else
-                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
-            if (cfg.getSendTunnel() != null)
-                out.write(" <td class=\"cells\" align=\"center\">" + cfg.getSendTunnel().getTunnelId() +"</td>");
-            else
-                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
-            if (cfg.getSendTo() != null)
-                out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(cfg.getSendTo()) +"</td>");
-            else
-//                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
-                out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
-            long timeLeft = cfg.getExpiration()-_context.clock().now();
-            if (timeLeft > 0)
-                out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>");
-            else
-                out.write(" <td class=\"cells\" align=\"center\">(grace period)</td>");
-            out.write(" <td class=\"cells\" align=\"center\">" + cfg.getProcessedMessagesCount() + "KB</td>");
-            int lifetime = (int) ((_context.clock().now() - cfg.getCreation()) / 1000);
-            if (lifetime <= 0)
-                lifetime = 1;
-            if (lifetime > 10*60)
-                lifetime = 10*60;
-            int bps = 1024 * (int) cfg.getProcessedMessagesCount() / lifetime;
-            out.write(" <td class=\"cells\" align=\"center\">" + bps + "Bps</td>");
-            if (cfg.getSendTo() == null)
-                out.write(" <td class=\"cells\" align=\"center\">Outbound Endpoint</td>");
-            else if (cfg.getReceiveFrom() == null)
-                out.write(" <td class=\"cells\" align=\"center\">Inbound Gateway</td>");
-            else
-                out.write(" <td class=\"cells\" align=\"center\">Participant</td>");
-            out.write("</tr>\n");
-            processed += cfg.getProcessedMessagesCount();
-        }
-        out.write("</table>\n");
-        out.write("<div class=\"statusnotes\"><b>Inactive participating tunnels: " + inactive + "</b></div>\n");
-        out.write("<div class=\"statusnotes\"><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processed*1024) + "B</b></div>\n");
-        renderPeers(out);
-    }
-    
-    class TunnelComparator implements Comparator {
-         public int compare(Object l, Object r) {
-             return (int) (((HopConfig)r).getProcessedMessagesCount() - ((HopConfig)l).getProcessedMessagesCount());
-        }
-    }
-
-    private void renderPool(Writer out, TunnelPool in, TunnelPool outPool) throws IOException {
-        List<TunnelInfo> tunnels = null;
-        if (in == null)
-            tunnels = new ArrayList();
-        else
-            tunnels = in.listTunnels();
-        if (outPool != null)
-            tunnels.addAll(outPool.listTunnels());
-        
-        long processedIn = (in != null ? in.getLifetimeProcessed() : 0);
-        long processedOut = (outPool != null ? outPool.getLifetimeProcessed() : 0);
-        
-        int live = 0;
-        int maxLength = 1;
-        for (int i = 0; i < tunnels.size(); i++) {
-            TunnelInfo info = tunnels.get(i);
-            if (info.getLength() > maxLength)
-                maxLength = info.getLength();
-        }
-        out.write("<table><tr><th>In/Out</th><th>Expiry</th><th>Usage</th><th>Gateway</th>");
-        if (maxLength > 3) {
-            out.write("<th align=\"center\" colspan=\"" + (maxLength - 2));
-            out.write("\">Participants</th>");
-        }
-        else if (maxLength == 3) {
-            out.write("<th>Participant</th>");
-        }
-        if (maxLength > 1) {
-            out.write("<th>Endpoint</th>");
-        }
-        out.write("</tr>\n");
-        for (int i = 0; i < tunnels.size(); i++) {
-            TunnelInfo info = tunnels.get(i);
-            long timeLeft = info.getExpiration()-_context.clock().now();
-            if (timeLeft <= 0)
-                continue; // don't display tunnels in their grace period
-            live++;
-            if (info.isInbound())
-                out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/inbound.png\" alt=\"Inbound\" title=\"Inbound\"></td>");
-            else
-                out.write("<tr> <td class=\"cells\" align=\"center\"><img src=\"/themes/console/images/outbound.png\" alt=\"Outbound\" title=\"Outbound\"></td>");
-            out.write(" <td class=\"cells\" align=\"center\">" + DataHelper.formatDuration(timeLeft) + "</td>\n");
-            out.write(" <td class=\"cells\" align=\"center\">" + info.getProcessedMessagesCount() + "KB</td>\n");
-            for (int j = 0; j < info.getLength(); j++) {
-                Hash peer = info.getPeer(j);
-                TunnelId id = (info.isInbound() ? info.getReceiveTunnelId(j) : info.getSendTunnelId(j));
-                if (_context.routerHash().equals(peer)) {
-                    out.write(" <td class=\"cells\" align=\"center\">" + (id == null ? "" : "" + id) + "</td>");
-                } else {
-                    String cap = getCapacity(peer);
-                    out.write(" <td class=\"cells\" align=\"center\">" + netDbLink(peer) + (id == null ? "" : " " + id) + cap + "</td>");                
-                }
-                if (info.getLength() < maxLength && (info.getLength() == 1 || j == info.getLength() - 2)) {
-                    for (int k = info.getLength(); k < maxLength; k++)
-                        out.write(" <td class=\"cells\" align=\"center\">&nbsp;</td>");
-                }
-            }
-            out.write("</tr>\n");
-            
-            if (info.isInbound()) 
-                processedIn += info.getProcessedMessagesCount();
-            else
-                processedOut += info.getProcessedMessagesCount();
-        }
-        out.write("</table>\n");
-        if (in != null) {
-            List pending = in.listPending();
-            if (pending.size() > 0)
-                out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " inbound</b></center></div>\n");
-            live += pending.size();
-        }
-        if (outPool != null) {
-            List pending = outPool.listPending();
-            if (pending.size() > 0)
-                out.write("<div class=\"statusnotes\"><center><b>Build in progress: " + pending.size() + " outbound</b></center></div>\n");
-            live += pending.size();
-        }
-        if (live <= 0)
-            out.write("<div class=\"statusnotes\"><center><b>No tunnels; waiting for the grace period to end.</center></b></div>\n");
-        out.write("<div class=\"statusnotes\"><center><b>Lifetime bandwidth usage: " + DataHelper.formatSize(processedIn*1024) + "B in, " +
-                  DataHelper.formatSize(processedOut*1024) + "B out</b></center></div>");
-    }
-    
-    private void renderPeers(Writer out) throws IOException {
-        // count up the peers in the local pools
-        ObjectCounter<Hash> lc = new ObjectCounter();
-        int tunnelCount = countTunnelsPerPeer(lc);
-
-        // count up the peers in the participating tunnels
-        ObjectCounter<Hash> pc = new ObjectCounter();
-        int partCount = countParticipatingPerPeer(pc);
-
-        Set<Hash> peers = new HashSet(lc.objects());
-        peers.addAll(pc.objects());
-        List<Hash> peerList = new ArrayList(peers);
-        Collections.sort(peerList, new HashComparator());
-
-        out.write("<h2><a name=\"peers\"></a>Tunnel Counts By Peer:</h2>\n");
-        out.write("<table><tr><th>Peer</th><th>Expl. + Client</th><th>% of total</th><th>Part. from + to</th><th>% of total</th></tr>\n");
-        for (Hash h : peerList) {
-             out.write("<tr> <td class=\"cells\" align=\"center\">");
-             out.write(netDbLink(h));
-             out.write(" <td class=\"cells\" align=\"center\">" + lc.count(h));
-             out.write(" <td class=\"cells\" align=\"center\">");
-             if (tunnelCount > 0)
-                 out.write("" + (lc.count(h) * 100 / tunnelCount));
-             else
-                 out.write('0');
-             out.write(" <td class=\"cells\" align=\"center\">" + pc.count(h));
-             out.write(" <td class=\"cells\" align=\"center\">");
-             if (partCount > 0)
-                 out.write("" + (pc.count(h) * 100 / partCount));
-             else
-                 out.write('0');
-             out.write('\n');
-        }
-        out.write("<tr class=\"tablefooter\"> <td align=\"center\"><b>Tunnels</b> <td align=\"center\"><b>" + tunnelCount);
-        out.write("</b> <td>&nbsp;</td> <td align=\"center\"><b>" + partCount);
-        out.write("</b> <td>&nbsp;</td></tr></table></div>\n");
     }
 
     /** @return total number of non-fallback expl. + client tunnels */
@@ -682,39 +463,27 @@ public class TunnelPoolManager implements TunnelManagerFacade {
         return rv;
     }
 
-    /** @return total number of part. tunnels */
-    private int countParticipatingPerPeer(ObjectCounter<Hash> pc) {
-        List<HopConfig> participating = _context.tunnelDispatcher().listParticipatingTunnels();
-        for (HopConfig cfg : participating) {
-            Hash from = cfg.getReceiveFrom();
-            if (from != null)
-                pc.increment(from);
-            Hash to = cfg.getSendTo();
-            if (to != null)
-                pc.increment(to);
+    /** for TunnelRenderer in router console */
+    public Map<Hash, TunnelPool> getInboundClientPools() {
+        synchronized (_clientInboundPools) {
+            return new HashMap(_clientInboundPools);
         }
-        return participating.size();
     }
 
-    class HashComparator implements Comparator {
-         public int compare(Object l, Object r) {
-             return ((Hash)l).toBase64().compareTo(((Hash)r).toBase64());
+    /** for TunnelRenderer in router console */
+    public Map<Hash, TunnelPool> getOutboundClientPools() {
+        synchronized (_clientOutboundPools) {
+            return new HashMap(_clientOutboundPools);
         }
     }
 
-    private String getCapacity(Hash peer) {
-        RouterInfo info = _context.netDb().lookupRouterInfoLocally(peer);
-        if (info != null) {
-            String caps = info.getCapabilities();
-            for (char c = Router.CAPABILITY_BW12; c <= Router.CAPABILITY_BW256; c++) {
-                if (caps.indexOf(c) >= 0)
-                    return " " + c;
-            }
-        }
-        return "";
+    /** for TunnelRenderer in router console */
+    public TunnelPool getInboundExploratoryPool() {
+        return _inboundExploratory;
     }
 
-    private String netDbLink(Hash peer) {
-        return _context.commSystem().renderPeerHTML(peer);
+    /** for TunnelRenderer in router console */
+    public TunnelPool getOutboundExploratoryPool() {
+        return _outboundExploratory;
     }
 }
diff --git a/router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java b/router/java/test/net/i2p/data/i2np/TunnelCreateMessage.java
similarity index 100%
rename from router/java/src/net/i2p/data/i2np/TunnelCreateMessage.java
rename to router/java/test/net/i2p/data/i2np/TunnelCreateMessage.java
diff --git a/router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java b/router/java/test/net/i2p/data/i2np/TunnelCreateStatusMessage.java
similarity index 100%
rename from router/java/src/net/i2p/data/i2np/TunnelCreateStatusMessage.java
rename to router/java/test/net/i2p/data/i2np/TunnelCreateStatusMessage.java