diff --git a/CMakeLists.txt b/CMakeLists.txt
index 39bbfb4f62aee622d46e5f617bbd433c931c6b95..ea07339f7e29b13c647a12566f106a2e66988dae 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,6 +7,8 @@ set(DAP_CHAIN_NET_SRCS
         dap_chain_node_ctl.c
         dap_chain_node_cli.c
         dap_chain_node_cli_cmd.c
+        dap_chain_node_cli_connect.c
+        dap_chain_node_remote.c
         )
 
 set(DAP_CHAIN_NET_HEADERS
@@ -15,6 +17,8 @@ set(DAP_CHAIN_NET_HEADERS
         dap_chain_node_ctl.h
         dap_chain_node_cli.h
         dap_chain_node_cli_cmd.h
+        dap_chain_node_cli_connect.h
+        dap_chain_node_remote.h
     )
 
 set(IPUTILS_INCLUDE_DIRS
@@ -29,7 +33,7 @@ add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_NET_SRCS} ${DAP_CHAIN_NET_HEADERS
 find_package(PkgConfig REQUIRED)
 pkg_search_module(GLIB REQUIRED glib-2.0)
 
-target_link_libraries(dap_chain_net dap_core dap_crypto dap_chain dap_chain_crypto dap_chain_global_db -lresolv ${GLIB_LDFLAGS})
+target_link_libraries(dap_chain_net dap_core dap_crypto dap_chain dap_chain_crypto dap_chain_mempool dap_chain_global_db -lresolv ${GLIB_LDFLAGS})
 target_include_directories(dap_chain_net INTERFACE . PUBLIC ${GLIB_INCLUDE_DIRS} ${IPUTILS_INCLUDE_DIRS})
 
 set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE)
diff --git a/dap_chain_node.c b/dap_chain_node.c
index efea2e6730ca133fdb66d0c82851136f9e0f6e9d..d2c09b125a4dd44c23483a308682b2ce2e304473 100644
--- a/dap_chain_node.c
+++ b/dap_chain_node.c
@@ -155,12 +155,12 @@ uint8_t* dap_chain_node_info_serialize(dap_chain_node_info_t *node_info, size_t
  */
 dap_chain_node_info_t* dap_chain_node_info_deserialize(uint8_t *node_info_str, size_t size)
 {
-    if(!node_info_str)
+    if(!node_info_str || size<=0)
         return NULL;
     dap_chain_node_info_t *node_info = DAP_NEW_Z_SIZE(dap_chain_node_info_t, (size / 2 + 1));
     if(hex2bin((char*) node_info, (const unsigned char *) node_info_str, size) == -1 ||
             (size / 2) != dap_chain_node_info_get_size(node_info)) {
-        log_it(L_ERROR, "node_info_deserialize - bad node_info size (%ld!=%ld)",
+        log_it(L_ERROR, "node_info_deserialize - incorrect node_info size (%ld!=%ld)",
                 size / 2, dap_chain_node_info_get_size(node_info));
         DAP_DELETE(node_info);
         return NULL;
diff --git a/dap_chain_node_cli.c b/dap_chain_node_cli.c
index 9744539cb86cb9b1f32d983a4d0f36f365e07da5..9e29499c40c368e13f14a0c8f6d27fb919e6451c 100644
--- a/dap_chain_node_cli.c
+++ b/dap_chain_node_cli.c
@@ -49,6 +49,7 @@ typedef int SOCKET;
 #include "iputils/iputils.h"
 #include "dap_common.h"
 #include "dap_chain_node_cli_cmd.h"
+#include "dap_chain_node_cli_connect.h"
 #include "dap_chain_node_cli.h"
 
 //#include "dap_chain_node_cli.h"
@@ -97,13 +98,13 @@ static int s_poll(int socket, int timeout)
     struct pollfd fds;
     int res;
     fds.fd = socket;
-// POLLIN - received data
-// POLLNVAL - closed the socket on our side
-// POLLHUP - closed the socket on another side (does not work! Received POLLIN and the next reading returns 0 bytes)
+    // POLLIN - received data
+    // POLLNVAL - closed the socket on our side
+    // POLLHUP - closed the socket on another side (does not work! Received POLLIN and the next reading returns 0 bytes)
     fds.events = POLLIN; // | | POLLNVAL | POLLHUP | POLLERR | POLLPRI
     res = poll(&fds, 1, timeout);
 
-// since POLLIN=(POLLRDNORM | POLLRDBAND), then maybe revents=POLLRDNORM
+    // since POLLIN=(POLLRDNORM | POLLRDBAND), then maybe revents=POLLRDNORM
     if(res == 1 && !(fds.revents & POLLIN)) //if(res==1 && fds.revents!=POLLIN)
         return -1;
     return res;
@@ -117,25 +118,25 @@ static bool is_valid_socket(SOCKET sock)
     struct pollfd fds;
     fds.fd = sock;
     fds.events = POLLIN;
-// return: -1 err, 0 timeout, 1 waited
+    // return: -1 err, 0 timeout, 1 waited
     int count_desc = poll(&fds, 1, 0);
-// error
+    // error
     if(count_desc == -1)
         return false;
-// event with an error code
+    // event with an error code
     if(count_desc > 0)
             {
-// feature of disconnection under Windows
-// under Windows, with socket closed fds.revents=POLLHUP, in Unix fds.events = POLLIN
+        // feature of disconnection under Windows
+        // under Windows, with socket closed fds.revents=POLLHUP, in Unix fds.events = POLLIN
         if(fds.revents & (POLLERR | POLLHUP | POLLNVAL))
             return false;
-// feature of disconnection under Unix (QNX)
-// under Windows, with socket closed res = 0, in Unix res = -1
+        // feature of disconnection under Unix (QNX)
+        // under Windows, with socket closed res = 0, in Unix res = -1
         char buf[2];
         int res = recv(sock, buf, 1, MSG_PEEK); // MSG_PEEK  The data is treated as unread and the next recv() function shall still return this data.
         if(res < 0)
             return false;
-// data in the buffer must be(count_desc>0), but read 0 bytes(res=0)
+        // data in the buffer must be(count_desc>0), but read 0 bytes(res=0)
         if(!res && (fds.revents & POLLIN))
             return false;
     }
@@ -162,7 +163,7 @@ int s_recv(SOCKET sock, unsigned char *buf, int bufsize, int timeout)
     if(res < 1) {
         return -1;
     }
-//    res = read(sock, (char*) buf, bufsize);
+    //    res = read(sock, (char*) buf, bufsize);
     res = recv(sock, (char*) buf, bufsize, 0); //MSG_WAITALL
     if(res <= 0) { //EINTR=4  ENOENT=2 EINVAL=22 ECONNRESET=254
         printf("[s_recv] recv()=%d errno=%d\n", res, errno);
@@ -183,29 +184,29 @@ char* s_get_next_str(SOCKET nSocket, int *dwLen, const char *stop_str, bool del_
     bool bSuccess = false;
     int nRecv = 0; // count of bytes received
     int stop_str_len = (stop_str) ? strlen(stop_str) : 0;
-// if there is nothing to look for
+    // if there is nothing to look for
     if(!stop_str_len)
         return NULL;
     int lpszBuffer_len = 256;
     char *lpszBuffer = calloc(1, lpszBuffer_len);
-// received string will not be larger than MAX_REPLY_LEN
+    // received string will not be larger than MAX_REPLY_LEN
     while(1) //nRecv < MAX_REPLY_LEN)
     {
-// read one byte
+        // read one byte
         int ret = s_recv(nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
-//int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
+        //int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
         if(ret <= 0)
                 {
             break;
         }
         nRecv += ret;
-//printf("**debug** socket=%d read  %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
+        //printf("**debug** socket=%d read  %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
         while((nRecv + 1) >= lpszBuffer_len)
         {
             lpszBuffer_len *= 2;
             lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
         }
-// search for the required string
+        // search for the required string
         if(nRecv >= stop_str_len) {
             // found the required string
             if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) {
@@ -214,9 +215,9 @@ char* s_get_next_str(SOCKET nSocket, int *dwLen, const char *stop_str, bool del_
             }
         }
     };
-// end reading
+    // end reading
     if(bSuccess) {
-// delete the searched string
+        // delete the searched string
         if(del_stop_str) {
             lpszBuffer[nRecv - stop_str_len] = '\0';
             if(dwLen)
@@ -230,7 +231,7 @@ char* s_get_next_str(SOCKET nSocket, int *dwLen, const char *stop_str, bool del_
         lpszBuffer = realloc(lpszBuffer, *dwLen + 1);
         return lpszBuffer;
     }
-// in case of an error or missing string
+    // in case of an error or missing string
     if(dwLen)
         *dwLen = 0;
     free(lpszBuffer);
@@ -252,26 +253,23 @@ static void* thread_one_client_func(void *args)
     GList *cmd_param_list = NULL;
     while(1)
     {
-// wait data from client
+        // wait data from client
         int is_data = s_poll(newsockfd, timeout);
-//printf("is data=%d sockfd=%d \n", is_data, newsockfd);
-// timeout
+        // timeout
         if(!is_data)
             continue;
-// error (may be socket closed)
+        // error (may be socket closed)
         if(is_data < 0)
             break;
 
         int is_valid = is_valid_socket(newsockfd);
         if(!is_valid)
         {
-            //printf("isvalid=%d sockfd=%d \n", is_valid, newsockfd);
             break;
         }
-// receiving http header
+        // receiving http header
         char *str_header = s_get_next_str(newsockfd, &str_len, "\r\n", true, timeout);
-//printf("str_header='%s' sock=%d\n", str_header, newsockfd);
-// bad format
+        // bad format
         if(!str_header)
             break;
         if(str_header && strlen(str_header) == 0) {
@@ -279,7 +277,7 @@ static void* thread_one_client_func(void *args)
             if(marker == 1)
                 continue;
         }
-// filling parameters of command
+        // filling parameters of command
         if(marker == 1) {
             cmd_param_list = g_list_append(cmd_param_list, str_header);
             //printf("g_list_append argc=%d command=%s ", argc, str_header);
@@ -332,7 +330,7 @@ static void* thread_one_client_func(void *args)
             break;
         }
     }
-// close connection
+    // close connection
     int cs = closesocket(newsockfd);
     log_it(L_INFO, "close connection=%d sockfd=%d", cs, newsockfd);
     return NULL;
@@ -346,23 +344,23 @@ static void* thread_main_func(void *args)
     SOCKET sockfd = (SOCKET) (intptr_t) args;
     SOCKET newsockfd;
     log_it(L_INFO, "Server start socket=%s", UNIX_SOCKET_FILE);
-// wait of clients
+    // wait of clients
     while(1)
     {
         pthread_t threadId;
         struct sockaddr_in peer;
         socklen_t size = sizeof(peer);
-// received a new connection request
+        // received a new connection request
         if((newsockfd = accept(sockfd, (struct sockaddr*) &peer, &size)) == (SOCKET) -1) {
             log_it(L_ERROR, "new connection break newsockfd=%d", newsockfd);
             break;
         }
-// create child thread for a client connection
+        // create child thread for a client connection
         pthread_create(&threadId, NULL, thread_one_client_func, (void*) (intptr_t) newsockfd);
-// in order to thread not remain in state "dead" after completion
+        // in order to thread not remain in state "dead" after completion
         pthread_detach(threadId);
     };
-// close connection
+    // close connection
     int cs = closesocket(sockfd);
     log_it(L_INFO, "Exit server thread=%d socket=%d", cs, sockfd);
     return NULL;
@@ -377,8 +375,11 @@ static void* thread_main_func(void *args)
 int dap_chain_node_cli_init(dap_config_t * g_config)
 {
     struct sockaddr_un server = { AF_UNIX, UNIX_SOCKET_FILE };
-//server.sun_family = AF_UNIX;
-//strcpy(server.sun_path, SOCKET_FILE);
+    //server.sun_family = AF_UNIX;
+    //strcpy(server.sun_path, SOCKET_FILE);
+
+    // init client for handshake
+    chain_node_client_init();
 
     SOCKET sockfd;
 
@@ -387,7 +388,7 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
         server_sockfd = 0;
     }
 
-// create socket
+    // create socket
     if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == INVALID_SOCKET)
         return -1;
     int gdsg = sizeof(struct sockaddr_un);
@@ -395,9 +396,9 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
             {
         unlink(UNIX_SOCKET_FILE);
     }
-// connecting the address with a socket
+    // connecting the address with a socket
     if(bind(sockfd, (const struct sockaddr*) &server, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
-// errno = EACCES  13  Permission denied
+        // errno = EACCES  13  Permission denied
         if(errno == EACCES) // EACCES=13
             log_it(L_ERROR, "Server can't start(err=%d). Can't create file=%s [Permission denied]", errno,
                     UNIX_SOCKET_FILE);
@@ -406,16 +407,16 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
         closesocket(sockfd);
         return -1;
     }
-// turn on reception of connections
+    // turn on reception of connections
     if(listen(sockfd, 5) == SOCKET_ERROR)
         return -1;
-// create thread for waiting of clients
+    // create thread for waiting of clients
     pthread_t threadId;
     if(pthread_create(&threadId, NULL, thread_main_func, (void*) (intptr_t) sockfd) != 0) {
         closesocket(sockfd);
         return -1;
     }
-// in order to thread not remain in state "dead" after completion
+    // in order to thread not remain in state "dead" after completion
     pthread_detach(threadId);
     server_sockfd = sockfd;
     return 0;
@@ -429,4 +430,7 @@ void dap_chain_node_cli_delete(void)
 {
     if(server_sockfd >= 0)
         closesocket(server_sockfd);
+
+    // deinit client for handshake
+    chain_node_client_deinit();
 }
diff --git a/dap_chain_node_cli_cmd.c b/dap_chain_node_cli_cmd.c
index 59eac88b6146f145b14dcd73ec08a57567dc8f67..f17c9383dc548ffe85f1d936adcf68c3b3051d9c 100644
--- a/dap_chain_node_cli_cmd.c
+++ b/dap_chain_node_cli_cmd.c
@@ -33,6 +33,8 @@
 //#include "dap_common.h"
 #include "dap_chain_node.h"
 #include "dap_chain_global_db.h"
+#include "dap_chain_node_cli_connect.h"
+#include "dap_chain_node_remote.h"
 #include "dap_chain_node_cli_cmd.h"
 
 // Max and min macros
@@ -201,6 +203,11 @@ static char* com_global_db_get_key_for_addr(dap_chain_node_addr_t *address)
 static void set_reply_text(char **str_reply, const char *str, ...)
 {
     if(str_reply) {
+        if(*str_reply) {
+            assert(!*str_reply);
+            g_free(*str_reply);
+            *str_reply = NULL;
+        }
         va_list args;
         va_start(args, str);
         *str_reply = g_strdup_vprintf(str, args); //*str_reply = g_strdup(str);
@@ -208,6 +215,54 @@ static void set_reply_text(char **str_reply, const char *str, ...)
     }
 }
 
+/**
+ * Read node from base
+ */
+static dap_chain_node_info_t* dap_chain_node_info_read(dap_chain_node_addr_t *address, char **str_reply)
+{
+    char *a_key = com_global_db_get_key_for_addr(address);
+    if(!a_key)
+    {
+        set_reply_text(str_reply, "can't calculate hash of addr");
+        return NULL;
+    }
+    // read node
+    char *str = dap_chain_global_db_gr_get(a_key, GROUP_NODE);
+    if(!str) {
+        set_reply_text(str_reply, "node not found in base");
+        DAP_DELETE(a_key);
+        return NULL;
+    }
+    dap_chain_node_info_t *node_info = dap_chain_node_info_deserialize(str, (str) ? strlen(str) : 0);
+    if(!node_info) {
+        set_reply_text(str_reply, "node has invalid format in base");
+    }
+    DAP_DELETE(str);
+    DAP_DELETE(a_key);
+    return node_info;
+}
+
+/**
+ * Save node from base
+ */
+static bool dap_chain_node_info_save(dap_chain_node_info_t *node_info, char **str_reply)
+{
+    if(!node_info || !node_info->hdr.address.uint64) {
+        set_reply_text(str_reply, "node addr not found");
+        return false;
+    }
+    char *a_key = com_global_db_get_key_for_addr(&node_info->hdr.address);
+    if(!a_key)
+    {
+        set_reply_text(str_reply, "can't calculate hash for addr");
+        return NULL;
+    }
+    char *a_value = dap_chain_node_info_serialize(node_info, NULL);
+    bool res = dap_chain_global_db_gr_set(a_key, a_value, GROUP_NODE);
+    DAP_DELETE(a_key);
+    DAP_DELETE(a_value);
+    return res;
+}
 /**
  * Handler of command 'global_db node add'
  *
@@ -239,8 +294,7 @@ static int com_global_db_add(dap_chain_node_info_t *node_info, const char *alias
     // check match addr to shard or no
     /*dap_chain_node_addr_t *addr = dap_chain_node_gen_addr(&node_info->hdr.shard_id);
      if(!dap_chain_node_check_addr(&node_info->hdr.address, &node_info->hdr.shard_id)) {
-     if(str_reply)
-     *str_reply = g_strdup("shard does not match addr");
+     set_reply_text(str_reply, "shard does not match addr");
      return -1;
      }*/
     if(alias_str) {
@@ -254,16 +308,11 @@ static int com_global_db_add(dap_chain_node_info_t *node_info, const char *alias
     }
 
     // write to base
-    char *a_key = dap_chain_global_db_hash((const uint8_t*) &(node_info->hdr.address),
-            sizeof(dap_chain_node_addr_t));
-    char *a_value = dap_chain_node_info_serialize(node_info, NULL);
-    bool res = dap_chain_global_db_gr_set(a_key, a_value, GROUP_NODE);
+    bool res = dap_chain_node_info_save(node_info, str_reply);
     if(res)
         set_reply_text(str_reply, "node added");
     else
-        set_reply_text(str_reply, "node not added");
-    DAP_DELETE(a_key);
-    DAP_DELETE(a_value);
+        return -1;
     if(res)
         return 0;
     return -1;
@@ -303,7 +352,7 @@ static int com_global_db_del(dap_chain_node_info_t *node_info, const char *alias
                     del_alias(alias);
                     list = g_list_next(list);
                 }
-                g_list_free_full(list_aliases, free);
+                g_list_free_full(list_aliases, (GDestroyNotify) free);
             }
             // set text response
             set_reply_text(str_reply, "node deleted");
@@ -351,30 +400,9 @@ static int com_global_db_link(dap_chain_node_info_t *node_info, const char *cmd,
         set_reply_text(str_reply, "alias not found");
         return -1;
     }
-    char *a_key = com_global_db_get_key_for_addr(address);
-    if(!a_key)
-    {
-        set_reply_text(str_reply, "addr to link can't be defined");
-        DAP_DELETE(address);
-        return -1;
-    }
-    bool res_successful = false;
-    // read node
-    char *str = dap_chain_global_db_gr_get(a_key, GROUP_NODE);
-    if(!str) {
-        set_reply_text(str_reply, "node is not found in base");
-        DAP_DELETE(address);
-        DAP_DELETE(a_key);
-        return -1;
-    }
-    dap_chain_node_info_t *node_info_read = dap_chain_node_info_deserialize(str, strlen(str));
-    if(!node_info_read) {
-        set_reply_text(str_reply, "node have bad format in base");
-        DAP_DELETE(str);
-        DAP_DELETE(a_key);
-        DAP_DELETE(address);
+    dap_chain_node_info_t *node_info_read = dap_chain_node_info_read(address, str_reply);
+    if(!node_info_read)
         return -1;
-    }
 
     int cmd_int = 0;
     if(!strcmp(cmd, "add"))
@@ -391,6 +419,7 @@ static int com_global_db_link(dap_chain_node_info_t *node_info, const char *cmd,
             break;
         }
     }
+    bool res_successful = false; // is successful whether add/del
     // add link
     if(cmd_int == 1) {
         if(index_link == -1) {
@@ -412,9 +441,7 @@ static int com_global_db_link(dap_chain_node_info_t *node_info, const char *cmd,
     }
     // save edited node_info
     if(res_successful) {
-
-        char *a_value = dap_chain_node_info_serialize(node_info_read, NULL);
-        bool res = dap_chain_global_db_gr_set(a_key, a_value, GROUP_NODE);
+        bool res = dap_chain_node_info_save(node_info_read, str_reply);
         if(res) {
             res_successful = true;
             if(cmd_int == 1)
@@ -425,7 +452,6 @@ static int com_global_db_link(dap_chain_node_info_t *node_info, const char *cmd,
         else {
             res_successful = false;
         }
-        DAP_DELETE(a_value);
     }
     else {
         if(cmd_int == 1) {
@@ -442,8 +468,6 @@ static int com_global_db_link(dap_chain_node_info_t *node_info, const char *cmd,
         }
     }
 
-    DAP_DELETE(str);
-    DAP_DELETE(a_key);
     DAP_DELETE(address);
     DAP_DELETE(node_info_read);
     if(res_successful)
@@ -464,86 +488,63 @@ static int com_global_db_dump(dap_chain_node_info_t *node_info, const char *alia
         set_reply_text(str_reply, "addr not found");
         return -1;
     }
-// find addr by alias or addr_str
+    // find addr by alias or addr_str
     dap_chain_node_addr_t *address = com_global_db_get_addr(node_info, &node_info->hdr.address, alias_str);
     if(!address) {
         set_reply_text(str_reply, "alias not found");
         return -1;
     }
-    a_key = com_global_db_get_key_for_addr(address);
-    if(a_key)
-    {
-        dap_chain_node_info_t *node_info_read = NULL;
-        // read node
-        char *str = dap_chain_global_db_gr_get(a_key, GROUP_NODE);
-        if(str) {
-            node_info_read = dap_chain_node_info_deserialize(str, strlen(str));
-            if(!node_info_read) {
-                set_reply_text(str_reply, "node have bad format in base");
-            }
-            else {
-                ;
-                //get_host(struct in_addr addr, char *host, size_t hostlen)
-                int hostlen = 128;
-                char host4[hostlen];
-                char host6[hostlen];
-                //const struct sockaddr *sa
-                //sockaddr_in
-                struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = node_info_read->hdr.ext_addr_v4 };
-                const char* res4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, hostlen);
-
-                struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = node_info_read->hdr.ext_addr_v6 };
-
-                const char* res6 = inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host6, hostlen);
-                //int res = getnameinfo(&sa4, sizeof sa4, host4, hostlen, 0, 0, 0);
-
-                // get aliases in form of string
-                GString *aliases_string = g_string_new(NULL);
-                GList *list_aliases = get_aliases_by_name(address);
-                if(list_aliases)
-                {
-                    GList *list = list_aliases;
-                    while(list)
-                    {
-                        const char *alias = (const char *) list->data;
-                        g_string_append_printf(aliases_string, "\nalias %s", alias);
-                        list = g_list_next(list);
-                    }
-                    g_list_free_full(list_aliases, (GDestroyNotify) free);
-                }
-                else
-                    g_string_append(aliases_string, "\nno aliases");
-
-                // get links in form of string
-                GString *links_string = g_string_new(NULL);
-                for(int i = 0; i < node_info_read->hdr.links_number; i++) {
-                    dap_chain_node_addr_t link_addr = node_info_read->links[i];
-                    g_string_append_printf(links_string, "\nlink%02d address : 0x%llx", i, link_addr.uint64);
-                }
-
-                // set full reply with node param
-                set_reply_text(str_reply, "node address 0x%llx\nshard 0x%llx%s\nipv4 %s\nipv6 %s\nnumber of links %d%s",
-                        node_info_read->hdr.address, node_info_read->hdr.shard_id, aliases_string->str,
-                        host4, host6,
-                        node_info_read->hdr.links_number, links_string->str);
-                g_string_free(aliases_string, TRUE);
-                g_string_free(links_string, TRUE);
-            }
-        }
-        else
-            set_reply_text(str_reply, "node is not found in base");
-        DAP_DELETE(str);
-        DAP_DELETE(a_key);
+    // read node
+    dap_chain_node_info_t *node_info_read = dap_chain_node_info_read(address, str_reply);
+    if(!node_info_read) {
         DAP_DELETE(address);
-        if(node_info_read)
+        return -1;
+    }
+
+    int hostlen = 128;
+    char host4[hostlen];
+    char host6[hostlen];
+    struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = node_info_read->hdr.ext_addr_v4 };
+    const char* str_ip4 = inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host4, hostlen);
+
+    struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = node_info_read->hdr.ext_addr_v6 };
+    const char* str_ip6 = inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host6, hostlen);
+
+    // get aliases in form of string
+    GString *aliases_string = g_string_new(NULL);
+    GList *list_aliases = get_aliases_by_name(address);
+    if(list_aliases)
+    {
+        GList *list = list_aliases;
+        while(list)
         {
-            DAP_DELETE(node_info_read);
-            return 0;
+            const char *alias = (const char *) list->data;
+            g_string_append_printf(aliases_string, "\nalias %s", alias);
+            list = g_list_next(list);
         }
-        return -1;
+        g_list_free_full(list_aliases, (GDestroyNotify) free);
     }
-    set_reply_text(str_reply, "addr to dump can't be defined");
+    else
+        g_string_append(aliases_string, "\nno aliases");
+
+    // get links in form of string
+    GString *links_string = g_string_new(NULL);
+    for(int i = 0; i < node_info_read->hdr.links_number; i++) {
+        dap_chain_node_addr_t link_addr = node_info_read->links[i];
+        g_string_append_printf(links_string, "\nlink%02d address : 0x%llx", i, link_addr.uint64);
+    }
+
+    // set full reply with node param
+    set_reply_text(str_reply, "node address 0x%llx\nshard 0x%llx%s\nipv4 %s\nipv6 %s\nnumber of links %d%s",
+            node_info_read->hdr.address, node_info_read->hdr.shard_id, aliases_string->str,
+            str_ip4, str_ip6,
+            node_info_read->hdr.links_number, links_string->str);
+    g_string_free(aliases_string, TRUE);
+    g_string_free(links_string, TRUE);
+
     DAP_DELETE(address);
+    DAP_DELETE(node_info_read);
+
     return -1;
 }
 
@@ -559,15 +560,14 @@ int com_global_db(int argc, const char ** argv, char **str_reply)
     };
     printf("com_global_db\n");
     int arg_index = 1;
-// find 'node' as first parameter only
+    // find 'node' as first parameter only
     arg_index = find_option_val(argv, arg_index, min(argc, arg_index + 1), "node", NULL);
     if(!arg_index || argc < 4) {
-        if(str_reply)
-            *str_reply = g_strdup("parameters are not valid");
+        set_reply_text(str_reply, "parameters are not valid");
         return -1;
     }
     int arg_index_n = ++arg_index;
-// find command (add, delete, etc) as second parameter only
+    // find command (add, delete, etc) as second parameter only
     int cmd_num = CMD_NONE;
     if((arg_index_n = find_option_val(argv, arg_index, min(argc, arg_index + 1), "add", NULL)) != 0) {
         cmd_num = CMD_ADD;
@@ -582,16 +582,15 @@ int com_global_db(int argc, const char ** argv, char **str_reply)
         cmd_num = CMD_DUMP;
     }
     if(cmd_num == CMD_NONE) {
-        if(str_reply)
-            *str_reply = g_strdup_printf("command %s not recognized", argv[1]);
+        set_reply_text(str_reply, "command %s not recognized", argv[1]);
         return -1;
     }
-//arg_index = arg_index_n; // no need, they are already equal must be
+    //arg_index = arg_index_n; // no need, they are already equal must be
     assert(arg_index == arg_index_n);
     arg_index++;
     const char *addr_str = NULL, *alias_str = NULL, *shard_str = NULL, *link_str = NULL;
     const char *ipv4_str = NULL, *ipv6_str = NULL;
-// find addr, alias
+    // find addr, alias
     find_option_val(argv, arg_index, argc, "-addr", &addr_str);
     find_option_val(argv, arg_index, argc, "-alias", &alias_str);
     find_option_val(argv, arg_index, argc, "-shard", &shard_str);
@@ -599,7 +598,7 @@ int com_global_db(int argc, const char ** argv, char **str_reply)
     find_option_val(argv, arg_index, argc, "-ipv6", &ipv6_str);
     find_option_val(argv, arg_index, argc, "-link", &link_str);
 
-// struct to write to the global db
+    // struct to write to the global db
     dap_chain_node_info_t node_info;
     dap_chain_node_addr_t link;
     memset(&node_info, 0, sizeof(dap_chain_node_info_t));
@@ -616,14 +615,13 @@ int com_global_db(int argc, const char ** argv, char **str_reply)
 
     switch (cmd_num)
     {
-// add new node to global_db
+    // add new node to global_db
     case CMD_ADD:
         if(!arg_index || argc < 8) {
-            if(str_reply)
-                *str_reply = g_strdup("parameters are not valid");
+            set_reply_text(str_reply, "invalid parameters");
             return -1;
         }
-// handler of command 'global_db node add'
+        // handler of command 'global_db node add'
         return com_global_db_add(&node_info, alias_str, shard_str, ipv4_str, ipv6_str, str_reply);
         break;
 
@@ -649,15 +647,9 @@ int com_global_db(int argc, const char ** argv, char **str_reply)
         return com_global_db_dump(&node_info, alias_str, str_reply);
         break;
     default:
-        if(str_reply)
-            *str_reply = g_strdup_printf("command %s not recognized", argv[1]);
+        set_reply_text(str_reply, "command %s not recognized", argv[1]);
         return -1;
     }
-
-//inet_ntop(AF_INET, &(dap_addr.uint64), str, INET_ADDRSTRLEN);
-//uint64
-//    timestamp = time(NULL);
-
     return -1;
 }
 
@@ -681,11 +673,11 @@ int com_node(int argc, const char ** argv, char **str_reply)
     }
     arg_index++;
     if(cmd_num == CMD_NONE) {
-        if(str_reply)
-            *str_reply = g_strdup_printf("command %s not recognized", argv[1]);
+        set_reply_text(str_reply, "command %s not recognized", argv[1]);
         return -1;
     }
     dap_chain_node_addr_t address;
+    memset(&address, 0, sizeof(dap_chain_node_addr_t));
     const char *addr_str = NULL, *alias_str = NULL;
 // find addr, alias
     find_option_val(argv, arg_index, argc, "-addr", &addr_str);
@@ -703,19 +695,16 @@ int com_node(int argc, const char ** argv, char **str_reply)
                 if(!add_alias(alias_str, &address))
                     log_it(L_WARNING, "can't save alias %s", alias_str);
                 else {
-                    if(str_reply)
-                        *str_reply = g_strdup("alias mapped successfully");
+                    set_reply_text(str_reply, "alias mapped successfully");
                 }
             }
             else {
-                if(str_reply)
-                    *str_reply = g_strdup("alias can't be mapped because -addr is not found");
+                set_reply_text(str_reply, "alias can't be mapped because -addr is not found");
                 return -1;
             }
         }
         else {
-            if(str_reply)
-                *str_reply = g_strdup("alias can't be mapped because -alias is not found");
+            set_reply_text(str_reply, "alias can't be mapped because -alias is not found");
             return -1;
         }
 
@@ -740,10 +729,33 @@ int com_node(int argc, const char ** argv, char **str_reply)
             return -1;
         }
 
