diff --git a/history.txt b/history.txt
index 8a4443def8d93c9afc2b044fed8373ab5f78972b..5569308c91a023e20db2e29d2f7d302eb58ffab7 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,30 @@
+2020-10-15 zzz
+ * Router:
+   - More efficient initialization of Noise state
+   - Destroy ratchet HandshakeState after NS failure
+   - Add support for ratchet zero key (proposals #144, #156)
+
+2020-10-12 zzz
+ * DTG: Enable by default for Linux KDE and LXDE;
+   Hide option on /configservice if not supported
+ * New translations for Kurdish, Turkmen, Argentinian Spanish
+ * NTCP:
+   - Fix sending termination on idle timeout (ticket #2777)
+   - Catch possible race IAE in Reader
+
+2020-10-11 zzz
+ * Installer: Disable pack200 (ticket #2778)
+
+2020-10-10 zzz
+ * i2psnark: Cache length of metainfo
+ * Transport: Improved IPv6 address validation
+
+2020-10-09 zzz
+ *  NetDB:
+    - Don't use DSA-SHA1 routers for lookups, stores, or tunnel peers
+    - Don't use non-ElGamal routers for lookups or stores
+    - Prevent DSA-SHA1 routers from auto-floodfill
+
 2020-10-07 zzz
  * Build:
    - Set javac release property (ticket #2775)
@@ -5,6 +32,7 @@
    - Drop support for Xenial package build
    - Fix up BOB build configuration
    - Fix i2psnark standalone build
+ * i2ptunnel: Filter server response headers even if not compressing
 
 2020-10-03 zzz
  * Router: Support building tunnels through ECIES routers (proposal 152)
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index c10128fe6223a59f4fa2320e38c3309487438a87..fecba78d6973d87e9930d631de787e05fec2a510 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 6;
+    public final static long BUILD = 7;
 
     /** for example "-test" */
     public final static String EXTRA = "";
diff --git a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
index 70e7b1eef11761f71b2678b30432fd3fe9883d4d..3e4bcd9709000edac7876f0f354c9f3585f0f1d0 100644
--- a/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
+++ b/router/java/src/net/i2p/router/message/GarlicMessageBuilder.java
@@ -275,16 +275,14 @@ public class GarlicMessageBuilder {
      * Called by OCMJH only.
      *
      * @param ctx scope
-     * @param config how/what to wrap
-     * @param target public key of the location being garlic routed to (may be null if we 
-     *               know the encryptKey and encryptTag)
+     * @param config how/what to wrap, must have key set with setRecipientPublicKey()
      * @param callback may be null
      * @return null if expired or on other errors
      * @throws IllegalArgumentException on error
      * @since 0.9.44
      */
     static GarlicMessage buildECIESMessage(RouterContext ctx, GarlicConfig config,
-                                           PublicKey target, Hash from, Destination to, SessionKeyManager skm,
+                                           Hash from, Destination to, SessionKeyManager skm,
                                            ReplyCallback callback) {
         PublicKey key = config.getRecipientPublicKey();
         if (key.getType() != EncType.ECIES_X25519)
@@ -315,7 +313,7 @@ public class GarlicMessageBuilder {
                 log.warn("No SKM for " + from.toBase32());
             return null;
         }
-        byte encData[] = ctx.eciesEngine().encrypt(cloveSet, target, to, priv, rskm, callback);
+        byte encData[] = ctx.eciesEngine().encrypt(cloveSet, key, to, priv, rskm, callback);
         if (encData == null) {
             if (log.shouldWarn())
                 log.warn("Encrypt fail for " + from.toBase32());
@@ -334,6 +332,42 @@ public class GarlicMessageBuilder {
         return msg;
     }
     
+    /**
+     * Encrypt from an anonymous source.
+     * ECIES_X25519 only.
+     * Called by MessageWrapper only.
+     *
+     * @param ctx scope
+     * @param config how/what to wrap, must have key set with setRecipientPublicKey()
+     * @throws IllegalArgumentException on error
+     * @since 0.9.48
+     */
+    public static GarlicMessage buildECIESMessage(RouterContext ctx, GarlicConfig config) {
+        PublicKey key = config.getRecipientPublicKey();
+        if (key.getType() != EncType.ECIES_X25519)
+            throw new IllegalArgumentException();
+        Log log = ctx.logManager().getLog(GarlicMessageBuilder.class);
+        GarlicMessage msg = new GarlicMessage(ctx);
+        CloveSet cloveSet = buildECIESCloveSet(ctx, config);
+        byte encData[] = ctx.eciesEngine().encrypt(cloveSet, key);
+        if (encData == null) {
+            if (log.shouldWarn())
+                log.warn("Encrypt fail for " + config);
+            return null;
+        }
+        msg.setData(encData);
+        msg.setMessageExpiration(config.getExpiration());
+        long timeFromNow = config.getExpiration() - ctx.clock().now();
+        if (timeFromNow < 1*1000) {
+            if (log.shouldDebug())
+                log.debug("Building a message expiring in " + timeFromNow + "ms: " + config, new Exception("created by"));
+            return null;
+        }
+        if (log.shouldDebug())
+            log.debug("Built ECIES CloveSet (" + config.getCloveCount() + " cloves) in " + msg);
+        return msg;
+    }
+    
 /****
     private static void noteWrap(RouterContext ctx, GarlicMessage wrapper, GarlicConfig contained) {
         for (int i = 0; i < contained.getCloveCount(); i++) {
diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java
index 9d4bc3ea8150e216e353b48d7b19323b4d201363..ac462bcfcee5bc922f6d950262c7533005c83d1c 100644
--- a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java
+++ b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java
@@ -131,7 +131,7 @@ class OutboundClientMessageJobHelper {
             return null;
         GarlicMessage msg;
         if (isECIES) {
-            msg = GarlicMessageBuilder.buildECIESMessage(ctx, config, recipientPK, from, dest, skm, callback);
+            msg = GarlicMessageBuilder.buildECIESMessage(ctx, config, from, dest, skm, callback);
         } else {
             // no use sending tags unless we have a reply token set up already
             int tagsToSend = replyToken >= 0 ? (tagsToSendOverride > 0 ? tagsToSendOverride : skm.getTagsToSend()) : 0;
@@ -146,6 +146,8 @@ class OutboundClientMessageJobHelper {
      * Make the top-level config, with a data clove, an optional ack clove, and
      * an optional leaseset clove.
      *
+     * The returned GarlicConfig will have the recipientPublicKey set.
+     *
      * @param dataClove may be null for ECIES-layer ack
      * @param replyTunnel non-null if requireAck is true or bundledReplyLeaseSet is non-null
      * @param requireAck if true, bundle replyToken in an ack clove
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java
index d7dca37e1e7926c07dd94ee2ecd57ba2c63a469c..ce5e0af322c1cd00b542f3ceadfdc111b8350d7f 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java
@@ -125,16 +125,13 @@ public class MessageWrapper {
     /**
      *  Garlic wrap a message from nobody, destined for a router,
      *  to hide the contents from the OBEP.
-     *  Forces ElGamal.
+     *  Forces full asymmetric encryption.
      *
-     *  @param to must be ELGAMAL_2048 EncType
+     *  @param to must be ELGAMAL_2048 or ECIES_X25519 EncType
      *  @return null on encrypt failure
      *  @since 0.9.5
      */
     static GarlicMessage wrap(RouterContext ctx, I2NPMessage m, RouterInfo to) {
-        PublicKey key = to.getIdentity().getPublicKey();
-        if (key.getType() != EncType.ELGAMAL_2048)
-            return null;
 
         PayloadGarlicConfig payload = new PayloadGarlicConfig(Certificate.NULL_CERT,
                                                               ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE),
@@ -142,9 +139,19 @@ public class MessageWrapper {
                                                               DeliveryInstructions.LOCAL, m);
         payload.setRecipient(to);
 
-        SessionKey sentKey = ctx.keyGenerator().generateSessionKey();
-        GarlicMessage msg = GarlicMessageBuilder.buildMessage(ctx, payload, null, 
-                                                              key, sentKey, null);
+        PublicKey key = to.getIdentity().getPublicKey();
+        EncType type = key.getType();
+        GarlicMessage msg;
+        if (type == EncType.ELGAMAL_2048) {
+            SessionKey sentKey = ctx.keyGenerator().generateSessionKey();
+            msg = GarlicMessageBuilder.buildMessage(ctx, payload, null, key, sentKey, null);
+        } else if (type == EncType.ECIES_X25519) {
+            payload.setRecipientPublicKey(key);
+            msg = GarlicMessageBuilder.buildECIESMessage(ctx, payload);
+        } else {
+            // unsupported
+            msg = null;
+        }
         return msg;
     }