diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/package.html b/apps/ministreaming/java/src/net/i2p/client/streaming/package.html
index 735135074871957df6d46764e63f54d99537e4d1..8418604532afb4a7ef62c9d7d6e0d9621895179a 100644
--- a/apps/ministreaming/java/src/net/i2p/client/streaming/package.html
+++ b/apps/ministreaming/java/src/net/i2p/client/streaming/package.html
@@ -16,9 +16,9 @@ net.i2p.client.streaming.I2PServerSocket#accept} method, which will provide an
 application wants to create a new stream to a peer, it should do so with the
 appropriate {@link net.i2p.client.streaming.I2PSocketManager#connect} call.</p>
 
-<p>There is a simple pair of demo applications available as well - {@link
-net.i2p.client.streaming.StreamSinkServer} listens to a destination and dumps 
-the data from all sockets it accepts to individual files, while {@link
-net.i2p.client.streaming.StreamSinkClient} connects to a particular destination
+<p>There is a simple pair of demo applications available as well -
+net.i2p.client.streaming.StreamSinkServer listens to a destination and dumps 
+the data from all sockets it accepts to individual files, while
+net.i2p.client.streaming.StreamSinkClient connects to a particular destination
 and sends a specific amount of random data then disconnects.</p>
 </body></html>
diff --git a/apps/routerconsole/jsp/debug.jsp b/apps/routerconsole/jsp/debug.jsp
new file mode 100644
index 0000000000000000000000000000000000000000..694f9c1df2452d71d0db24845313ad65703ecd28
--- /dev/null
+++ b/apps/routerconsole/jsp/debug.jsp
@@ -0,0 +1,30 @@
+<%@page contentType="text/html"%>
+<%@page pageEncoding="UTF-8"%>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head><title>DEBUG</title>
+<%@include file="css.jsp" %>
+</head><body>
+<%@include file="summary.jsp" %>
+<div class="main" id="main">
+<%
+    /*
+     *  Quick and easy place to put debugging stuff
+     */
+    net.i2p.router.RouterContext ctx = (net.i2p.router.RouterContext) net.i2p.I2PAppContext.getGlobalContext();
+
+    /*
+     *  Print out the status for all the SessionKeyManagers
+     */
+    out.print("<h1>Router SKM</h1>");
+    ctx.sessionKeyManager().renderStatusHTML(out);
+    java.util.Set<net.i2p.data.Destination> clients = ctx.clientManager().listClients();
+    for (net.i2p.data.Destination dest : clients) {
+        net.i2p.data.Hash h = dest.calculateHash();
+        net.i2p.crypto.SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(h);
+        if (skm != null) {
+            out.print("<h1>" + h.toBase64().substring(0,6) + " SKM</h1>");
+            skm.renderStatusHTML(out);
+        }
+    }
+%>
+</div></body></html>
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index c7495131f3d1609cc31840fb67e892298245801f..a47d361a668e0e0495eaaebfec4f1578614d8606 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -354,6 +354,7 @@ public class Connection {
          */
     }
     
+/*********
     private class PingNotifier implements ConnectionManager.PingNotifier {
         private long _startedPingOn;
         public PingNotifier() {
@@ -367,6 +368,7 @@ public class Connection {
                 _options.updateRTT((int)time*2);
         }
     }
+*********/
     
     List ackPackets(long ackThrough, long nacks[]) {
         if (ackThrough < _highestAckedThrough) {
@@ -548,20 +550,21 @@ public class Connection {
         killOutstandingPackets();
     }
     
+    /** ignore tag issues */
     private void killOutstandingPackets() {
-        boolean tagsCancelled = false;
+        //boolean tagsCancelled = false;
         synchronized (_outboundPackets) {
             for (Iterator iter = _outboundPackets.values().iterator(); iter.hasNext(); ) {
                 PacketLocal pl = (PacketLocal)iter.next();
-                if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) )
-                    tagsCancelled = true;
+                //if ( (pl.getTagsSent() != null) && (pl.getTagsSent().size() > 0) )
+                //    tagsCancelled = true;
                 pl.cancelled();
             }
             _outboundPackets.clear();
             _outboundPackets.notifyAll();
         }            
-        if (tagsCancelled)
-            _context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
+        //if (tagsCancelled)
+        //    _context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
     }
     
     private class DisconnectEvent implements SimpleTimer.TimedEvent {
@@ -1140,12 +1143,12 @@ public class Connection {
                 
                 // in case things really suck, the other side may have lost thier
                 // session tags (e.g. they restarted), so jump back to ElGamal.
-                int failTagsAt = _options.getMaxResends() - 2;
-                if ( (newWindowSize == 1) && (numSends == failTagsAt) ) {
-                    if (_log.shouldLog(Log.WARN))
-                        _log.warn("Optimistically failing tags at resend " + numSends);
-                    _context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
-                }
+                //int failTagsAt = _options.getMaxResends() - 2;
+                //if ( (newWindowSize == 1) && (numSends == failTagsAt) ) {
+                //    if (_log.shouldLog(Log.WARN))
+                //        _log.warn("Optimistically failing tags at resend " + numSends);
+                //    _context.sessionKeyManager().failTags(_remotePeer.getPublicKey());
+                //}
                 
                 if (numSends - 1 > _options.getMaxResends()) {
                     if (_log.shouldLog(Log.DEBUG))
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
index 7efc6cc401e99f56e9fb6fb1ba4b88c814238224..af44c41f24b1f0d02552f9241b5700732bb3a4f4 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionManager.java
@@ -349,24 +349,35 @@ public class ConnectionManager {
             return new HashSet(_connectionByInboundId.values());
         }
     }
+
+    /** blocking */
     public boolean ping(Destination peer, long timeoutMs) {
-        return ping(peer, timeoutMs, true);
+        return ping(peer, timeoutMs, true, null);
     }
     public boolean ping(Destination peer, long timeoutMs, boolean blocking) {
-        return ping(peer, timeoutMs, blocking, null, null, null);
+        return ping(peer, timeoutMs, blocking, null);
     }
 
+    /**
+     * @deprecated I2PSession ignores tags, use non-tag variant
+     * @param keyToUse ignored
+     * @param tagsToSend ignored
+     */
     public boolean ping(Destination peer, long timeoutMs, boolean blocking, SessionKey keyToUse, Set tagsToSend, PingNotifier notifier) {
+        return ping(peer, timeoutMs, blocking, notifier);
+    }
+
+    public boolean ping(Destination peer, long timeoutMs, boolean blocking, PingNotifier notifier) {
         Long id = new Long(_context.random().nextLong(Packet.MAX_STREAM_ID-1)+1);
         PacketLocal packet = new PacketLocal(_context, peer);
         packet.setSendStreamId(id.longValue());
         packet.setFlag(Packet.FLAG_ECHO);
         packet.setFlag(Packet.FLAG_SIGNATURE_INCLUDED);
         packet.setOptionalFrom(_session.getMyDestination());
-        if ( (keyToUse != null) && (tagsToSend != null) ) {
-            packet.setKeyUsed(keyToUse);
-            packet.setTagsSent(tagsToSend);
-        }
+        //if ( (keyToUse != null) && (tagsToSend != null) ) {
+        //    packet.setKeyUsed(keyToUse);
+        //    packet.setTagsSent(tagsToSend);
+        //}
         
         PingRequest req = new PingRequest(peer, packet, notifier);
         
@@ -435,7 +446,7 @@ public class ConnectionManager {
         }
         public void pong() { 
             _log.debug("Ping successful");
-            _context.sessionKeyManager().tagsDelivered(_peer.getPublicKey(), _packet.getKeyUsed(), _packet.getTagsSent());
+            //_context.sessionKeyManager().tagsDelivered(_peer.getPublicKey(), _packet.getKeyUsed(), _packet.getTagsSent());
             synchronized (ConnectionManager.PingRequest.this) {
                 _ponged = true; 
                 ConnectionManager.PingRequest.this.notifyAll();
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
index f7b245cb83fb0748f3d37e805ed1c2bd345b70b9..91a06e088b983b041a09d068bd5c46c8cffa22a0 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionPacketHandler.java
@@ -263,12 +263,12 @@ public class ConnectionPacketHandler {
                     numResends++;
                 
                 // ACK the tags we delivered so we can use them
-                if ( (p.getKeyUsed() != null) && (p.getTagsSent() != null) 
-                      && (p.getTagsSent().size() > 0) ) {
-                    _context.sessionKeyManager().tagsDelivered(p.getTo().getPublicKey(), 
-                                                               p.getKeyUsed(), 
-                                                               p.getTagsSent());
-                }
+                //if ( (p.getKeyUsed() != null) && (p.getTagsSent() != null) 
+                //      && (p.getTagsSent().size() > 0) ) {
+                //    _context.sessionKeyManager().tagsDelivered(p.getTo().getPublicKey(), 
+                //                                               p.getKeyUsed(), 
+                //                                               p.getTagsSent());
+                //}
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("Packet acked after " + p.getAckTime() + "ms: " + p);
             }
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
index 9ed29b50fb21aca3d865fd281838246ef0926364..b1438a033ad63ec6c95e5a8483ce3014ddf92dbf 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java
@@ -47,11 +47,31 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
     public Destination getTo() { return _to; }
     public void setTo(Destination to) { _to = to; }
     
+    /**
+     * @deprecated should always return null
+     */
     public SessionKey getKeyUsed() { return _keyUsed; }
-    public void setKeyUsed(SessionKey key) { _keyUsed = key; }
+
+    /**
+     * @deprecated I2PSession throws out the tags
+     */
+    public void setKeyUsed(SessionKey key) {
+        if (key != null)
+            _log.error("Who is sending tags thru the streaming lib?");
+        _keyUsed = key;
+    }
     
+    /**
+     * @deprecated should always return null or an empty set
+     */
     public Set getTagsSent() { return _tagsSent; }
+
+    /**
+     * @deprecated I2PSession throws out the tags
+     */
     public void setTagsSent(Set tags) { 
+        if (tags != null && tags.size() > 0)
+            _log.error("Who is sending tags thru the streaming lib? " + tags.size());
         if ( (_tagsSent != null) && (_tagsSent.size() > 0) && (tags.size() > 0) ) {
             //int old = _tagsSent.size();
             //_tagsSent.addAll(tags);
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
index db4adb27cd00f643199dc6ecb0e56273ad50cc43..8a4692ada76444ae972c52861b776d2453f6b4f5 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketQueue.java
@@ -36,16 +36,18 @@ public class PacketQueue {
     
     /**
      * Add a new packet to be sent out ASAP
+     *
+     * keys and tags disabled since dropped in I2PSession
      */
     public void enqueue(PacketLocal packet) {
         packet.prepare();
         
-        SessionKey keyUsed = packet.getKeyUsed();
-        if (keyUsed == null)
-            keyUsed = new SessionKey();
-        Set tagsSent = packet.getTagsSent();
-        if (tagsSent == null)
-            tagsSent = new HashSet(0);
+        //SessionKey keyUsed = packet.getKeyUsed();
+        //if (keyUsed == null)
+        //    keyUsed = new SessionKey();
+        //Set tagsSent = packet.getTagsSent();
+        //if (tagsSent == null)
+        //    tagsSent = new HashSet(0);
 
         // cache this from before sendMessage
         String conStr = null;
@@ -92,13 +94,19 @@ public class PacketQueue {
                 // I2PSessionImpl2
                 //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires);
                 // I2PSessionMuxedImpl
-                sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires,
+                //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, expires,
+                //                 I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
+                // I2PSessionMuxedImpl no tags
+                sent = _session.sendMessage(packet.getTo(), buf, 0, size, null, null, expires,
                                  I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
             else
                 // I2PSessionImpl2
                 //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent, 0);
                 // I2PSessionMuxedImpl
-                sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent,
+                //sent = _session.sendMessage(packet.getTo(), buf, 0, size, keyUsed, tagsSent,
+                //                 I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
+                // I2PSessionMuxedImpl no tags
+                sent = _session.sendMessage(packet.getTo(), buf, 0, size, null, null,
                                  I2PSession.PROTO_STREAMING, I2PSession.PORT_UNSPECIFIED, I2PSession.PORT_UNSPECIFIED);
             end = _context.clock().now();
             
@@ -129,13 +137,11 @@ public class PacketQueue {
             if (c != null) // handle race on b0rk
                 c.disconnect(false);
         } else {
-            packet.setKeyUsed(keyUsed);
-            packet.setTagsSent(tagsSent);
+            //packet.setKeyUsed(keyUsed);
+            //packet.setTagsSent(tagsSent);
             packet.incrementSends();
             if (_log.shouldLog(Log.DEBUG)) {
-                String msg = "SEND " + packet + (tagsSent.size() > 0 
-                             ? " with " + tagsSent.size() + " tags"
-                             : "")
+                String msg = "SEND " + packet
                              + " send # " + packet.getNumSends()
                              + " sendTime: " + (end-begin)
                              + " con: " + conStr;
diff --git a/core/java/src/net/i2p/client/I2CPMessageProducer.java b/core/java/src/net/i2p/client/I2CPMessageProducer.java
index b897d22d0ee2230577e80c6972f52ed77e97efba..220c0a851e0a59c4cd4887dbb8f9b1782962937e 100644
--- a/core/java/src/net/i2p/client/I2CPMessageProducer.java
+++ b/core/java/src/net/i2p/client/I2CPMessageProducer.java
@@ -93,6 +93,10 @@ class I2CPMessageProducer {
     /**
      * Package up and send the payload to the router for delivery
      *
+     * @param tag unused - no end-to-end crypto
+     * @param tags unused - no end-to-end crypto
+     * @param key unused - no end-to-end crypto
+     * @param newKey unused - no end-to-end crypto
      */
     public void sendMessage(I2PSessionImpl session, Destination dest, long nonce, byte[] payload, SessionTag tag,
                             SessionKey key, Set tags, SessionKey newKey, long expires) throws I2PSessionException {
@@ -135,6 +139,10 @@ class I2CPMessageProducer {
     /**
      * Create a new signed payload and send it off to the destination
      *
+     * @param tag unused - no end-to-end crypto
+     * @param tags unused - no end-to-end crypto
+     * @param key unused - no end-to-end crypto
+     * @param newKey unused - no end-to-end crypto
      */
     private Payload createPayload(Destination dest, byte[] payload, SessionTag tag, SessionKey key, Set tags,
                                   SessionKey newKey) throws I2PSessionException {
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 8fc3087158020cb5112565f7d18ba46a30b1062c..7bee11a2c9bfa152a589b20b5f33553546a417b9 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -355,17 +355,23 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
      */
     public abstract boolean sendMessage(Destination dest, byte[] payload) throws I2PSessionException;
     
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     public abstract boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, 
                                         Set tagsSent) throws I2PSessionException;
 
     public abstract void receiveStatus(int msgId, long nonce, int status);
 
+/****** no end-to-end crypto
     protected static final Set createNewTags(int num) {
         Set tags = new HashSet();
         for (int i = 0; i < num; i++)
             tags.add(new SessionTag(true));
         return tags;
     }
+*******/
 
     /**
      * Recieve a payload message and let the app know its available
diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java
index 508057c2c55e6e2796280fb94d2bbe91049bc176..5b57b3137b47493d65551df4602012e51200f67f 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl2.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java
@@ -133,14 +133,28 @@ class I2PSessionImpl2 extends I2PSessionImpl {
         return sendMessage(dest, payload, offset, size, null, null, 0);
     }
     
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     @Override
     public boolean sendMessage(Destination dest, byte[] payload, SessionKey keyUsed, Set tagsSent) throws I2PSessionException {
         return sendMessage(dest, payload, 0, payload.length, keyUsed, tagsSent, 0);
     }
+
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent)
                    throws I2PSessionException {
         return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0);
     }
+
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent, long expires)
                    throws I2PSessionException {
         if (_log.shouldLog(Log.DEBUG)) _log.debug("sending message");
@@ -196,13 +210,17 @@ class I2PSessionImpl2 extends I2PSessionImpl {
     
     private static final int NUM_TAGS = 50;
 
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     protected boolean sendBestEffort(Destination dest, byte payload[], SessionKey keyUsed, Set tagsSent, long expires)
                     throws I2PSessionException {
-        SessionKey key = null;
-        SessionKey newKey = null;
-        SessionTag tag = null;
-        Set sentTags = null;
-        int oldTags = 0;
+        //SessionKey key = null;
+        //SessionKey newKey = null;
+        //SessionTag tag = null;
+        //Set sentTags = null;
+        //int oldTags = 0;
         long begin = _context.clock().now();
         /***********
         if (I2CPMessageProducer.END_TO_END_CRYPTO) {
@@ -256,27 +274,27 @@ class I2PSessionImpl2 extends I2PSessionImpl {
         long nonce = _context.random().nextInt(Integer.MAX_VALUE);
         if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state");
         MessageState state = new MessageState(_context, nonce, getPrefix());
-        state.setKey(key);
-        state.setTags(sentTags);
-        state.setNewKey(newKey);
+        //state.setKey(key);
+        //state.setTags(sentTags);
+        //state.setNewKey(newKey);
         state.setTo(dest);
-        if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
+        //if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Setting key = " + key);
 
-        if (keyUsed != null) {
+        //if (keyUsed != null) {
             //if (I2CPMessageProducer.END_TO_END_CRYPTO) {
             //    if (newKey != null)
             //        keyUsed.setData(newKey.getData());
             //    else
             //        keyUsed.setData(key.getData());
             //} else {
-                keyUsed.setData(SessionKey.INVALID_KEY.getData());
+            //    keyUsed.setData(SessionKey.INVALID_KEY.getData());
             //}
-        }
-        if (tagsSent != null) {
-            if (sentTags != null) {
-                tagsSent.addAll(sentTags);
-            }
-        }
+        //}
+        //if (tagsSent != null) {
+        //    if (sentTags != null) {
+        //        tagsSent.addAll(sentTags);
+        //    }
+        //}
 
         if (_log.shouldLog(Log.DEBUG)) _log.debug("before sync state");
         long beforeSendingSync = _context.clock().now();
@@ -291,7 +309,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
                        + state.getNonce() + " for best effort "
                        + " sync took " + (inSendingSync-beforeSendingSync) 
                        + " add took " + (afterSendingSync-inSendingSync));
-        _producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires);
+        //_producer.sendMessage(this, dest, nonce, payload, tag, key, sentTags, newKey, expires);
+        _producer.sendMessage(this, dest, nonce, payload, null, null, null, null, expires);
         
         // since this is 'best effort', all we're waiting for is a status update 
         // saying that the router received it - in theory, that should come back
diff --git a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
index 58b5cae9f29005e774df26d8bb407ecd0ca86a0a..c0533b1fff24a466102abad0cbec70342c3facc3 100644
--- a/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionMuxedImpl.java
@@ -128,6 +128,10 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
         return sendMessage(dest, payload, 0, payload.length, null, null, 0, proto, fromport, toport);
     }
 
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     @Override
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size,
                                SessionKey keyUsed, Set tagsSent, long expires)
@@ -135,6 +139,10 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
         return sendMessage(dest, payload, offset, size, keyUsed, tagsSent, 0, PROTO_UNSPECIFIED, PORT_UNSPECIFIED, PORT_UNSPECIFIED);
     }
 
+    /**
+     * @param keyUsed unused - no end-to-end crypto
+     * @param tagsSent unused - no end-to-end crypto
+     */
     @Override
     public boolean sendMessage(Destination dest, byte[] payload, int offset, int size, SessionKey keyUsed, Set tagsSent,
                                int proto, int fromport, int toport) throws I2PSessionException {
@@ -142,6 +150,8 @@ class I2PSessionMuxedImpl extends I2PSessionImpl2 implements I2PSession {
     }
 
     /**
+     *  @param keyUsed unused - no end-to-end crypto
+     *  @param tagsSent unused - no end-to-end crypto
      *  @param proto 1-254 or 0 for unset; recommended:
      *         I2PSession.PROTO_UNSPECIFIED
      *         I2PSession.PROTO_STREAMING
diff --git a/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java b/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java
index 4b17a67d3a4c2c4d35754deab90b65c28d9773e1..7b294e3b6e54333d61966cb743aeaa9469c2fcc1 100644
--- a/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java
+++ b/core/java/src/net/i2p/client/MessagePayloadMessageHandler.java
@@ -22,6 +22,8 @@ import net.i2p.util.Log;
  * of a message by accepting it, decrypting the payload, adding it to the set of
  * recieved messages, and telling the router that it has been recieved correctly.
  *
+ * We don't really decrypt (no more end-to-end crypto)
+ *
  * @author jrandom
  */
 class MessagePayloadMessageHandler extends HandlerImpl {
@@ -51,21 +53,24 @@ class MessagePayloadMessageHandler extends HandlerImpl {
 
     /**
      * Decrypt the payload
+     *
+     * We don't really decrypt (no more end-to-end crypto)
+     * If we do, we need to use the correct key manager in the decrypt() call below
      */
     private Payload decryptPayload(MessagePayloadMessage msg, I2PSessionImpl session) throws DataFormatException {
         Payload payload = msg.getPayload();
-        if (!I2CPMessageProducer.END_TO_END_CRYPTO) {
+        //if (!I2CPMessageProducer.END_TO_END_CRYPTO) {
             payload.setUnencryptedData(payload.getEncryptedData());
             return payload;
-        }
+        //}
             
-        byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
-        if (data == null) {
-            if (_log.shouldLog(Log.WARN))
-                _log.warn("Error decrypting the payload");
-            throw new DataFormatException("Unable to decrypt the payload");
-        }
-        payload.setUnencryptedData(data);
-        return payload;
+        //byte[] data = _context.elGamalAESEngine().decrypt(payload.getEncryptedData(), session.getDecryptionKey());
+        //if (data == null) {
+        //    if (_log.shouldLog(Log.WARN))
+        //        _log.warn("Error decrypting the payload");
+        //    throw new DataFormatException("Unable to decrypt the payload");
+        //}
+        //payload.setUnencryptedData(data);
+        //return payload;
     }
 }
diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java
index 0335d1e7eb0ef6f3edfe96373847548dd4df1622..e84489d97173337d30dcdf92a26c85ac23c47067 100644
--- a/core/java/src/net/i2p/crypto/HMAC256Generator.java
+++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java
@@ -12,7 +12,7 @@ import org.bouncycastle.crypto.macs.I2PHMac;
 
 /**
  * Calculate the HMAC-SHA256 of a key+message.  All the good stuff occurs
- * in {@link org.bouncycastle.crypto.macs.HMac} and 
+ * in {@link org.bouncycastle.crypto.macs.I2PHMac} and 
  * {@link org.bouncycastle.crypto.digests.MD5Digest}.
  *
  */
diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java
index e37ec92023efbc7fb4d9f3ea5305b383cb38f5c3..9bf06aa70e32f83391daa70aa755b57e55928bcf 100644
--- a/core/java/src/net/i2p/crypto/HMACGenerator.java
+++ b/core/java/src/net/i2p/crypto/HMACGenerator.java
@@ -15,7 +15,7 @@ import org.bouncycastle.crypto.macs.I2PHMac;
 
 /**
  * Calculate the HMAC-MD5 of a key+message.  All the good stuff occurs
- * in {@link org.bouncycastle.crypto.macs.HMac} and 
+ * in {@link org.bouncycastle.crypto.macs.I2PHMac} and 
  * {@link org.bouncycastle.crypto.digests.MD5Digest}.
  *
  */
diff --git a/core/java/src/net/i2p/crypto/SessionKeyManager.java b/core/java/src/net/i2p/crypto/SessionKeyManager.java
index b1547864cd4c2d4a279b07b158ae04ee4691284b..2cd86ba7d9a54f3b9adb0a6e26327fef88a36d25 100644
--- a/core/java/src/net/i2p/crypto/SessionKeyManager.java
+++ b/core/java/src/net/i2p/crypto/SessionKeyManager.java
@@ -9,6 +9,8 @@ package net.i2p.crypto;
  *
  */
 
+import java.io.IOException;
+import java.io.Writer;
 import java.util.Set;
 
 import net.i2p.I2PAppContext;
@@ -130,4 +132,6 @@ public class SessionKeyManager {
      */
     public void shutdown() { // nop
     }
+
+    public void renderStatusHTML(Writer out) throws IOException {}
 }
diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
index 02be5acd578f19d634d703c2273b6d4b464f104a..e079fce5d1127933ebeb8c79780ec4cecbab86a5 100644
--- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
+++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
@@ -9,7 +9,10 @@ package net.i2p.crypto;
  *
  */
 
+import java.io.IOException;
+import java.io.Writer;
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -17,6 +20,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
@@ -503,57 +507,83 @@ public class TransientSessionKeyManager extends SessionKeyManager {
         return removed;
     }
 
-    public String renderStatusHTML() {
+    @Override
+    public void renderStatusHTML(Writer out) throws IOException {
         StringBuilder buf = new StringBuilder(1024);
-        buf.append("<h2>Inbound sessions</h2>");
-        buf.append("<table>");
+        buf.append("<h2>Inbound sessions</h2>" +
+                   "<table>");
         Set<TagSet> inbound = getInboundTagSets();
         Map<SessionKey, Set<TagSet>> inboundSets = new HashMap(inbound.size());
+        // Build a map of the inbound tag sets, grouped by SessionKey
         for (Iterator<TagSet> iter = inbound.iterator(); iter.hasNext();) {
             TagSet ts = iter.next();
             if (!inboundSets.containsKey(ts.getAssociatedKey())) inboundSets.put(ts.getAssociatedKey(), new HashSet());
             Set<TagSet> sets = inboundSets.get(ts.getAssociatedKey());
             sets.add(ts);
         }
+        int total = 0;
+        long now = _context.clock().now();
         for (Iterator<SessionKey> iter = inboundSets.keySet().iterator(); iter.hasNext();) {
             SessionKey skey = iter.next();
-            Set<TagSet> sets = inboundSets.get(skey);
-            buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>");
-            buf.append("<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>");
-            buf.append("<tr><td colspan=\"2\"><ul>");
+            Set<TagSet> sets = new TreeSet(new TagSetComparator());
+            sets.addAll(inboundSets.get(skey));
+            buf.append("<tr><td><b>Session key</b>: ").append(skey.toBase64()).append("</td>" +
+                       "<td><b># Sets:</b> ").append(sets.size()).append("</td></tr>" +
+                       "<tr><td colspan=\"2\"><ul>");
             for (Iterator<TagSet> siter = sets.iterator(); siter.hasNext();) {
                 TagSet ts = siter.next();
-                buf.append("<li><b>Received on:</b> ").append(new Date(ts.getDate())).append(" with ")
-                   .append(ts.getTags().size()).append(" tags remaining</li>");
+                int size = ts.getTags().size();
+                total += size;
+                buf.append("<li><b>Received:</b> ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with ");
+                buf.append(size).append(" tags remaining</li>");
             }
-            buf.append("</ul></td></tr>");
-        }
-        buf.append("</table>");
-
-        buf.append("<h2><b>Outbound sessions</b></h2>");
-
-        buf.append("<table>");
+            buf.append("</ul></td></tr>\n");
+            out.write(buf.toString());
+            buf.setLength(0);
+        }
+        buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
+        buf.append(DataHelper.formatSize(32*total)).append("B)</th></tr>\n" +
+                   "</table>" +
+                   "<h2><b>Outbound sessions</b></h2>" +
+                   "<table>");
+        total = 0;
         Set<OutboundSession> outbound = getOutboundSessions();
         for (Iterator<OutboundSession> iter = outbound.iterator(); iter.hasNext();) {
             OutboundSession sess = iter.next();
-            buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toString()).append("<br>");
-            buf.append("<b>Established:</b> ").append(new Date(sess.getEstablishedDate())).append("<br>");
-            buf.append("<b>Last Used:</b> ").append(new Date(sess.getLastUsedDate())).append("<br>");
-            buf.append("<b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>");
-            buf.append("<tr><td><b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td></tr>");
-            buf.append("<tr><td><ul>");
-            for (Iterator<TagSet> siter = sess.getTagSets().iterator(); siter.hasNext();) {
+            Set<TagSet> sets = new TreeSet(new TagSetComparator());
+            sets.addAll(sess.getTagSets());
+            buf.append("<tr><td><b>Target key:</b> ").append(sess.getTarget().toBase64().substring(0, 64)).append("<br>" +
+                       "<b>Established:</b> ").append(DataHelper.formatDuration(now - sess.getEstablishedDate())).append(" ago<br>" +
+                       "<b>Last Used:</b> ").append(DataHelper.formatDuration(now - sess.getLastUsedDate())).append(" ago<br>" +
+                       "<b>Session key:</b> ").append(sess.getCurrentKey().toBase64()).append("</td>" +
+                       "<td><b># Sets:</b> ").append(sess.getTagSets().size()).append("</td></tr>" +
+                       "<tr><td colspan=\"2\"><ul>");
+            for (Iterator<TagSet> siter = sets.iterator(); siter.hasNext();) {
                 TagSet ts = siter.next();
-                buf.append("<li><b>Sent on:</b> ").append(new Date(ts.getDate())).append(" with ").append(
-                                                                                                          ts.getTags()
-                                                                                                            .size())
-                   .append(" tags remaining</li>");
+                int size = ts.getTags().size();
+                total += size;
+                buf.append("<li><b>Sent:</b> ").append(DataHelper.formatDuration(now - ts.getDate())).append(" ago with ");
+                buf.append(size).append(" tags remaining</li>");
             }
-            buf.append("</ul></td></tr>");
+            buf.append("</ul></td></tr>\n");
+            out.write(buf.toString());
+            buf.setLength(0);
         }
-        buf.append("</table>");
+        buf.append("<tr><th colspan=\"2\">Total tags: ").append(total).append(" (");
+        buf.append(DataHelper.formatSize(32*total)).append("B)</th></tr>\n" +
+                   "</table>");
 
-        return buf.toString();
+        out.write(buf.toString());
+    }
+
+    /**
+     *  Just for the HTML method above so we can see what's going on easier
+     *  Earliest first
+     */
+    private class TagSetComparator implements Comparator {
+         public int compare(Object l, Object r) {
+             return (int) (((TagSet)l).getDate() - ((TagSet)r).getDate());
+        }
     }
 
     class OutboundSession {
@@ -760,7 +790,7 @@ public class TransientSessionKeyManager extends SessionKeyManager {
         @Override
         public int hashCode() {
             long rv = 0;
-            if (_key != null) rv = rv * 7 + _key.hashCode();
+            if (_key != null) rv = _key.hashCode();
             rv = rv * 7 + _date;
             // no need to hashCode the tags, key + date should be enough
             return (int) rv;
diff --git a/router/java/src/net/i2p/router/ClientManagerFacade.java b/router/java/src/net/i2p/router/ClientManagerFacade.java
index 0ce20df6adbf00fd566b3364faa696c4df26536a..1318232879f32d20b7d60c582630d47d7158e0d3 100644
--- a/router/java/src/net/i2p/router/ClientManagerFacade.java
+++ b/router/java/src/net/i2p/router/ClientManagerFacade.java
@@ -85,13 +85,13 @@ public abstract class ClientManagerFacade implements Service {
      *
      * @return set of Destination objects
      */
-    public Set listClients() { return Collections.EMPTY_SET; }
+    public Set<Destination> listClients() { return Collections.EMPTY_SET; }
     
     /**
      * Return the client's current config, or null if not connected
      *
      */
     public abstract SessionConfig getClientSessionConfig(Destination dest);
-    public abstract SessionKeyManager getClientSessionKeyManager(Destination dest);
+    public abstract SessionKeyManager getClientSessionKeyManager(Hash dest);
     public void renderStatusHTML(Writer out) throws IOException { }
 }
diff --git a/router/java/src/net/i2p/router/DummyClientManagerFacade.java b/router/java/src/net/i2p/router/DummyClientManagerFacade.java
index 5e362e3ddb105d54e9d45b79c6c60219192a6aa4..9c0c6838e0b3f4365ff123757d4fd8218ce46ba1 100644
--- a/router/java/src/net/i2p/router/DummyClientManagerFacade.java
+++ b/router/java/src/net/i2p/router/DummyClientManagerFacade.java
@@ -41,7 +41,7 @@ public class DummyClientManagerFacade extends ClientManagerFacade {
     public void messageDeliveryStatusUpdate(Destination fromDest, MessageId id, boolean delivered) {}
     
     public SessionConfig getClientSessionConfig(Destination _dest) { return null; }
-    public SessionKeyManager getClientSessionKeyManager(Destination _dest) { return null; }
+    public SessionKeyManager getClientSessionKeyManager(Hash _dest) { return null; }
     
     public void requestLeaseSet(Hash dest, LeaseSet set) {}
     
diff --git a/router/java/src/net/i2p/router/client/ClientManager.java b/router/java/src/net/i2p/router/client/ClientManager.java
index 5b4a4fb535ac0b1586d0308562adefa562dd084e..cbc5d778b01b4749f6e31294a774df45638c3701 100644
--- a/router/java/src/net/i2p/router/client/ClientManager.java
+++ b/router/java/src/net/i2p/router/client/ClientManager.java
@@ -42,8 +42,8 @@ import net.i2p.util.Log;
 public class ClientManager {
     private Log _log;
     private ClientListenerRunner _listener;
-    private final HashMap _runners;        // Destination --> ClientConnectionRunner
-    private final Set _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
+    private final HashMap<Destination, ClientConnectionRunner>  _runners;        // Destination --> ClientConnectionRunner
+    private final Set<ClientConnectionRunner> _pendingRunners; // ClientConnectionRunner for clients w/out a Dest yet
     private RouterContext _ctx;
 
     /** ms to wait before rechecking for inbound messages to deliver to clients */
@@ -90,21 +90,21 @@ public class ClientManager {
     public void shutdown() {
         _log.info("Shutting down the ClientManager");
         _listener.stopListening();
-        Set runners = new HashSet();
+        Set<ClientConnectionRunner> runners = new HashSet();
         synchronized (_runners) {
-            for (Iterator iter = _runners.values().iterator(); iter.hasNext();) {
-                ClientConnectionRunner runner = (ClientConnectionRunner)iter.next();
+            for (Iterator<ClientConnectionRunner> iter = _runners.values().iterator(); iter.hasNext();) {
+                ClientConnectionRunner runner = iter.next();
                 runners.add(runner);
             }
         }
         synchronized (_pendingRunners) {
-            for (Iterator iter = _pendingRunners.iterator(); iter.hasNext();) {
-                ClientConnectionRunner runner = (ClientConnectionRunner)iter.next();
+            for (Iterator<ClientConnectionRunner> iter = _pendingRunners.iterator(); iter.hasNext();) {
+                ClientConnectionRunner runner = iter.next();
                 runners.add(runner);
             }
         }
-        for (Iterator iter = runners.iterator(); iter.hasNext(); ) {
-            ClientConnectionRunner runner = (ClientConnectionRunner)iter.next();
+        for (Iterator<ClientConnectionRunner> iter = runners.iterator(); iter.hasNext(); ) {
+            ClientConnectionRunner runner = iter.next();
             runner.stopRunning();
         }
     }
@@ -131,15 +131,26 @@ public class ClientManager {
         }
     }
     
+    /**
+     * Add to the clients list. Check for a dup destination.
+     */
     public void destinationEstablished(ClientConnectionRunner runner) {
+        Destination dest = runner.getConfig().getDestination();
         if (_log.shouldLog(Log.DEBUG))
-            _log.debug("DestinationEstablished called for destination " + runner.getConfig().getDestination().calculateHash().toBase64());
+            _log.debug("DestinationEstablished called for destination " + dest.calculateHash().toBase64());
 
         synchronized (_pendingRunners) {
             _pendingRunners.remove(runner);
         }
+        boolean fail = false;
         synchronized (_runners) {
-            _runners.put(runner.getConfig().getDestination(), runner);
+            fail = _runners.containsKey(dest);
+            if (!fail)
+                _runners.put(dest, runner);
+        }
+        if (fail) {
+            _log.log(Log.CRIT, "Client attempted to register duplicate destination " + dest.calculateHash().toBase64());
+            runner.disconnectClient("Duplicate destination");
         }
     }
     
@@ -278,8 +289,8 @@ public class ClientManager {
         return true;
     }
 
-    public Set listClients() {
-        Set rv = new HashSet();
+    public Set<Destination> listClients() {
+        Set<Destination> rv = new HashSet();
         synchronized (_runners) {
             rv.addAll(_runners.keySet());
         }
@@ -293,7 +304,7 @@ public class ClientManager {
         long inLock = 0;
         synchronized (_runners) {
             inLock = _ctx.clock().now();
-            rv = (ClientConnectionRunner)_runners.get(dest);
+            rv = _runners.get(dest);
         }
         long afterLock = _ctx.clock().now();
         if (afterLock - beforeLock > 50) {
@@ -317,9 +328,10 @@ public class ClientManager {
     
     /**
      * Return the client's SessionKeyManager
-     *
+     * Use this instead of the RouterContext.sessionKeyManager()
+     * to prevent correlation attacks across destinations
      */
-    public SessionKeyManager getClientSessionKeyManager(Destination dest) {
+    public SessionKeyManager getClientSessionKeyManager(Hash dest) {
         ClientConnectionRunner runner = getRunner(dest);
         if (runner != null)
             return runner.getSessionKeyManager();
@@ -331,8 +343,8 @@ public class ClientManager {
         if (destHash == null) 
             return null;
         synchronized (_runners) {
-            for (Iterator iter = _runners.values().iterator(); iter.hasNext(); ) {
-                ClientConnectionRunner cur = (ClientConnectionRunner)iter.next();
+            for (Iterator<ClientConnectionRunner> iter = _runners.values().iterator(); iter.hasNext(); ) {
+                ClientConnectionRunner cur = iter.next();
                 if (cur.getDestHash().equals(destHash))
                     return cur;
 	    }
@@ -354,8 +366,8 @@ public class ClientManager {
         }
     }
     
-    Set getRunnerDestinations() {
-        Set dests = new HashSet();
+    Set<Destination> getRunnerDestinations() {
+        Set<Destination> dests = new HashSet();
         long beforeLock = _ctx.clock().now();
         long inLock = 0;
         synchronized (_runners) {
@@ -390,13 +402,13 @@ public class ClientManager {
         StringBuilder buf = new StringBuilder(8*1024);
         buf.append("<u><b>Local destinations</b></u><br>");
         
-        Map runners = null;
+        Map<Destination, ClientConnectionRunner> runners = null;
         synchronized (_runners) {
             runners = (Map)_runners.clone();
         }
-        for (Iterator iter = runners.keySet().iterator(); iter.hasNext(); ) {
-            Destination dest = (Destination)iter.next();
-            ClientConnectionRunner runner = (ClientConnectionRunner)runners.get(dest);
+        for (Iterator<Destination> iter = runners.keySet().iterator(); iter.hasNext(); ) {
+            Destination dest = iter.next();
+            ClientConnectionRunner runner = runners.get(dest);
             buf.append("<b>*</b> ").append(dest.calculateHash().toBase64().substring(0,6)).append("<br>\n");
             LeaseSet ls = runner.getLeaseSet();
             if (ls == null) {
diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
index 9e706beda02fcc7449cf19214a08eb722f7744fe..e90d12f53599c7facc7c17b967c3ddc7b0c5cc5d 100644
--- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
+++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java
@@ -194,7 +194,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
      * Return the client's current manager or null if not connected
      *
      */
-    public SessionKeyManager getClientSessionKeyManager(Destination dest) {
+    public SessionKeyManager getClientSessionKeyManager(Hash dest) {
         if (_manager != null)
             return _manager.getClientSessionKeyManager(dest);
         else {
@@ -215,7 +215,7 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
      * @return set of Destination objects
      */
     @Override
-    public Set listClients() {
+    public Set<Destination> listClients() {
         if (_manager != null)
             return _manager.listClients();
         else
diff --git a/router/java/src/net/i2p/router/message/BuildTestMessageJob.java b/router/java/test/net/i2p/router/message/BuildTestMessageJob.java
similarity index 100%
rename from router/java/src/net/i2p/router/message/BuildTestMessageJob.java
rename to router/java/test/net/i2p/router/message/BuildTestMessageJob.java
diff --git a/router/java/src/net/i2p/router/message/SendGarlicJob.java b/router/java/test/net/i2p/router/message/SendGarlicJob.java
similarity index 100%
rename from router/java/src/net/i2p/router/message/SendGarlicJob.java
rename to router/java/test/net/i2p/router/message/SendGarlicJob.java