-        // TODO start handshake
+        dap_chain_node_info_t *node_info = dap_chain_node_info_read(&address, str_reply);
+        if(!node_info) {
+            return -1;
+        }
+        int timeout_ms = 100000; //100 sec.
+        // start handshake
+        chain_node_client_t *client = chain_node_client_connect(node_info);
+        if(!client) {
+            set_reply_text(str_reply, "can't connect");
+            DAP_DELETE(node_info);
+            return -1;
+        }
+        // wait handshake
+        int res = chain_node_client_wait(client, NODE_CLIENT_STATE_CONNECTED, timeout_ms);
+        if(res != 1) {
+            set_reply_text(str_reply, "no response from node");
+            // clean client struct
+            chain_node_client_close(client);
+            DAP_DELETE(node_info);
+            return -1;
+        }
+        DAP_DELETE(node_info);
 
-        if(str_reply)
-            *str_reply = g_strdup("handshake in progress...");
+        //Add new established connection in the list
+        chain_node_client_list_add(client);
+
+        set_reply_text(str_reply, "connection established");
         break;
     }
     return 0;
@@ -763,56 +775,55 @@ int com_traceroute(int argc, const char** argv, char **str_reply)
     iputils_set_verbose();
     int res = (addr) ? traceroute_util(addr, &hops, &time_usec) : -EADDRNOTAVAIL;
     if(res >= 0) {
-        if(str_reply)
-            *str_reply = g_strdup_printf("traceroute %s hops=%d time=%.1lf ms", addr, hops, time_usec * 1. / 1000);
+        set_reply_text(str_reply, "traceroute %s hops=%d time=%.1lf ms", addr, hops, time_usec * 1. / 1000);
     }
     else {
         if(str_reply) {
             switch (-res)
             {
             case EADDRNOTAVAIL:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", (addr) ? addr : "",
+                set_reply_text(str_reply, "traceroute %s error: %s", (addr) ? addr : "",
                         (addr) ? "Name or service not known" : "Host not defined");
                 break;
             case 2:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "Unknown traceroute module");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "Unknown traceroute module");
                 break;
             case 3:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "first hop out of range");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "first hop out of range");
                 break;
             case 4:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "max hops cannot be more than 255");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "max hops cannot be more than 255");
                 break;
             case 5:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "no more than 10 probes per hop");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "no more than 10 probes per hop");
                 break;
             case 6:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "bad wait specifications");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "bad wait specifications");
                 break;
             case 7:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "too big packetlen ");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "too big packetlen ");
                 break;
             case 8:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr,
