diff --git a/core/include/dap_common.h b/core/include/dap_common.h
index ba771572db8448889b35da6b52efe4a6b1741542..d150a149b88376779f36a61c49c74b95150393b8 100755
--- a/core/include/dap_common.h
+++ b/core/include/dap_common.h
@@ -1142,14 +1142,8 @@ int exec_silent(const char *a_cmd);
 }
 #endif
 
-DAP_STATIC_INLINE int dap_stream_node_addr_from_str(dap_stream_node_addr_t *a_addr, const char *a_addr_str)
-{
-    if (!a_addr || !a_addr_str)
-        return -2;
-    return sscanf(a_addr_str, NODE_ADDR_FP_STR, NODE_ADDR_FPS_ARGS(a_addr)) == 4
-        || sscanf(a_addr_str, "0x%016" DAP_UINT64_FORMAT_x, (uint64_t*)a_addr) == 1
-        ? 0 : -1;
-}
+int dap_stream_node_addr_from_str(dap_stream_node_addr_t *a_addr, const char *a_addr_str);
+
 
 DAP_STATIC_INLINE bool dap_stream_node_addr_is_blank(dap_stream_node_addr_t *a_addr) { return !a_addr->uint64; }
 
diff --git a/core/src/dap_common.c b/core/src/dap_common.c
index b1cd1d83c354b9fd7c79fa0c9fd8c5822d183aee..aa96fa41a75d15a4c44926b7d2a0e11a760b4eec 100755
--- a/core/src/dap_common.c
+++ b/core/src/dap_common.c
@@ -1555,6 +1555,22 @@ dap_node_addr_str_t dap_stream_node_addr_to_str_static_(dap_stream_node_addr_t a
     snprintf((char*)&l_ret, sizeof(l_ret), NODE_ADDR_FP_STR, NODE_ADDR_FP_ARGS_S(a_address));
     return l_ret;
 }
