diff --git a/router/java/src/net/i2p/router/LeaseSetKeys.java b/router/java/src/net/i2p/router/LeaseSetKeys.java
index 559a0b65f6bb24aa43660e3f2b1de6896b2c831a..537e36d316a60152557ab4c3daebac8fcd3caaf5 100644
--- a/router/java/src/net/i2p/router/LeaseSetKeys.java
+++ b/router/java/src/net/i2p/router/LeaseSetKeys.java
@@ -32,7 +32,7 @@ public class LeaseSetKeys {
      * @since 0.9.44
      */
     public static final Set<EncType> SET_ELG = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048));
-    private static final Set<EncType> SET_EC = Collections.unmodifiableSet(EnumSet.of(EncType.ECIES_X25519));
+    public static final Set<EncType> SET_EC = Collections.unmodifiableSet(EnumSet.of(EncType.ECIES_X25519));
     private static final Set<EncType> SET_BOTH = Collections.unmodifiableSet(EnumSet.of(EncType.ELGAMAL_2048, EncType.ECIES_X25519));
     private static final Set<EncType> SET_NONE = Collections.unmodifiableSet(EnumSet.noneOf(EncType.class));
 
diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
index 5f62ebdfe7b6978a04d717f177021c5b28d7f84c..b16df7199ceeaee52122d515087f70e6bd4ad9ae 100644
--- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
+++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
@@ -606,14 +606,14 @@ class ClientConnectionRunner {
             if (hasElg) {
                 TransientSessionKeyManager tskm = new TransientSessionKeyManager(_context, tags, thresh);
                 if (hasEC) {
-                    RatchetSKM rskm = new RatchetSKM(_context);
+                    RatchetSKM rskm = new RatchetSKM(_context, dest);
                     _sessionKeyManager = new MuxedSKM(tskm, rskm);
                 } else {
                     _sessionKeyManager = tskm;
                 }
             } else {
                 if (hasEC) {
-                    _sessionKeyManager = new RatchetSKM(_context);
+                    _sessionKeyManager = new RatchetSKM(_context, dest);
                 } else {
                     _log.error("No supported encryption types in i2cp.leaseSetEncType for " + dest.toBase32());
                     return SessionStatusMessage.STATUS_INVALID;
diff --git a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
index ebe13ce0d977ab16bb971b4793a72d198ec283ef..d57eaa66bf844e4a8687fb2965d84b9ed4541bf6 100644
--- a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
+++ b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
@@ -19,15 +19,23 @@ import net.i2p.crypto.HKDF;
 import net.i2p.crypto.SessionKeyManager;
 import net.i2p.data.Base64;
 import net.i2p.data.Certificate;
+import net.i2p.data.DatabaseEntry;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
 import net.i2p.data.Hash;
+import net.i2p.data.LeaseSet;
+import net.i2p.data.LeaseSet2;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.SessionKey;
 import net.i2p.data.SessionTag;
+import net.i2p.data.i2np.DatabaseStoreMessage;
 import net.i2p.data.i2np.GarlicClove;
+import net.i2p.data.i2np.I2NPMessage;
 import static net.i2p.router.crypto.ratchet.RatchetPayload.*;
+import net.i2p.router.LeaseSetKeys;
+import net.i2p.router.Router;
 import net.i2p.router.RouterContext;
 import net.i2p.router.message.CloveSet;
 import net.i2p.util.Log;
@@ -395,13 +403,13 @@ public final class ECIESAEADEngine {
             return NO_CLOVES;
         }
 
-        byte[] bobPK = new byte[KEYLEN];
-        state.getRemotePublicKey().getPublicKey(bobPK, 0);
+        byte[] alicePK = new byte[KEYLEN];
+        state.getRemotePublicKey().getPublicKey(alicePK, 0);
         if (_log.shouldDebug()) {
-            _log.debug("NS decrypt success from PK " + Base64.encode(bobPK));
+            _log.debug("NS decrypt success from PK " + Base64.encode(alicePK));
             _log.debug("State after decrypt new session: " + state);
         }
-        if (Arrays.equals(bobPK, NULLPK)) {
+        if (Arrays.equals(alicePK, NULLPK)) {
             // TODO
             if (_log.shouldWarn())
                 _log.warn("Zero static key in IB NS");
@@ -434,8 +442,8 @@ public final class ECIESAEADEngine {
         }
 
         // tell the SKM
-        PublicKey bob = new PublicKey(EncType.ECIES_X25519, bobPK);
-        keyManager.createSession(bob, state, null);
+        PublicKey alice = new PublicKey(EncType.ECIES_X25519, alicePK);
+        keyManager.createSession(alice, state, null);
 
         if (pc.cloveSet.isEmpty()) {
             // this is legal
@@ -447,6 +455,8 @@ public final class ECIESAEADEngine {
         GarlicClove[] arr = new GarlicClove[num];
         // msg id and expiration not checked in GarlicMessageReceiver
         CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
+        // TODO
+        //setResponseTimer(alice, pc.cloveSet, keyManager);
         return rv;
     }
 
@@ -587,6 +597,8 @@ public final class ECIESAEADEngine {
         GarlicClove[] arr = new GarlicClove[num];
         // msg id and expiration not checked in GarlicMessageReceiver
         CloveSet rv = new CloveSet(pc.cloveSet.toArray(arr), Certificate.NULL_CERT, 0, pc.datetime);
+        // TODO
+        //setResponseTimer(bob, pc.cloveSet, keyManager);
         return rv;
     }
 
@@ -679,6 +691,7 @@ public final class ECIESAEADEngine {
         return true;
     }
 
+
     //// end decrypt, start encrypt ////
 
 
@@ -1207,6 +1220,39 @@ public final class ECIESAEADEngine {
         return payload;
     }
 
+    /*
+     * Set a timer for a ratchet-layer reply if the application does not respond.
+     *
+     * @since 0.9.46
+     */
+    private void setResponseTimer(PublicKey from, List<GarlicClove> cloveSet, RatchetSKM skm) {
+        for (GarlicClove clove : cloveSet) {
+            I2NPMessage msg = clove.getData();
+            if (msg.getType() != DatabaseStoreMessage.MESSAGE_TYPE)
+                continue;
+            DatabaseStoreMessage dsm = (DatabaseStoreMessage) msg;
+            DatabaseEntry entry = dsm.getEntry();
+            if (entry.getType() != DatabaseEntry.KEY_TYPE_LS2)
+                continue;
+            LeaseSet2 ls2 = (LeaseSet2) entry;
+            if (!ls2.isCurrent(Router.CLOCK_FUDGE_FACTOR))
+                continue;
+            PublicKey pk = ls2.getEncryptionKey(LeaseSetKeys.SET_EC);
+            if (!from.equals(pk))
+                continue;
+            if (!ls2.verifySignature())
+                continue;
+            // OK, we have a valid place to send the reply
+            Destination d = ls2.getDestination();
+            if (_log.shouldInfo())
+                _log.info("Validated NS sender: " + d.toBase32());
+            // TODO
+            return;
+        }
+        if (_log.shouldInfo())
+            _log.info("Unvalidated NS sender: " + from);
+    }
+
 
 /****
     public static void main(String args[]) {
diff --git a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java
index ad6fd706595cb2a2d1328b186ba39c665bafa497..71ea1b12d6d95dade8a3c4e564679f34227015ee 100644
--- a/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java
+++ b/router/java/src/net/i2p/router/crypto/ratchet/RatchetSKM.java
@@ -26,6 +26,7 @@ import net.i2p.crypto.SessionKeyManager;
 import net.i2p.crypto.TagSetHandle;
 import net.i2p.data.Base64;
 import net.i2p.data.DataHelper;
+import net.i2p.data.Destination;
 import net.i2p.data.PrivateKey;
 import net.i2p.data.PublicKey;
 import net.i2p.data.SessionKey;
@@ -52,6 +53,7 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
     private volatile boolean _alive;
     private final HKDF _hkdf;
     private final DecayingHashSet _replayFilter;
+    private final Destination _destination;
 
     /**
      * Let outbound session tags sit around for this long before expiring them.
@@ -81,10 +83,11 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
      * appropriate application context itself.
      *
      */
-    public RatchetSKM(RouterContext context) {
+    public RatchetSKM(RouterContext context, Destination dest) {
         super(context);
         _log = context.logManager().getLog(RatchetSKM.class);
         _context = context;
+        _destination = dest;
         _outboundSessions = new ConcurrentHashMap<PublicKey, OutboundSession>(64);
         _pendingOutboundSessions = new HashMap<PublicKey, List<OutboundSession>>(64);
         _inboundTagSets = new ConcurrentHashMap<RatchetSessionTag, RatchetTagSet>(128);
@@ -124,6 +127,12 @@ public class RatchetSKM extends SessionKeyManager implements SessionTagListener
         }
     }
 
+    /**
+     *  @since 0.9.46
+     */
+    public Destination getDestination() {
+        return _destination;
+    }
 
     /** RatchetTagSet */
     private Set<RatchetTagSet> getRatchetTagSets() {