+                set_reply_text(str_reply, "traceroute %s error: %s", addr,
                         "IP version mismatch in addresses specified");
                 break;
             case 9:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "bad sendtime");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "bad sendtime");
                 break;
             case 10:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "init_ip_options");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "init_ip_options");
                 break;
             case 11:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "calloc");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "calloc");
                 break;
             case 12:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "parse cmdline");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "parse cmdline");
                 break;
             case 13:
-                *str_reply = g_strdup_printf("traceroute %s error: %s", addr, "trace method's init failed");
+                set_reply_text(str_reply, "traceroute %s error: %s", addr, "trace method's init failed");
                 break;
             default:
-                *str_reply = g_strdup_printf("traceroute %s error(%d) %s", addr, res, "trace not found");
+                set_reply_text(str_reply, "traceroute %s error(%d) %s", addr, res, "trace not found");
             }
         }
     }
@@ -834,48 +845,48 @@ int com_tracepath(int argc, const char** argv, char **str_reply)
     int res = (addr) ? tracepath_util(addr, &hops, &time_usec) : -EADDRNOTAVAIL;
     if(res >= 0) {
         if(str_reply)
-            *str_reply = g_strdup_printf("tracepath %s hops=%d time=%.1lf ms", addr, hops, time_usec * 1. / 1000);
+            set_reply_text(str_reply, "tracepath %s hops=%d time=%.1lf ms", addr, hops, time_usec * 1. / 1000);
     }
     else {
         if(str_reply) {
             switch (-res)
             {
             case EADDRNOTAVAIL:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", (addr) ? addr : "",
+                set_reply_text(str_reply, "tracepath %s error: %s", (addr) ? addr : "",
                         (addr) ? "Name or service not known" : "Host not defined");
                 break;
             case ESOCKTNOSUPPORT:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't create socket");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't create socket");
                 break;
             case 2:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IPV6_MTU_DISCOVER");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IPV6_MTU_DISCOVER");
                 break;
             case 3:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IPV6_RECVERR");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IPV6_RECVERR");
                 break;
             case 4:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IPV6_HOPLIMIT");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IPV6_HOPLIMIT");
                 break;
             case 5:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IP_MTU_DISCOVER");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IP_MTU_DISCOVER");
                 break;
             case 6:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IP_RECVERR");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IP_RECVERR");
                 break;
             case 7:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IP_RECVTTL");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IP_RECVTTL");
                 break;
             case 8:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "malloc");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "malloc");
                 break;
             case 9:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IPV6_UNICAST_HOPS");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IPV6_UNICAST_HOPS");
                 break;
             case 10:
