diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp
index 4267a8e747ce2be9f71d35cdc50e8eac0117a618..fd38edc7ed876e0eb8fd087b8f187a696abdaa73 100644
--- a/tools/dhtnode.cpp
+++ b/tools/dhtnode.cpp
@@ -44,6 +44,18 @@ void print_usage() {
     std::cout << "Report bugs to: http://opendht.net" << std::endl;
 }
 
+void print_id_req() {
+    std::cout << "An identity is required to perform this operation (run with -i)" << std::cout;
+}
+
+void print_node_info(const DhtRunner& dht, const dht_params& params) {
+    std::cout << "OpenDht node " << dht.getNodeId() << " running on port " <<  params.port << std::endl;
+    if (params.is_bootstrap_node)
+        std::cout << "Running in bootstrap mode (discouraged)." << std::endl;
+    if (params.generate_identity)
+        std::cout << "Public key ID " << dht.getId() << std::endl;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -58,17 +70,21 @@ main(int argc, char **argv)
     if (rc != GNUTLS_E_SUCCESS)
         throw std::runtime_error(std::string("Error initializing GnuTLS: ")+gnutls_strerror(rc));
 
-    auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
-    auto crt_tmp = dht::crypto::generateIdentity("DHT Node", ca_tmp);
+    dht::crypto::Identity crt {};
+    if (params.generate_identity) {
+        auto ca_tmp = dht::crypto::generateIdentity("DHT Node CA");
+        crt = dht::crypto::generateIdentity("DHT Node", ca_tmp);
+    }
 
     DhtRunner dht;
-    dht.run(params.port, crt_tmp, true, [](dht::Dht::Status /* ipv4 */, dht::Dht::Status /* ipv6 */) {});
+    dht.run(params.port, crt, true, params.is_bootstrap_node);
 
-    if (not params.bootstrap.empty())
+    if (not params.bootstrap.empty()) {
+        std::cout << "Bootstrap: " << params.bootstrap[0] << ":" << params.bootstrap[1] << std::endl;
         dht.bootstrap(params.bootstrap[0].c_str(), params.bootstrap[1].c_str());
+    }
 
-    std::cout << "OpenDht node " << dht.getNodeId() << " running on port " <<  params.port << std::endl;
-    std::cout << "Public key ID " << dht.getId() << std::endl;
+    print_node_info(dht, params);
     std::cout << " (type 'h' or 'help' for a list of possible commands)" << std::endl << std::endl;
 
     bool do_log {false};
@@ -105,12 +121,11 @@ main(int argc, char **argv)
             std::cout << std::endl;
             continue;
         } else if (op == "ll") {
+            print_node_info(dht, params);
             unsigned good4, dubious4, cached4, incoming4;
             unsigned good6, dubious6, cached6, incoming6;
             dht.getNodesStats(AF_INET, &good4, &dubious4, &cached4, &incoming4);
             dht.getNodesStats(AF_INET6, &good6, &dubious6, &cached6, &incoming6);
-            std::cout << "OpenDht node " << dht.getNodeId() << " running on port " <<  params.port << std::endl;
-            std::cout << "Public key ID " << dht.getId() << std::endl;
             std::cout << "IPv4 nodes : " << good4 << " good, " << dubious4 << " dubious, " << incoming4 << " incoming." << std::endl;
             std::cout << "IPv6 nodes : " << good6 << " good, " << dubious6 << " dubious, " << incoming6 << " incoming." << std::endl;
             continue;
@@ -184,6 +199,10 @@ main(int argc, char **argv)
             });
         }
         else if (op == "s") {
+            if (not params.generate_identity) {
+                print_id_req();
+                continue;
+            }
             std::string v;
             iss >> v;
             dht.putSigned(id, dht::Value {
@@ -195,6 +214,10 @@ main(int argc, char **argv)
             });
         }
         else if (op == "e") {
+            if (not params.generate_identity) {
+                print_id_req();
+                continue;
+            }
             std::string tostr;
             std::string v;
             iss >> tostr >> v;
diff --git a/tools/tools_common.h b/tools/tools_common.h
index 7eb4e0326722d530015aa03759faa80af287a533..83cb8b8e0ede2b3c1a2f2eeb885f3f514348a3df 100644
--- a/tools/tools_common.h
+++ b/tools/tools_common.h
@@ -130,6 +130,7 @@ struct dht_params {
     bool help {false}; // print help and exit
     in_port_t port {DHT_DEFAULT_PORT};
     bool is_bootstrap_node {false};
+    bool generate_identity {false};
     std::vector<std::string> bootstrap {};
 };
 
@@ -137,6 +138,7 @@ static const struct option long_options[] = {
    {"help",       no_argument,       nullptr, 'h'},
    {"port",       required_argument, nullptr, 'p'},
    {"bootstrap",  optional_argument, nullptr, 'b'},
+   {"identity",   no_argument      , nullptr, 'i'},
    {nullptr,      0,                 nullptr,  0}
 };
 
@@ -144,7 +146,7 @@ dht_params
 parseArgs(int argc, char **argv) {
     dht_params params;
     int opt;
-    while ((opt = getopt_long(argc, argv, "hp:b:", long_options, nullptr)) != -1) {
+    while ((opt = getopt_long(argc, argv, ":hip:b:", long_options, nullptr)) != -1) {
         switch (opt) {
         case 'p': {
                 int port_arg = atoi(optarg);
@@ -166,6 +168,19 @@ parseArgs(int argc, char **argv) {
         case 'h':
             params.help = true;
             break;
+        case 'i':
+            params.generate_identity = true;
+            break;
+        case ':':
+            switch (optopt) {
+            case 'b':
+                params.is_bootstrap_node = true;
+                break;
+            default:
+                std::cout << "option requires an argument -- '" << optopt << '\'' << std::endl;
+                break;
+            }
+            break;
         default:
             break;
         }