diff --git a/core/java/src/net/i2p/client/I2PSessionImpl.java b/core/java/src/net/i2p/client/I2PSessionImpl.java
index 2a0f9ed4a76cbadcc04cb1677fc4d76bd6bb905d..6990f2a51d73bf323572df1d874e007aa9311365 100644
--- a/core/java/src/net/i2p/client/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/I2PSessionImpl.java
@@ -647,6 +647,8 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
             // now send CreateSessionMessages for all subsessions, one at a time, must wait for each response
             synchronized(_subsessionLock) {
                 for (SubSession ss : _subsessions) {
+                   if (_log.shouldLog(Log.INFO))
+                       _log.info(getPrefix() + "Connecting subsession " + ss);
                     _producer.connect(ss);
                 }
             }
diff --git a/core/java/src/net/i2p/client/SubSession.java b/core/java/src/net/i2p/client/SubSession.java
index 1e75fa409e98d64a29dccb838a6cd8e622329c8b..9dcf04e72d2cbd355286a99e56f9ed7351d3e33a 100644
--- a/core/java/src/net/i2p/client/SubSession.java
+++ b/core/java/src/net/i2p/client/SubSession.java
@@ -114,7 +114,8 @@ class SubSession extends I2PSessionMuxedImpl {
      */
     @Override
     public boolean isClosed() {
-        return getSessionId() == null || _primary.isClosed();
+        // FIXME
+        return /* getSessionId() == null || */  _primary.isClosed();
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
index e12205e8305c82d21c10bb3964cce69351e4463e..ead5dcd2628f2123c15294e68455ca6de125031a 100644
--- a/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
+++ b/router/java/src/net/i2p/router/client/ClientMessageEventListener.java
@@ -248,9 +248,18 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
         SessionConfig cfg = new SessionConfig(dest);
         cfg.setSignature(in.getSignature());
         Properties props = new Properties();
-        props.putAll(in.getOptions());
-        cfg.setOptions(props);
         boolean isPrimary = _runner.getSessionIds().isEmpty();
+        if (!isPrimary) {
+            // all the primary options, then the overrides from the alias
+            SessionConfig pcfg = _runner.getPrimaryConfig();
+            if (pcfg != null) {
+                props.putAll(pcfg.getOptions());
+            } else {
+                _log.error("no primary config?");
+            }
+        }
+        props.putAll(inProps);
+        cfg.setOptions(props);
         // this sets the session id
         int status = _runner.sessionEstablished(cfg);
         if (status != SessionStatusMessage.STATUS_CREATED) {
@@ -269,28 +278,27 @@ class ClientMessageEventListener implements I2CPMessageReader.I2CPMessageEventLi
         }
         // get the new session ID
         id = _runner.getSessionId(dest.calculateHash());
-        sendStatusMessage(id, status);
 
         if (_log.shouldLog(Log.INFO))
             _log.info("Session " + id + " established for " + dest.calculateHash());
         if (isPrimary) {
+            sendStatusMessage(id, status);
             startCreateSessionJob(cfg);
         } else {
             SessionConfig pcfg = _runner.getPrimaryConfig();
             if (pcfg != null) {
-                ///////////
-                // new tunnel name etc.
                 ClientTunnelSettings settings = new ClientTunnelSettings(dest.calculateHash());
-                // all the primary options, then the overrides from the alias
-                props.putAll(pcfg.getOptions());
-                props.putAll(props);
                 settings.readFromProperties(props);
                 boolean ok = _context.tunnelManager().addAlias(dest, settings, pcfg.getDestination());
                 if (!ok) {
                     _log.error("Add alias failed");
-                    // send status message...
+                    status = SessionStatusMessage.STATUS_REFUSED;
                 }
+            } else {
+                _log.error("no primary config?");
+                status = SessionStatusMessage.STATUS_INVALID;
             }
+            sendStatusMessage(id, status);
         }
     }
     
diff --git a/router/java/src/net/i2p/router/tunnel/pool/AliasedTunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/AliasedTunnelPool.java
new file mode 100644
index 0000000000000000000000000000000000000000..05132c9adeeee29895b5d94ce63b258970b45501
--- /dev/null
+++ b/router/java/src/net/i2p/router/tunnel/pool/AliasedTunnelPool.java
@@ -0,0 +1,141 @@
+package net.i2p.router.tunnel.pool;
+
+import java.util.List;
+
+import net.i2p.data.Hash;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.TunnelId;
+import net.i2p.router.RouterContext;
+import net.i2p.router.TunnelInfo;
+import net.i2p.router.TunnelPoolSettings;
+import net.i2p.util.Log;
+
+/**
+ *  A tunnel pool with its own settings and Destination,
+ *  but uses another pool for its tunnels.
+ *
+ *  @since 0.9.20
+ */
+public class AliasedTunnelPool extends TunnelPool {
+    
+    private final TunnelPool _aliasOf;
+
+    AliasedTunnelPool(RouterContext ctx, TunnelPoolManager mgr, TunnelPoolSettings settings, TunnelPool aliasOf) {
+        super(ctx, mgr, settings, null);
+        if (settings.isExploratory())
+            throw new IllegalArgumentException();
+        if (settings.getAliasOf() == null)
+            throw new IllegalArgumentException();
+        _aliasOf = aliasOf;
+    }
+    
+    @Override
+    synchronized void startup() {
+        if (_log.shouldLog(Log.INFO))
+            _log.info(toString() + ": Startup() called, was already alive? " + _alive, new Exception());
+        _alive = true;
+    }
+    
+    @Override
+    synchronized void shutdown() {
+        if (_log.shouldLog(Log.WARN))
+            _log.warn(toString() + ": Shutdown called");
+        _alive = false;
+    }
+    
+    @Override
+    TunnelInfo selectTunnel() {
+        return _aliasOf.selectTunnel();
+    }
+
+    @Override
+    TunnelInfo selectTunnel(Hash closestTo) {
+        return _aliasOf.selectTunnel(closestTo);
+    }
+    
+    @Override
+    public TunnelInfo getTunnel(TunnelId gatewayId) {
+        return _aliasOf.getTunnel(gatewayId);
+    }
+    
+    @Override
+    public List<TunnelInfo> listTunnels() {
+        return _aliasOf.listTunnels();
+    }
+    
+    @Override
+    boolean needFallback() {
+        return false;
+    }
+
+    @Override
+    public List<PooledTunnelCreatorConfig> listPending() {
+        return _aliasOf.listPending();
+    }
+    
+    @Override
+    public boolean isAlive() {
+        return _alive && _aliasOf.isAlive();
+    }
+
+    @Override
+    public int size() { 
+        return _aliasOf.size();
+    }
+    
+    @Override
+    void addTunnel(TunnelInfo info) {
+        _aliasOf.addTunnel(info);
+    }
+    
+    @Override
+    void removeTunnel(TunnelInfo info) {
+        _aliasOf.removeTunnel(info);
+    }
+
+    @Override
+    void tunnelFailed(TunnelInfo cfg) {
+        _aliasOf.tunnelFailed(cfg);
+    }
+
+    @Override
+    void tunnelFailed(TunnelInfo cfg, Hash blamePeer) {
+        _aliasOf.tunnelFailed(cfg, blamePeer);
+    }
+
+    @Override
+    void refreshLeaseSet() {}
+
+    @Override
+    boolean buildFallback() {
+        return _aliasOf.buildFallback();
+    }
+
+    @Override
+    protected LeaseSet locked_buildNewLeaseSet() {
+        return _context.netDb().lookupLeaseSetLocally(_aliasOf.getSettings().getDestination());
+    }
+
+    @Override
+    public long getLifetimeProcessed() {
+        return _aliasOf.getLifetimeProcessed();
+    }
+
+    @Override
+    int countHowManyToBuild() {
+        return 0;
+    }
+    
+    @Override
+    PooledTunnelCreatorConfig configureNewTunnel() {
+        return null;
+    }
+
+    @Override
+    void buildComplete(PooledTunnelCreatorConfig cfg) {}
+    
+    @Override
+    public String toString() {
+        return "Aliased " + super.toString();
+    }
+}
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
index e30a0faddd3d7d9d01b56630105bc775eb823db8..b69caa7896cca678071014a352afba859a02e025 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
@@ -31,13 +31,13 @@ import net.i2p.util.Log;
  */
 public class TunnelPool {
     private final List<PooledTunnelCreatorConfig> _inProgress = new ArrayList<PooledTunnelCreatorConfig>();
-    private final RouterContext _context;
-    private final Log _log;
+    protected final RouterContext _context;
+    protected final Log _log;
     private TunnelPoolSettings _settings;
     private final List<TunnelInfo> _tunnels;
     private final TunnelPeerSelector _peerSelector;
     private final TunnelPoolManager _manager;
-    private volatile boolean _alive;
+    protected volatile boolean _alive;
     private long _lifetimeProcessed;
     private TunnelInfo _lastSelected;
     private long _lastSelectionPeriod;
@@ -119,19 +119,15 @@ public class TunnelPool {
         }
     }
 
-    void refreshSettings() {
-        if (!_settings.isExploratory()) {
+    private void refreshSettings() {
+        if (!_settings.isExploratory())
             return; // don't override client specified settings
-        } else {
-            if (_settings.isExploratory()) {
-                Properties props = new Properties();
-                props.putAll(_context.router().getConfigMap());
-                if (_settings.isInbound())
-                    _settings.readFromProperties(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY, props);
-                else
-                    _settings.readFromProperties(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY, props);
-            }
-        }
+        Properties props = new Properties();
+        props.putAll(_context.router().getConfigMap());
+        if (_settings.isInbound())
+            _settings.readFromProperties(TunnelPoolSettings.PREFIX_INBOUND_EXPLORATORY, props);
+        else
+            _settings.readFromProperties(TunnelPoolSettings.PREFIX_OUTBOUND_EXPLORATORY, props);
     }
     
     /** 
@@ -413,11 +409,15 @@ public class TunnelPool {
     public List<PooledTunnelCreatorConfig> listPending() { synchronized (_inProgress) { return new ArrayList<PooledTunnelCreatorConfig>(_inProgress); } }
     
     /** duplicate of size(), let's pick one */
-    int getTunnelCount() { synchronized (_tunnels) { return _tunnels.size(); } }
+    int getTunnelCount() { return size(); }
     
     public TunnelPoolSettings getSettings() { return _settings; }
 
     void setSettings(TunnelPoolSettings settings) { 
+        if (settings != null && _settings != null) {
+            settings.getAliases().addAll(_settings.getAliases());
+            settings.setAliasOf(_settings.getAliasOf());
+        }
         _settings = settings; 
         if (_settings != null) {
             if (_log.shouldLog(Log.INFO))
@@ -717,7 +717,7 @@ public class TunnelPool {
      *
      * @return null on failure
      */
-    private LeaseSet locked_buildNewLeaseSet() {
+    protected LeaseSet locked_buildNewLeaseSet() {
         if (!_alive)
             return null;
 
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 6cba6ef4b6dfc1bed1e850339335b13cc255d4dc..f318b479de28b7e3fd68960ec9f1d991288a93a5 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPoolManager.java
@@ -451,14 +451,35 @@ public class TunnelPoolManager implements TunnelManagerFacade {
         Hash h = dest.calculateHash();
         Hash e = existingClient.calculateHash();
         synchronized(this) {
-            TunnelPool inbound = _clientInboundPools.get(e);
-            TunnelPool outbound = _clientOutboundPools.get(e);
-/////// gah same tunnel pool or different?
-            if (inbound == null || outbound == null)
+            TunnelPool inbound = _clientInboundPools.get(h);
+            TunnelPool outbound = _clientOutboundPools.get(h);
+            if (inbound != null || outbound != null) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("already have alias " + dest);
+                return false;
+            }
+            TunnelPool eInbound = _clientInboundPools.get(e);
+            TunnelPool eOutbound = _clientOutboundPools.get(e);
+            if (eInbound == null || eOutbound == null) {
+                if (_log.shouldLog(Log.WARN))
+                    _log.warn("primary not found " + existingClient);
                 return false;
+            }
+            eInbound.getSettings().getAliases().add(h);
+            eOutbound.getSettings().getAliases().add(h);
+            TunnelPoolSettings newIn = settings.getInboundSettings();
+            TunnelPoolSettings newOut = settings.getOutboundSettings();
+            newIn.setAliasOf(e);
+            newOut.setAliasOf(e);
+            inbound = new AliasedTunnelPool(_context, this, newIn, eInbound);
+            outbound = new AliasedTunnelPool(_context, this, newOut, eOutbound);
             _clientInboundPools.put(h, inbound);
             _clientOutboundPools.put(h, outbound);
+            inbound.startup();
+            outbound.startup();
         }
+        if (_log.shouldLog(Log.WARN))
+            _log.warn("Added " + h + " as alias for " + e + " with settings " + settings);
         return true;
     }
 
@@ -483,9 +504,9 @@ public class TunnelPoolManager implements TunnelManagerFacade {
             }
             TunnelPool outbound = _clientOutboundPools.remove(h);
             if (outbound != null) {
-                Hash p = inbound.getSettings().getAliasOf();
+                Hash p = outbound.getSettings().getAliasOf();
                 if (p != null) {
-                    TunnelPool pri = _clientInboundPools.get(p);
+                    TunnelPool pri = _clientOutboundPools.get(p);
                     if (pri != null) {
                         Set<Hash> aliases = pri.getSettings().getAliases();
                         if (aliases != null)