-                *str_reply = g_strdup_printf("tracepath %s error: %s", addr, "Can't setsockopt IP_TTL");
+                set_reply_text(str_reply, "tracepath %s error: %s", addr, "Can't setsockopt IP_TTL");
                 break;
             default:
-                *str_reply = g_strdup_printf("tracepath %s error(%d) %s", addr, res, "trace not found");
+                set_reply_text(str_reply, "tracepath %s error(%d) %s", addr, res, "trace not found");
             }
         }
     }
@@ -904,24 +915,24 @@ int com_ping(int argc, const char** argv, char **str_reply)
     int res = (addr) ? ping_util(addr, n) : -EADDRNOTAVAIL;
     if(res >= 0) {
         if(str_reply)
-            *str_reply = g_strdup_printf("ping %s time=%.1lf ms", addr, res * 1. / 1000);
+            set_reply_text(str_reply, "ping %s time=%.1lf ms", addr, res * 1. / 1000);
     }
     else {
         if(str_reply) {
             switch (-res)
             {
             case EDESTADDRREQ:
-                *str_reply = g_strdup_printf("ping %s error: %s", addr, "Destination address required");
+                set_reply_text(str_reply, "ping %s error: %s", addr, "Destination address required");
                 break;
             case EADDRNOTAVAIL:
-                *str_reply = g_strdup_printf("ping %s error: %s", (addr) ? addr : "",
+                set_reply_text(str_reply, "ping %s error: %s", (addr) ? addr : "",
                         (addr) ? "Host not found" : "Host not defined");
                 break;
             case EPFNOSUPPORT:
-                *str_reply = g_strdup_printf("ping %s error: %s", addr, "Unknown protocol family");
+                set_reply_text(str_reply, "ping %s error: %s", addr, "Unknown protocol family");
                 break;
             default:
-                *str_reply = g_strdup_printf("ping %s error(%d)", addr, -res);
+                set_reply_text(str_reply, "ping %s error(%d)", addr, -res);
             }
         }
     }
@@ -942,10 +953,10 @@ int com_help(int argc, const char ** argv, char **str_reply)
             return 1;
         }
         if(str_reply)