+
+int dap_stream_node_addr_from_str(dap_stream_node_addr_t *a_addr, const char *a_addr_str)
+{
+    if (!a_addr || !a_addr_str)
+        return -2;
+    bool l_res = true;
+    size_t l_len = 0;
+    for (; l_res && *(a_addr_str + l_len) && l_len < 23; l_len++) {
+        l_res &= *(a_addr_str + l_len) == ':' || isxdigit(*(a_addr_str + l_len));
+    }
+    l_res &= l_len == 18 || l_len == 22;
+    return l_res ? (sscanf(a_addr_str, NODE_ADDR_FP_STR, NODE_ADDR_FPS_ARGS(a_addr)) == 4
+        || sscanf(a_addr_str, "0x%016" DAP_UINT64_FORMAT_x, (uint64_t*)a_addr) == 1
+        ? 0 : -1) : -4;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/net/server/cli_server/dap_cli_server.c b/net/server/cli_server/dap_cli_server.c
index fa0cc797df85bb276c6c289ac84683733de9847c..6d61a47d8bf88233dc5ecaafd78b2b88c74ba925 100644
--- a/net/server/cli_server/dap_cli_server.c
+++ b/net/server/cli_server/dap_cli_server.c
@@ -54,11 +54,14 @@
 
 static dap_server_t *s_cli_server = NULL;
 static bool s_debug_cli = false;
+static atomic_int_fast32_t s_cmd_thread_count = 0;
+static bool s_allowed_cmd_control = false;
+static const char **s_allowed_cmd_array = NULL;
 
 static dap_cli_cmd_t *cli_commands = NULL;
 static dap_cli_cmd_aliases_t *s_command_alias = NULL;
 
-static inline dap_cli_cmd_t *s_cmd_add_ex(const char *a_name, dap_cli_server_cmd_callback_ex_t a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex);
+static dap_cli_server_cmd_stat_callback_t s_stat_callback = NULL;
 
 typedef struct cli_cmd_arg {
     dap_worker_t *worker;
@@ -69,7 +72,9 @@ typedef struct cli_cmd_arg {
 
 static void* s_cli_cmd_exec(void *a_arg);
 
-static bool s_allowed_cmd_check(char *a_buf) {
+static bool s_allowed_cmd_check(const char *a_buf) {
+    if (!s_allowed_cmd_array)
+        return false;
     enum json_tokener_error jterr;
     const char *l_method;
     json_object *jobj = json_tokener_parse_verbose(a_buf, &jterr),
@@ -84,7 +89,7 @@ static bool s_allowed_cmd_check(char *a_buf) {
         return false;
     }
 
-    bool l_allowed = !!dap_str_find( dap_config_get_array_str(g_config, "cli-server", "allowed_cmd", NULL), l_method );
+    bool l_allowed = !!dap_str_find( s_allowed_cmd_array, l_method );
     return debug_if(!l_allowed, L_ERROR, "Command %s is restricted", l_method), json_object_put(jobj), l_allowed;
 }
 
@@ -119,11 +124,14 @@ DAP_STATIC_INLINE void s_cli_cmd_schedule(dap_events_socket_t *a_es, void *a_arg
         if ( a_es->buf_in_size < l_arg->buf_size + l_hdr_len )
             return;
 
-        if ( ((struct sockaddr_in*)&a_es->addr_storage)->sin_addr.s_addr != htonl(INADDR_LOOPBACK)
+        if (!(   
 #ifdef DAP_OS_UNIX
-            && a_es->addr_storage.ss_family != AF_UNIX
+            a_es->addr_storage.ss_family == AF_UNIX ||
 #endif
-            && !s_allowed_cmd_check(l_arg->buf) ) {
+            ( ((struct sockaddr_in*)&a_es->addr_storage)->sin_addr.s_addr == htonl(INADDR_LOOPBACK) && !s_allowed_cmd_control) ||
+
+            (((struct sockaddr_in*)&a_es->addr_storage)->sin_addr.s_addr && s_allowed_cmd_control && s_allowed_cmd_check(l_arg->buf)))
+        ) {
                 dap_events_socket_write_f_unsafe(a_es, "HTTP/1.1 403 Forbidden\r\n");
                 a_es->flags |= DAP_SOCK_SIGNAL_CLOSE;
                 return;
@@ -184,18 +192,6 @@ void dap_cli_server_deinit()
     dap_server_delete(s_cli_server);
 }
 
-/**
- * @brief dap_cli_server_cmd_add
- * @param a_name
- * @param a_func
- * @param a_doc
- * @param a_doc_ex
- */
-dap_cli_cmd_t *dap_cli_server_cmd_add(const char * a_name, dap_cli_server_cmd_callback_t a_func, const char *a_doc, const char *a_doc_ex)
-{
-    return s_cmd_add_ex(a_name, (dap_cli_server_cmd_callback_ex_t)(void *)a_func, NULL, a_doc, a_doc_ex);
-}
-
 /**
  * @brief s_cmd_add_ex
  * @param a_name
@@ -204,7 +200,7 @@ dap_cli_cmd_t *dap_cli_server_cmd_add(const char * a_name, dap_cli_server_cmd_ca
  * @param a_doc
  * @param a_doc_ex
  */
-static inline dap_cli_cmd_t *s_cmd_add_ex(const char * a_name, dap_cli_server_cmd_callback_ex_t a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex)
+DAP_STATIC_INLINE dap_cli_cmd_t *s_cmd_add_ex(const char * a_name, dap_cli_server_cmd_callback_ex_t a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex, int16_t a_id)
 {
     dap_cli_cmd_t *l_cmd_item = DAP_NEW_Z(dap_cli_cmd_t);
     if (!l_cmd_item) {
@@ -220,11 +216,24 @@ static inline dap_cli_cmd_t *s_cmd_add_ex(const char * a_name, dap_cli_server_cm
     } else {
         l_cmd_item->func = (dap_cli_server_cmd_callback_t )(void *)a_func;
     }
+    l_cmd_item->id = a_id;
     HASH_ADD_STR(cli_commands,name,l_cmd_item);
     log_it(L_DEBUG,"Added command %s",l_cmd_item->name);
     return l_cmd_item;
 }
 
+/**
+ * @brief dap_cli_server_cmd_add
+ * @param a_name
+ * @param a_func
+ * @param a_doc
+ * @param a_doc_ex
+ */
+dap_cli_cmd_t *dap_cli_server_cmd_add(const char * a_name, dap_cli_server_cmd_callback_t a_func, const char *a_doc, int16_t a_id, const char *a_doc_ex)
+{
+    return s_cmd_add_ex(a_name, (dap_cli_server_cmd_callback_ex_t)(void *)a_func, NULL, a_doc, a_doc_ex, a_id);
+}
+
 int json_commands(const char * a_name) {
     static const char* long_cmd[] = {
             "tx_history",
@@ -240,7 +249,6 @@ int json_commands(const char * a_name) {
             "chain_ca_copy",
             "dag",
             "block",
-            "dag",
             "token",
             "esbocs",
             "global_db",
@@ -429,6 +437,7 @@ dap_cli_cmd_t *dap_cli_server_cmd_find_by_alias(const char *a_alias, char **a_ap
 }
 
 static void *s_cli_cmd_exec(void *a_arg) {
+    atomic_fetch_add(&s_cmd_thread_count, 1);
     cli_cmd_arg_t *l_arg = (cli_cmd_arg_t*)a_arg;
     char    *l_ret = dap_cli_cmd_exec(l_arg->buf),
             *l_full_ret = dap_strdup_printf("HTTP/1.1 200 OK\r\n"
@@ -438,6 +447,7 @@ static void *s_cli_cmd_exec(void *a_arg) {
     dap_events_socket_write_mt(l_arg->worker, l_arg->es_uid, l_full_ret, dap_strlen(l_full_ret));
     // TODO: pagination
     DAP_DEL_MULTY(l_arg->buf, /* l_full_ret, */ l_arg);
+    atomic_fetch_sub(&s_cmd_thread_count, 1);
     return NULL;
 }
 
@@ -505,6 +515,10 @@ char *dap_cli_cmd_exec(char *a_req_str) {
         }
         // Call the command function
         if(l_cmd &&  l_argv && l_cmd->func) {
+            dap_time_t l_call_time = 0;
+            if (s_stat_callback) {
+                l_call_time = dap_nanotime_now();
+            }
             if (json_commands(cmd_name)) {
                 res = l_cmd->func(l_argc, l_argv, (void *)&l_json_arr_reply);
             } else if (l_cmd->arg_func) {
@@ -512,6 +526,9 @@ char *dap_cli_cmd_exec(char *a_req_str) {
             } else {
                 res = l_cmd->func(l_argc, l_argv, (void *)&str_reply);
             }
+            if (s_stat_callback) {
+                s_stat_callback(l_cmd->id, (dap_nanotime_now() - l_call_time) / 1000000);
+            }
         } else if (l_cmd) {
             log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd);
             dap_json_rpc_error_add(l_json_arr_reply, -1, "NULL arguments for input for command \"%s\"", str_cmd);
@@ -550,3 +567,27 @@ char *dap_cli_cmd_exec(char *a_req_str) {
     dap_json_rpc_request_free(request);
     return response_string;
 }
+
+DAP_INLINE int32_t dap_cli_get_cmd_thread_count()
+{
+    return atomic_load(&s_cmd_thread_count);
+}
+
+/**
+ * @brief dap_cli_server_cmd_add
+ * @param a_callback callback to statistic collect
+ */
+void dap_cli_server_statistic_callback_add(dap_cli_server_cmd_stat_callback_t a_callback)
+{
+    if (a_callback && s_stat_callback)
+        log_it(L_ERROR, "Dap cli server statistic callback already added");
+    else
+        s_stat_callback = a_callback;
+}
+
+DAP_INLINE void dap_cli_server_set_allowed_cmd_check(const char **a_cmd_array)
+{
+    dap_return_if_pass_err(s_allowed_cmd_array, "Allowed cmd array already exist");
+    s_allowed_cmd_array = a_cmd_array;
+    s_allowed_cmd_control = true;
+}
\ No newline at end of file
diff --git a/net/server/cli_server/include/dap_cli_server.h b/net/server/cli_server/include/dap_cli_server.h
index aecdd2d8196fbe0f100244390faf56205643fb5f..e9e689ead9230025a594041db47f7aec413742ff 100644
--- a/net/server/cli_server/include/dap_cli_server.h
+++ b/net/server/cli_server/include/dap_cli_server.h
@@ -32,6 +32,7 @@
 
 typedef int (*dap_cli_server_cmd_callback_ex_t)(int argc, char ** argv, void *arg_func, void **a_str_reply);
 typedef int (*dap_cli_server_cmd_callback_t)(int argc, char ** argv, void **a_str_reply);
+typedef void (*dap_cli_server_cmd_stat_callback_t)(int16_t a_cmd_num, int64_t a_call_time);  // use to statistic collect
 
 typedef void (*dap_cli_server_override_log_cmd_callback_t)(const char*);
 
@@ -50,6 +51,7 @@ typedef struct dap_cli_cmd{
     char *doc; /* Documentation for this function.  */
     char *doc_ex; /* Full documentation for this function.  */
     dap_cli_server_cmd_override_t overrides; /* Used to change default behaviour */
+    int16_t id;
     UT_hash_handle hh;
 } dap_cli_cmd_t;
 
@@ -64,7 +66,7 @@ typedef struct dap_cli_cmd_aliases{
 int dap_cli_server_init(bool a_debug_more, const char *a_cfg_section);
 void dap_cli_server_deinit();
 
-dap_cli_cmd_t *dap_cli_server_cmd_add(const char * a_name, dap_cli_server_cmd_callback_t a_func, const char *a_doc, const char *a_doc_ex);
+dap_cli_cmd_t *dap_cli_server_cmd_add(const char * a_name, dap_cli_server_cmd_callback_t a_func, const char *a_doc, int16_t a_id, const char *a_doc_ex);
 DAP_PRINTF_ATTR(2, 3) void dap_cli_server_cmd_set_reply_text(void **a_str_reply, const char *str, ...);
 int dap_cli_server_cmd_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value);
 int dap_cli_server_cmd_check_option( char** argv, int arg_start, int arg_end, const char *opt_name);
@@ -75,7 +77,10 @@ dap_cli_cmd_t* dap_cli_server_cmd_find(const char *a_name);
 
 dap_cli_cmd_aliases_t *dap_cli_server_alias_add(dap_cli_cmd_t *a_cmd, const char *a_pre_cmd, const char *a_alias);
 dap_cli_cmd_t *dap_cli_server_cmd_find_by_alias(const char *a_cli, char **a_append, char **a_ncmd);
+int32_t dap_cli_get_cmd_thread_count();
 
 //for json
 int json_commands(const char * a_name);
 char *dap_cli_cmd_exec(char *a_req_str);
+void dap_cli_server_statistic_callback_add(dap_cli_server_cmd_stat_callback_t a_callback);
+void dap_cli_server_set_allowed_cmd_check(const char **a_cmd_array);
\ No newline at end of file
diff --git a/net/server/json_rpc/rpc_core/src/dap_json_rpc_response.c b/net/server/json_rpc/rpc_core/src/dap_json_rpc_response.c
index d5ea2f6b3269e7003d9426c660a2a99ccb15b088..ab111fdacccace0188fa1f53112e01dbc3bde7a4 100644
--- a/net/server/json_rpc/rpc_core/src/dap_json_rpc_response.c
+++ b/net/server/json_rpc/rpc_core/src/dap_json_rpc_response.c
@@ -212,7 +212,7 @@ void json_print_value(json_object *obj, const char *key, int indent_level, bool
             printf(print_separator ? "%s, " : "%s", json_object_get_string(obj));
             break;
         case json_type_int:
-            printf("%lld", json_object_get_int64(obj));
+            printf("%ld", json_object_get_int64(obj));
             break;
         case json_type_double:
             printf("%lf", json_object_get_double(obj));
diff --git a/net/stream/stream/dap_stream.c b/net/stream/stream/dap_stream.c
index 23f7ab17615ca89a27bad94bd8708e7759f3060c..2098daf9f554e423d5d312e9b348a95bdbaccd63 100644
--- a/net/stream/stream/dap_stream.c
+++ b/net/stream/stream/dap_stream.c
@@ -77,6 +77,7 @@ static dap_cluster_t        *s_global_links_cluster = NULL;
 static pthread_rwlock_t     s_streams_lock = PTHREAD_RWLOCK_INITIALIZER;    // Lock for all tables and list under
 static dap_stream_t         *s_authorized_streams = NULL;                   // Authorized streams hashtable by addr
 static dap_stream_t         *s_streams = NULL;                              // Double-linked list
+static int32_t              s_streams_count = 0;                            // Stream count
 static dap_enc_key_type_t   s_stream_get_preferred_encryption_type = DAP_ENC_KEY_TYPE_IAES;
 
 static int s_add_stream_info(authorized_stream_t **a_hash_table, authorized_stream_t *a_item, dap_stream_t *a_stream);
@@ -966,8 +967,10 @@ void s_stream_delete_from_list(dap_stream_t *a_stream)
         return log_it(L_CRITICAL, "! Attempt to aquire streams lock recursively !");
 
     dap_stream_t *l_stream = NULL;
-    if (a_stream->prev)
+    if (a_stream->prev) {
         DL_DELETE(s_streams, a_stream);
+        --s_streams_count;
+    }
     if (a_stream->authorized) {
         // It's an authorized stream, try to replace it in hastable
         if (a_stream->primary)
@@ -995,6 +998,7 @@ int dap_stream_add_to_list(dap_stream_t *a_stream)
     if ( lock == EDEADLK )
         return log_it(L_CRITICAL, "! Attempt to aquire streams lock recursively !"), -666;
     DL_APPEND(s_streams, a_stream);
+    ++s_streams_count;
     if (a_stream->authorized)
         l_ret = s_stream_add_to_hashtable(a_stream);
     pthread_rwlock_unlock(&s_streams_lock);
@@ -1158,6 +1162,12 @@ dap_stream_info_t *dap_stream_get_links_info(dap_cluster_t *a_cluster, size_t *a
     return l_ret;
 }
 
+
+DAP_INLINE int32_t dap_stream_get_links_count()
+{
+    return s_streams_count;
+} 
+
 void dap_stream_delete_links_info(dap_stream_info_t *a_info, size_t a_count)
 {
     dap_return_if_fail(a_info && a_count);
diff --git a/net/stream/stream/include/dap_stream.h b/net/stream/stream/include/dap_stream.h
index 416e0e5f39bfdd8ba09516e038080501a1bb7cf9..cb84c0750a14b017427c7fc03669399439c0c0f3 100644
--- a/net/stream/stream/include/dap_stream.h
+++ b/net/stream/stream/include/dap_stream.h
@@ -160,3 +160,4 @@ dap_stream_node_addr_t dap_stream_node_addr_from_cert(dap_cert_t *a_cert);
 dap_stream_node_addr_t dap_stream_node_addr_from_pkey(dap_pkey_t *a_pkey);
 dap_stream_info_t *dap_stream_get_links_info(dap_cluster_t *a_cluster, size_t *a_count);
 void dap_stream_delete_links_info(dap_stream_info_t *a_info, size_t a_count);
+int32_t dap_stream_get_links_count();
diff --git a/plugin/src/dap_plugin_command.c b/plugin/src/dap_plugin_command.c
index c7d036612f54b225ee2f14a128082aa90a61a4d2..cf0fec07f3ee091b313a9dc5b1ded3741ec186d8 100644
--- a/plugin/src/dap_plugin_command.c
+++ b/plugin/src/dap_plugin_command.c
@@ -22,7 +22,7 @@ void dap_plugin_command_init(void)
 {
     if (!s_l_restart_plugins){
         dap_cli_server_cmd_add("plugin", s_command_handler,
-                                           "Commands for working with plugins:\n",
+                                           "Commands for working with plugins:\n", -1,
                                            "plugin list\n"
                                            "\tShow plugins list\n"
                                            "plugin show <plugin name>\n"