diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
index 55c9419c7cf48552c0dd37ec46fba306005019fa..3ad564c5152ae2523f02c33fbd429db096b2a49d 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java
@@ -1204,18 +1204,29 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
                 }
             } else if("i2p".equals(host)) {
                 clientDest = null;
-            } else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
+            } else if (destination.length() >= 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
                 // use existing session to look up for efficiency
                 verifySocketManager();
                 I2PSession sess = sockMgr.getSession();
-                if(!sess.isClosed()) {
-                    byte[] hData = Base32.decode(destination.substring(0, 52));
-                    if(hData != null) {
-                        if(_log.shouldLog(Log.INFO)) {
-                            _log.info("lookup in-session " + destination);
+                if (!sess.isClosed()) {
+                    int len = destination.length();
+                    if (len == 60) {
+                        byte[] hData = Base32.decode(destination.substring(0, 52));
+                        if (hData != null) {
+                            if (_log.shouldInfo())
+                                _log.info("lookup b32 in-session " + destination);
+                            Hash hash = Hash.create(hData);
+                            clientDest = sess.lookupDest(hash, 20*1000);
+                        } else {
+                            clientDest = null;
                         }
-                        Hash hash = Hash.create(hData);
-                        clientDest = sess.lookupDest(hash, 20 * 1000);
+                    } else if (len >= 64) {
+                        if (_log.shouldInfo())
+                            _log.info("lookup b33 in-session " + destination);
+                        clientDest = sess.lookupDest(destination, 20*1000);
+                    } else {
+                        // 61-63 chars, this won't work
+                        clientDest = _context.namingService().lookup(destination);
                     }
                 } else {
                     clientDest = _context.namingService().lookup(destination);
diff --git a/core/java/src/net/i2p/client/naming/DummyNamingService.java b/core/java/src/net/i2p/client/naming/DummyNamingService.java
index 73970a09dd8654f01d4bc0f7cd3133dedf223748..89a73540c0e3837da96e79d3b990b522300a22e7 100644
--- a/core/java/src/net/i2p/client/naming/DummyNamingService.java
+++ b/core/java/src/net/i2p/client/naming/DummyNamingService.java
@@ -69,10 +69,16 @@ public class DummyNamingService extends NamingService {
         }
 
         // Try Base32 decoding
-        if (hostname.length() == BASE32_HASH_LENGTH + 8 && hostname.toLowerCase(Locale.US).endsWith(".b32.i2p") &&
+        if (hostname.length() >= BASE32_HASH_LENGTH + 8 && hostname.toLowerCase(Locale.US).endsWith(".b32.i2p") &&
                 _context.getBooleanPropertyDefaultTrue(PROP_B32)) {
             try {
-                d = LookupDest.lookupBase32Hash(_context, hostname.substring(0, BASE32_HASH_LENGTH));
+                if (hostname.length() == BASE32_HASH_LENGTH + 8) {
+                    // b32
+                    d = LookupDest.lookupBase32Hash(_context, hostname.substring(0, BASE32_HASH_LENGTH));
+                } else {
+                    // b33
+                    d = LookupDest.lookupHostname(_context, hostname);
+                }
                 if (d != null) {
                     putCache(hostname, d);
                     return d;
diff --git a/core/java/src/net/i2p/client/naming/LookupDest.java b/core/java/src/net/i2p/client/naming/LookupDest.java
index c8e5381c5e35a774274ee876e04abd448cb46eca..ae91e8b6a1593783589a5dbb4c6a3a49de3ff65c 100644
--- a/core/java/src/net/i2p/client/naming/LookupDest.java
+++ b/core/java/src/net/i2p/client/naming/LookupDest.java
@@ -59,10 +59,49 @@ class LookupDest {
     ****/
 
     /** @param h 32 byte hash */
-    static Destination lookupHash(I2PAppContext ctx, byte[] h) throws I2PSessionException {
+    private static Destination lookupHash(I2PAppContext ctx, byte[] h) throws I2PSessionException {
         Hash key = Hash.create(h);
         Destination rv = null;
         I2PClient client = new I2PSimpleClient();
+        Properties opts = getOpts(ctx);
+        I2PSession session = null;
+        try {
+            session = client.createSession(null, opts);
+            session.connect();
+            rv = session.lookupDest(key, DEFAULT_TIMEOUT);
+        } finally {
+            if (session != null)
+                session.destroySession();
+        }
+        return rv;
+    }
+
+    /**
+     * Any hostname, but this is for long-format b32
+     *
+     * @param hostname a "b33" hostname, 64+ chars ending with ".b32.i2p"
+     * @since 0.9.40
+     */
+    static Destination lookupHostname(I2PAppContext ctx, String hostname) throws I2PSessionException {
+        Destination rv = null;
+        I2PClient client = new I2PSimpleClient();
+        Properties opts = getOpts(ctx);
+        I2PSession session = null;
+        try {
+            session = client.createSession(null, opts);
+            session.connect();
+            rv = session.lookupDest(hostname, DEFAULT_TIMEOUT);
+        } finally {
+            if (session != null)
+                session.destroySession();
+        }
+        return rv;
+    }
+
+    /**
+     * @since 0.9.40 split out from above
+     */
+    private static Properties getOpts(I2PAppContext ctx) {
         Properties opts = new Properties();
         if (!ctx.isRouterContext()) {
             String s = ctx.getProperty(I2PClient.PROP_TCP_HOST);
@@ -81,16 +120,7 @@ class LookupDest {
             if (s != null)
                 opts.put(PROP_PW, s);
         }
-        I2PSession session = null;
-        try {
-            session = client.createSession(null, opts);
-            session.connect();
-            rv = session.lookupDest(key, DEFAULT_TIMEOUT);
-        } finally {
-            if (session != null)
-                session.destroySession();
-        }
-        return rv;
+        return opts;
     }
 
     public static void main(String args[]) throws I2PSessionException {
diff --git a/core/java/src/net/i2p/client/naming/MetaNamingService.java b/core/java/src/net/i2p/client/naming/MetaNamingService.java
index 8f603ba1af0d0a29f0e2bd6b231519d059111936..f86894075a60277abb9a8954ecc0b54c6c2a9ad4 100644
--- a/core/java/src/net/i2p/client/naming/MetaNamingService.java
+++ b/core/java/src/net/i2p/client/naming/MetaNamingService.java
@@ -103,7 +103,7 @@ public class MetaNamingService extends DummyNamingService {
         if (d != null)
             return d;
         // Base32 failed?
-        if (hostname.length() == BASE32_HASH_LENGTH + 8 && hostname.toLowerCase(Locale.US).endsWith(".b32.i2p"))
+        if (hostname.length() >= BASE32_HASH_LENGTH + 8 && hostname.toLowerCase(Locale.US).endsWith(".b32.i2p"))
             return null;
 
         for (NamingService ns : _services) { 
diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java
index c18736283373aeef7a78f743c706320394bb5f8f..a0cd6d83880e21d3fcaa685a7d37e923dd72ae07 100644
--- a/core/java/src/net/i2p/crypto/Blinding.java
+++ b/core/java/src/net/i2p/crypto/Blinding.java
@@ -4,11 +4,15 @@ import java.security.GeneralSecurityException;
 import java.text.SimpleDateFormat;
 import java.util.Locale;
 import java.util.TimeZone;
+import java.util.zip.Checksum;
+import java.util.zip.CRC32;
 
 import net.i2p.I2PAppContext;
 import net.i2p.crypto.eddsa.EdDSABlinding;
 import net.i2p.crypto.eddsa.EdDSAPrivateKey;
 import net.i2p.crypto.eddsa.EdDSAPublicKey;
+import net.i2p.data.Base32;
+import net.i2p.data.BlindData;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
@@ -176,13 +180,155 @@ public final class Blinding {
         return new SigningPrivateKey(TYPER, b);
     }
 
+    /**
+     *  What's the default blinded type for a given unblinded type?
+     *
+     *  @return non-null
+     *  @since 0.9.40
+     */
+    public static SigType getDefaultBlindedType(SigType unblindedType) {
+        if (unblindedType == TYPE)
+            return TYPER;
+        return unblindedType;
+    }
+
+    /**
+     *  Decode a new-format b32 address.
+     *  PRELIMINARY - Subject to change - see proposal 149
+     *
+     *  @param address ending with ".b32.i2p"
+     *  @throws IllegalArgumentException on bad inputs
+     *  @throws UnsupportedOperationException unless supported SigTypes
+     *  @since 0.9.40
+     */
+    public static BlindData decode(I2PAppContext ctx, String address) throws RuntimeException {
+        address = address.toLowerCase(Locale.US);
+        if (!address.endsWith(".b32.i2p"))
+            throw new IllegalArgumentException("Not a .b32.i2p address");
+        byte[] b = Base32.decode(address.substring(0, address.length() - 8));
+        if (b == null)
+            throw new IllegalArgumentException("Bad base32 encoding");
+        if (b.length < 35)
+            throw new IllegalArgumentException("Not a new-format address");
+        return decode(ctx, b);
+    }
+
+    /**
+     *  Decode a new-format b32 address.
+     *  PRELIMINARY - Subject to change - see proposal 149
+     *
+     *  @param b 35+ bytes
+     *  @throws IllegalArgumentException on bad inputs
+     *  @throws UnsupportedOperationException unless supported SigTypes
+     *  @since 0.9.40
+     */
+    public static BlindData decode(I2PAppContext ctx, byte[] b) throws RuntimeException {
+        Checksum crc = new CRC32();
+        crc.update(b, 3, b.length - 3);
+        long check = crc.getValue();
+        b[0] ^= (byte) check;
+        b[1] ^= (byte) (check >> 8);
+        b[2] ^= (byte) (check >> 16);
+        int flag = b[0] & 0xff;
+        if ((flag & 0xf8) != 0)
+            throw new IllegalArgumentException("Corrupt b32 or unsupported options");
+        if ((flag & 0x01) != 0)
+            throw new IllegalArgumentException("Two byte sig types unsupported");
+        if ((flag & 0x04) != 0)
+            throw new IllegalArgumentException("Per-client auth unsupported");
+        // TODO two-byte sigtypes
+        int st1 = b[1] & 0xff;
+        int st2 = b[2] & 0xff;
+        SigType sigt1 = SigType.getByCode(st1);
+        SigType sigt2 = SigType.getByCode(st2);
+        if (sigt1 == null)
+            throw new IllegalArgumentException("Unknown sig type " + st1);
+        if (!sigt1.isAvailable())
+            throw new IllegalArgumentException("Unavailable sig type " + sigt1);
+        if (sigt2 == null)
+            throw new IllegalArgumentException("Unknown blinded sig type " + st2);
+        if (!sigt2.isAvailable())
+            throw new IllegalArgumentException("Unavailable blinded sig type " + sigt2);
+        // todo secret/privkey
+        int spkLen = sigt1.getPubkeyLen();
+        if (3 + spkLen > b.length)
+            throw new IllegalArgumentException("b32 too short");
+        byte[] spkData = new byte[spkLen];
+        System.arraycopy(b, 3, spkData, 0, spkLen);
+        SigningPublicKey spk = new SigningPublicKey(sigt1, spkData);
+        String secret;
+        if ((flag & 0x02) != 0) {
+            if (4 + spkLen > b.length)
+                throw new IllegalArgumentException("No secret data");
+            int secLen = b[3 + spkLen] & 0xff;
+            if (4 + spkLen + secLen != b.length)
+                throw new IllegalArgumentException("Bad b32 length");
+            secret = DataHelper.getUTF8(b, 4 + spkLen, secLen);
+        } else if (3 + spkLen != b.length) {
+            throw new IllegalArgumentException("b32 too long");
+        } else {
+            secret = null;
+        }
+        BlindData rv = new BlindData(ctx, spk, sigt2, secret);
+        return rv;
+    }
+
+    /**
+     *  Encode a public key as a new-format b32 address.
+     *  PRELIMINARY - Subject to change - see proposal 149
+     *
+     *  @param secret may be empty or null
+     *  @return (56+ chars).b32.i2p
+     *  @throws IllegalArgumentException on bad inputs
+     *  @throws UnsupportedOperationException unless supported SigTypes
+     *  @since 0.9.40
+     */
+    public static String encode(I2PAppContext ctx, SigningPublicKey key, String secret) throws RuntimeException {
+        SigType type = key.getType();
+        if (type != TYPE && type != TYPER)
+            throw new UnsupportedOperationException();
+        byte sdata[] = (secret != null) ? DataHelper.getUTF8(secret) : null;
+        int slen = (secret != null) ? 1 + sdata.length : 0;
+        if (slen > 256)
+            throw new IllegalArgumentException("secret too long");
+        byte[] d = key.getData();
+        byte[] b = new byte[d.length + slen + 3];
+        System.arraycopy(d, 0, b, 3, d.length);
+        if (slen > 0) {
+            b[3 + d.length] = (byte) sdata.length;
+            System.arraycopy(sdata, 0, b, 4 + d.length, sdata.length);
+        }
+        Checksum crc = new CRC32();
+        crc.update(b, 3, b.length - 3);
+        long check = crc.getValue();
+        // TODO two-byte sigtypes
+        if (slen > 0)
+            b[0] = 0x02;
+        b[1] = (byte) (type.getCode() & 0xff);
+        b[2] = (byte) (TYPER.getCode() & 0xff);
+        b[0] ^= (byte) check;
+        b[1] ^= (byte) (check >> 8);
+        b[2] ^= (byte) (check >> 16);
+        // todo privkey
+        return Base32.encode(b) + ".b32.i2p";
+    }
+
 /******
     public static void main(String args[]) throws Exception {
         net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
         SigningPublicKey pub = (SigningPublicKey) keys[0];
         SigningPrivateKey priv = (SigningPrivateKey) keys[1];
+        I2PAppContext ctx = I2PAppContext.getGlobalContext();
+        //String b32 = encode(ctx, pub, null);
+        String b32 = encode(ctx, pub, "foobarbaz");
+        System.out.println("pub b32 is " + b32);
+        BlindData bd = decode(ctx, b32);
+        if (bd.getBlindedPubKey().equals(pub))
+            System.out.println("B32 test failed");
+        else
+            System.out.println("B32 test passed");
         byte[] b = new byte[64];
-        net.i2p.I2PAppContext.getGlobalContext().random().nextBytes(b);
+        ctx.random().nextBytes(b);
         b = EdDSABlinding.reduce(b);
         SigningPrivateKey alpha = new SigningPrivateKey(TYPER, b);
         SigningPublicKey bpub = null;
diff --git a/core/java/src/net/i2p/data/BlindData.java b/core/java/src/net/i2p/data/BlindData.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d3eed59ce674c4bc2ca1bb81060df237dc4f72a
--- /dev/null
+++ b/core/java/src/net/i2p/data/BlindData.java
@@ -0,0 +1,130 @@
+package net.i2p.data;
+
+import net.i2p.I2PAppContext;
+import net.i2p.crypto.Blinding;
+import net.i2p.crypto.SigType;
+
+/**
+ * Cache data for Blinding EdDSA keys.
+ * PRELIMINARY - Subject to change - see proposal 123
+ *
+ * @since 0.9.40
+ */
+public class BlindData {
+
+    private final I2PAppContext _context;
+    private final SigningPublicKey _clearSPK;
+    private final String _secret;
+    private SigningPublicKey _blindSPK;
+    private final SigType _blindType;
+    private final int _authType;
+    private final PrivateKey _authKey;
+    private Hash _blindHash;
+    private SigningPrivateKey _alpha;
+    private Destination _dest;
+    private long _routingKeyGenMod;
+
+    /**
+     *  @throws IllegalArgumentException on various errors
+     */
+    public BlindData(I2PAppContext ctx, Destination dest, SigType blindType, String secret) {
+        this(ctx, dest.getSigningPublicKey(), blindType, secret);
+        _dest = dest;
+    }
+
+    /**
+     *  @throws IllegalArgumentException on various errors
+     */
+    public BlindData(I2PAppContext ctx, SigningPublicKey spk, SigType blindType, String secret) {
+        _context = ctx;
+        _clearSPK = spk;
+        _blindType = blindType;
+        _secret = secret;
+        _authType = 0;
+        _authKey = null;
+        // defer until needed
+        //calculate();
+    }
+
+    /**
+     *  @return The blinded key for the current day
+     */
+    public synchronized SigningPublicKey getBlindedPubKey() {
+        calculate();
+        return _blindSPK;
+    }
+
+    /**
+     *  @return The hash of the blinded key for the current day
+     */
+    public synchronized Hash getBlindedHash() {
+        calculate();
+        return _blindHash;
+    }
+
+    /**
+     *  @return Alpha for the current day
+     */
+    public synchronized SigningPrivateKey getAlpha() {
+        calculate();
+        return _alpha;
+    }
+
+    /**
+     *  @return null if unknown
+     */
+    public synchronized Destination getDestination() {
+        return _dest;
+    }
+
+    /**
+     *  @throws IllegalArgumentException on SigningPublicKey mismatch
+     */
+    public synchronized void setDestination(Destination d) {
+        if (_dest != null) {
+            if (!_dest.equals(d))
+                throw new IllegalArgumentException("Dest mismatch");
+            return;
+        }
+        if (!d.getSigningPublicKey().equals(_clearSPK))
+            throw new IllegalArgumentException("Dest mismatch");
+        _dest = d;
+    }
+
+    /**
+     *  @return null if none
+     */
+    public String getSecret() {
+        return _secret;
+    }
+
+    /**
+     *  @return 0 for no client auth
+     */
+    public int getAuthType() {
+        return _authType;
+    }
+
+    private synchronized void calculate() {
+        if (_context.isRouterContext()) {
+            RoutingKeyGenerator gen = _context.routingKeyGenerator();
+            long mod = gen.getLastChanged();
+            if (mod == _routingKeyGenMod)
+                return;
+            _routingKeyGenMod = mod;
+        }
+        // For now, always calculate in app context,
+        // where we don't have a routingKeyGenerator
+        // TODO we could cache based on current day
+        _alpha = Blinding.generateAlpha(_context, _clearSPK, _secret);
+        _blindSPK = Blinding.blind(_clearSPK, _alpha);
+        SigType bsigt2 = _blindSPK.getType();
+        if (_blindType != bsigt2) {
+            throw new IllegalArgumentException("Requested blinded sig type " + _blindType + " supported type " + bsigt2);
+        }
+        byte[] hashData = new byte[2 + Hash.HASH_LENGTH];
+        DataHelper.toLong(hashData, 0, 2, _blindType.getCode());
+        System.arraycopy(_blindSPK.getData(), 0, hashData, 2, _blindSPK.length());
+        _blindHash = _context.sha().calculateHash(hashData);
+    }
+}
diff --git a/core/java/src/net/i2p/data/PrivateKeyFile.java b/core/java/src/net/i2p/data/PrivateKeyFile.java
index db064bfc21aa16b7f3b9d4afd8d305fa3cf88069..96e73b3d18f31bb7adc78bf7840011cb86eeed48 100644
--- a/core/java/src/net/i2p/data/PrivateKeyFile.java
+++ b/core/java/src/net/i2p/data/PrivateKeyFile.java
@@ -20,12 +20,14 @@ import com.nettgryppa.security.HashCash;
 
 import gnu.getopt.Getopt;
 
+import net.i2p.I2PAppContext;
 import net.i2p.I2PException;
 import net.i2p.client.I2PClient;
 import net.i2p.client.I2PClientFactory;
 import net.i2p.client.I2PSession;
 import net.i2p.client.I2PSessionException;
 import net.i2p.client.naming.HostTxtEntry;
+import net.i2p.crypto.Blinding;
 import net.i2p.crypto.DSAEngine;
 import net.i2p.crypto.KeyGenerator;
 import net.i2p.crypto.SigType;
@@ -755,6 +757,15 @@ public class PrivateKeyFile {
         s.append(this.dest != null ? this.dest.toBase64() : "null");
         s.append("\nB32: ");
         s.append(this.dest != null ? this.dest.toBase32() : "null");
+        if (dest != null) {
+            SigningPublicKey spk = dest.getSigningPublicKey();
+            SigType type = spk.getType();
+            if (type == SigType.EdDSA_SHA512_Ed25519 ||
+                type == SigType.RedDSA_SHA512_Ed25519) {
+                I2PAppContext ctx = I2PAppContext.getGlobalContext();
+                s.append("\nBlinded B32: ").append(Blinding.encode(ctx, spk, null));
+            }
+        }
         s.append("\nContains: ");
         s.append(this.dest);
         s.append("\nPrivate Key: ");
diff --git a/router/java/src/net/i2p/router/client/LookupDestJob.java b/router/java/src/net/i2p/router/client/LookupDestJob.java
index 02212dc7e167a5b482a401327f5879be93526a90..b6a1cd939ffbc5cf560ed38d1a635baff3ba1e83 100644
--- a/router/java/src/net/i2p/router/client/LookupDestJob.java
+++ b/router/java/src/net/i2p/router/client/LookupDestJob.java
@@ -6,7 +6,9 @@ package net.i2p.router.client;
 
 import java.util.Locale;
 
+import net.i2p.crypto.Blinding;
 import net.i2p.data.Base32;
+import net.i2p.data.BlindData;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.data.LeaseSet;
@@ -17,12 +19,14 @@ import net.i2p.data.i2cp.I2CPMessageException;
 import net.i2p.data.i2cp.SessionId;
 import net.i2p.router.JobImpl;
 import net.i2p.router.RouterContext;
+import net.i2p.util.Log;
 
 /**
  * Look up the lease of a hash, to convert it to a Destination for the client.
  * Or, since 0.9.11, lookup a host name in the naming service.
  */
 class LookupDestJob extends JobImpl {
+    private final Log _log;
     private final ClientConnectionRunner _runner;
     private final long _reqID;
     private final long _timeout;
@@ -52,24 +56,44 @@ class LookupDestJob extends JobImpl {
                          long reqID, long timeout, SessionId sessID, Hash h, String name,
                          Hash fromLocalDest) {
         super(context);
+        _log = context.logManager().getLog(LookupDestJob.class);
         if ((h == null && name == null) ||
             (h != null && name != null) ||
             (reqID >= 0 && sessID == null) ||
-            (reqID < 0 && name != null))
+            (reqID < 0 && name != null)) {
+            _log.warn("bad args");
             throw new IllegalArgumentException();
+        }
         _runner = runner;
         _reqID = reqID;
         _timeout = timeout;
         _sessID = sessID;
         _fromLocalDest = fromLocalDest;
-        if (name != null && name.length() == 60) {
+        if (name != null && name.length() >= 60) {
             // convert a b32 lookup to a hash lookup
             String nlc = name.toLowerCase(Locale.US);
             if (nlc.endsWith(".b32.i2p")) {
-                byte[] b = Base32.decode(nlc.substring(0, 52));
-                if (b != null && b.length == Hash.HASH_LENGTH) {
-                    h = Hash.create(b);
-                    name = null;
+                byte[] b = Base32.decode(nlc.substring(0, nlc.length() - 8));
+                if (b != null) {
+                    if (b.length == Hash.HASH_LENGTH) {
+                        h = Hash.create(b);
+                        if (_log.shouldDebug())
+                            _log.debug("Converting name lookup " + name + " to " + h);
+                        name = null;
+                    } else if (b.length >= 35) {
+                        // encrypted LS2
+                        try {
+                            BlindData bd = Blinding.decode(context, b);
+                            h = bd.getBlindedHash();
+                            if (_log.shouldDebug())
+                                _log.debug("Converting name lookup " + name + " to blinded " + h);
+                            name = null;
+                        } catch (RuntimeException re) {
+                            if (_log.shouldWarn())
+                                _log.debug("Failed blinding conversion of " + name, re);
+                            // lookup as a name, which will probably fail
+                        }
+                    }
                 }
             }
         }
@@ -86,10 +110,15 @@ class LookupDestJob extends JobImpl {
         if (_name != null) {
             // inline, ignore timeout
             Destination d = getContext().namingService().lookup(_name);
-            if (d != null)
+            if (d != null) {
+                if (_log.shouldDebug())
+                    _log.debug("Found name lookup " + _name + " to " + d);
                 returnDest(d);
-            else
+            } else {
+                if (_log.shouldDebug())
+                    _log.debug("Failed name lookup " + _name);
                 returnFail();
+            }
         } else {
             DoneJob done = new DoneJob(getContext());
             getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
@@ -103,10 +132,15 @@ class LookupDestJob extends JobImpl {
         public String getName() { return "LeaseSet Lookup Reply to Client"; }
         public void runJob() {
             Destination dest = getContext().netDb().lookupDestinationLocally(_hash);
-            if (dest != null)
+            if (dest != null) {
+                if (_log.shouldDebug())
+                    _log.debug("Found hash lookup " + _hash + " to " + dest);
                 returnDest(dest);
-            else
+            } else {
+                if (_log.shouldDebug())
+                    _log.debug("Failed hash lookup " + _hash);
                 returnFail();
+            }
         }
     }