-            *str_reply = g_strdup_printf("command \"%s\" not recognized", argv[1]);
+            set_reply_text(str_reply, "command \"%s\" not recognized", argv[1]);
     }
     if(str_reply)
-        *str_reply = g_strdup("command not defined, enter \"help <cmd name>\"");
+        set_reply_text(str_reply, "command not defined, enter \"help <cmd name>\"");
     return -1;
 }
 
diff --git a/dap_chain_node_cli_connect.c b/dap_chain_node_cli_connect.c
new file mode 100644
index 0000000000000000000000000000000000000000..b3bf58164381c40a2844b81b91a429db3180bc8b
--- /dev/null
+++ b/dap_chain_node_cli_connect.c
@@ -0,0 +1,198 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <naeper@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <glib.h>
+#include <time.h>
+
+#include "dap_common.h"
+#include "dap_client.h"
+#include "dap_client_pvt.h"
+#include "dap_config.h"
+#include "dap_events.h"
+#include "dap_http_client_simple.h"
+#include "dap_chain_node_cli_connect.h"
+
+#define LOG_TAG "chain_node_cli_connect"
+
+#define DAP_APP_NAME NODE_NETNAME"-node"
+#define SYSTEM_PREFIX "/opt/"DAP_APP_NAME
+#define SYSTEM_CONFIGS_DIR SYSTEM_PREFIX"/etc"
+
+static int listen_port_tcp = 8079;
+
+int chain_node_client_init(void)
+{
+    int res = dap_client_init();
+    res = dap_http_client_simple_init();
+    dap_config_t *g_config;
+    // read listen_port_tcp from settings
+    dap_config_init(SYSTEM_CONFIGS_DIR);
+    if((g_config = dap_config_open(DAP_APP_NAME)) == NULL) {
+        return -1;
+    }
+    else {
+        const char *port_str = dap_config_get_item_str(g_config, "server", "listen_port_tcp");
+        listen_port_tcp = (port_str) ? atoi(port_str) : 8079;
+    }
+    if(g_config)
+        dap_config_close(g_config);
+    return res;
+}
+
+void chain_node_client_deinit()
+{
+    dap_http_client_simple_deinit();
+    dap_client_deinit();
+}
+
+// callback for dap_client_new() in chain_node_client_connect()
+static void stage_status_callback(dap_client_t *a_client, void *a_arg)
+{
+    //printf("* stage_status_callback client=%x data=%x\n", a_client, a_arg);
+}
+// callback for dap_client_new() in chain_node_client_connect()
+static void stage_status_error_callback(dap_client_t *a_client, void *a_arg)
+{
+    //printf("* tage_status_error_callback client=%x data=%x\n", a_client, a_arg);
+}
+
+// callback for the end of handshake in dap_client_go_stage() / chain_node_client_connect()
+static void a_stage_end_callback(dap_client_t *a_client, void *a_arg)
+{
+    chain_node_client_t *client = a_client->_inheritor;
+    assert(client);
+    if(client) {
+        pthread_mutex_lock(&client->wait_mutex);
+        client->state = NODE_CLIENT_STATE_CONNECT;
+        pthread_cond_signal(&client->wait_cond);
+        pthread_mutex_unlock(&client->wait_mutex);
+    }
+}
+
+/**
+ * Create connection to server
+ *
+ * return a connection handle, or NULL, if an error
+ */
+chain_node_client_t* chain_node_client_connect(dap_chain_node_info_t *node_info)
+{
+    if(!node_info)
+        return NULL;
+    chain_node_client_t *client = DAP_NEW_Z(chain_node_client_t);
+    client->state = NODE_CLIENT_STATE_INIT;
+    pthread_condattr_t attr;
+    pthread_condattr_init(&attr);
+    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
+    pthread_cond_init(&client->wait_cond, &attr);
+    pthread_mutex_init(&client->wait_mutex, NULL);
+    client->a_events = dap_events_new();
+    client->a_client = dap_client_new(client->a_events, stage_status_callback, stage_status_error_callback);
+    client->a_client->_inheritor = client;
+    dap_client_pvt_t *l_client_internal = DAP_CLIENT_PVT(client->a_client);
+
+    int hostlen = 128;
+    char host[hostlen];
+    if(node_info->hdr.ext_addr_v4.s_addr)
+    {
+        struct sockaddr_in sa4 = { .sin_family = AF_INET, .sin_addr = node_info->hdr.ext_addr_v4 };
+        inet_ntop(AF_INET, &(((struct sockaddr_in *) &sa4)->sin_addr), host, hostlen);
+    }
+    else
+    {
+        struct sockaddr_in6 sa6 = { .sin6_family = AF_INET6, .sin6_addr = node_info->hdr.ext_addr_v6 };
+        inet_ntop(AF_INET6, &(((struct sockaddr_in6 *) &sa6)->sin6_addr), host, hostlen);
+    }
+    // address not defined
+    if(!strcmp(host, "::")) {
+        chain_node_client_close(client);
+        return NULL;
+    }
+    l_client_internal->uplink_addr = strdup(host);
+    l_client_internal->uplink_port = listen_port_tcp; // reads from settings, default 8079
+    l_client_internal->uplink_protocol_version = DAP_PROTOCOL_VERSION;
+    dap_client_stage_t a_stage_target = STAGE_ENC_INIT;
+
+    client->state = NODE_CLIENT_STATE_CONNECT;
+    // Handshake
+    dap_client_go_stage(client->a_client, a_stage_target, a_stage_end_callback);
+    return client;
+}
+
+/**
+ * Close connection to server, delete chain_node_client_t *client
+ */
+void chain_node_client_close(chain_node_client_t *client)
+{
+    if(client) {
+        // clean client
+        dap_client_pvt_t *l_client_internal = DAP_CLIENT_PVT(client->a_client);
+        DAP_DELETE(l_client_internal->uplink_addr);
+        dap_client_delete(client->a_client);
+        dap_events_delete(client->a_events);
+        pthread_cond_destroy(&client->wait_cond);
+        pthread_mutex_destroy(&client->wait_mutex);
+        DAP_DELETE(client);
+    }
+}
+
+/**
+ * wait for the complete of request
+ *
+ * timeout_ms timeout in milliseconds
+ * waited_state state which we will wait, sample NODE_CLIENT_STATE_CONNECT or NODE_CLIENT_STATE_SENDED
+ * return -1 false, 0 timeout, 1 end of connection or sending data
+ */
+int chain_node_client_wait(chain_node_client_t *client, int waited_state, int timeout_ms)
+{
+    int ret = -1;
+    if(!client)
+        return -1;
+    pthread_mutex_lock(&client->wait_mutex);
+    // have waited
+    if(client->state == waited_state) {
+        pthread_mutex_unlock(&client->wait_mutex);
+        return 1;
+    }
+    // prepare for signal waiting
+    struct timespec to;
+    clock_gettime(CLOCK_MONOTONIC, &to);
+    int64_t nsec_new = to.tv_nsec + timeout_ms * 1000000ll;
+    // if the new number of nanoseconds is more than a second
+    if(nsec_new > (long) 1e9) {
+        to.tv_sec += nsec_new / (long) 1e9;
+        to.tv_nsec = nsec_new % (long) 1e9;
+    }
+    else
+        to.tv_nsec = (long) nsec_new;
+    // signal waiting
+    int wait = pthread_cond_timedwait(&client->wait_cond, &client->wait_mutex, &to);
+    if(wait == 0) //0
+        ret = 1;
+    else if(wait == ETIMEDOUT) // 110 260
+        ret = 0;
+    pthread_mutex_unlock(&client->wait_mutex);
+    return ret;
+}
diff --git a/dap_chain_node_cli_connect.h b/dap_chain_node_cli_connect.h
new file mode 100644
index 0000000000000000000000000000000000000000..c2f91c1ecf31182e587d4d2180cf8dab26ea011f
--- /dev/null
+++ b/dap_chain_node_cli_connect.h
@@ -0,0 +1,75 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <naeper@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <pthread.h>
+#include <stdbool.h>
+
+#include "dap_client.h"
+#include "dap_chain_node.h"
+
+// connection states
+enum {
+    NODE_CLIENT_STATE_ERROR = -1,
+    NODE_CLIENT_STATE_INIT,
+    NODE_CLIENT_STATE_CONNECT,
+    NODE_CLIENT_STATE_CONNECTED,
+    NODE_CLIENT_STATE_SEND,
+    NODE_CLIENT_STATE_SENDED,
+    NODE_CLIENT_STATE_END
+};
+
+// state for a client connection
+typedef struct chain_node_client {
+    int state;
+    dap_client_t *a_client;
+    dap_events_t *a_events;
+    pthread_cond_t wait_cond;
+    pthread_mutex_t wait_mutex;
+} chain_node_client_t;
+
+
+int chain_node_client_init(void);
+
+void chain_node_client_deinit();
+
+/**
+ * Create connection to server
+ *
+ * return a connection handle, or NULL, if an error
+ */
+chain_node_client_t* chain_node_client_connect(dap_chain_node_info_t *node_info);
+
+/**
+ * Close connection to server, delete chain_node_client_t *client
+ */
+void chain_node_client_close(chain_node_client_t *client);
+
+/**
+ * wait for the complete of request
+ *
+ * timeout_ms timeout in milliseconds
+ * waited_state state which we will wait, sample NODE_CLIENT_STATE_CONNECT or NODE_CLIENT_STATE_SENDED
+ * return -1 false, 0 timeout, 1 end of connection or sending data
+ */
+int chain_node_client_wait(chain_node_client_t *client, int waited_state, int timeout_ms);
+
diff --git a/dap_chain_node_remote.c b/dap_chain_node_remote.c
new file mode 100644
index 0000000000000000000000000000000000000000..914a847221d0a14ae9b7cc78e7fcaea4bf2ffca9
--- /dev/null
+++ b/dap_chain_node_remote.c
@@ -0,0 +1,90 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <naeper@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <pthread.h>
+
+#include "dap_chain_node_remote.h"
+
+
+// List of connections
+static GList *connect_list = NULL;
+
+// for separate access to connect_list
+static pthread_mutex_t connect_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Add new established connection in the list
+ */
+bool chain_node_client_list_add(chain_node_client_t *client)
+{
+    if(!client)
+        return false;
+    pthread_mutex_lock(&connect_list_mutex);
+    connect_list = g_list_append(connect_list, client);
+    pthread_mutex_unlock(&connect_list_mutex);
+    return true;
+}
+
+/**
+ * Delete established connection from the list
+ */
+bool chain_node_client_list_del(chain_node_client_t *client)
+{
+    pthread_mutex_lock(&connect_list_mutex);
+    GList *list = g_list_find(connect_list, client);
+    // found
+    if(list)
+        connect_list = g_list_remove(connect_list, client);
+    pthread_mutex_unlock(&connect_list_mutex);
+    if(list)
+        return true;
+    return false;
+}
+
+/**
+ * Get one established connection
+ *
+ * n - the position of the established connection, counting from 0
+ *
+ * return client, or NULL if the position is off the end of the list
+ */
+chain_node_client_t* chain_node_client_list_get_item(int n)
+{
+    pthread_mutex_lock(&connect_list_mutex);
+    chain_node_client_t *client = g_list_nth_data(connect_list, (guint) n);
+    pthread_mutex_unlock(&connect_list_mutex);
+    return client;
+}
+/**
+ * Get the number of established connections
+ */
+int chain_node_client_list_count(void)
+{
+    pthread_mutex_lock(&connect_list_mutex);
+    int len = g_list_length(connect_list);
+    pthread_mutex_unlock(&connect_list_mutex);
+    return len;
+}
+
diff --git a/dap_chain_node_remote.h b/dap_chain_node_remote.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef7f4c5ef5452c2d5f5a1fdcfb046c67e03391a2
--- /dev/null
+++ b/dap_chain_node_remote.h
@@ -0,0 +1,50 @@
+/*
+ * Authors:
+ * Dmitriy A. Gearasimov <naeper@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+
+ This file is part of DAP (Deus Applications Prototypes) the open source project
+
+ DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ DAP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with any DAP based project.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+#include "dap_chain_node_cli_connect.h"
+
+/**
+ * Add new established connection in the list
+ */
+bool chain_node_client_list_add(chain_node_client_t *client);
+
+/**
+ * Delete established connection from the list
+ */
+bool chain_node_client_list_del(chain_node_client_t *client);
+
+/**
+ * Get one established connection
+ *
+ * n - the position of the established connection, counting from 0
+ *
+ * return client, or NULL if the position is off the end of the list
+ */
+chain_node_client_t* chain_node_client_list_get_item(int n);
+
+/**
+ * Get the number of established connections
+ */
+int chain_node_client_list_count(void);