diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bd1c578c83ad14858aad02c39306f3cf5328bc36
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,18 @@
+stages:
+    - prepare
+    - analyze
+    - build
+    - test
+
+variables:
+    GIT_SUBMODULE_STRATEGY: normal
+
+cellframe-sdk-analyze:
+    stage: analyze
+    tags:
+    - cellframe-sdk
+    script: ~/production/integration/PVS-studio/analyze.sh 
+    artifacts:
+      paths:
+      - report/issues.txt
+      when: always
diff --git a/3rdparty/wepoll/wepoll.c b/3rdparty/wepoll/wepoll.c
index 651673aad37227314985327a42f6d94790fdb653..6b5e8c6ba1e6dad8d47983e7dfbeb91f913d0aac 100644
--- a/3rdparty/wepoll/wepoll.c
+++ b/3rdparty/wepoll/wepoll.c
@@ -136,9 +136,9 @@ WEPOLL_EXPORT int epoll_wait(HANDLE ephnd,
 #pragma warning(push, 1)
 #endif
 
-#include <WS2tcpip.h>
-#include <WinSock2.h>
-#include <Windows.h>
+#include <ws2tcpip.h>
+#include <winsock2.h>
+#include <windows.h>
 
 #ifndef __GNUC__
 #pragma warning(pop)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8ab298486152842388a0616cc148ffd7c142227..61a81f20584496b93acb229992865dedb54180b3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -34,6 +34,7 @@ add_subdirectory(dap-sdk)
 add_subdirectory(3rdparty/monero_crypto)
 add_subdirectory(3rdparty/cuttdb)
 add_subdirectory(modules/)
+add_subdirectory(modules_dynamic)
 
 add_library(${PROJECT_NAME} STATIC cellframe-sdk.c)
 
@@ -123,6 +124,12 @@ if (CELLFRAME_MODULES MATCHES "srv-stake")
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_chain_net_srv_stake )
 endif()
 
+# Enable service for dynamic modules
+if (CELLFRAME_MODULES MATCHES "modules-dynamic")
+    message("[+] Module 'dap_modules_dynamic_cdb'")
+    set(CELLFRAME_LIBS ${CELLFRAME_LIBS} dap_modules_dynamic_cdb )
+endif()
+
 if (WIN32)
     set(CELLFRAME_LIBS ${CELLFRAME_LIBS} KERNEL32 USER32 SHELL32 WINMM GDI32 ADVAPI32
 					 Ole32 Version Imm32 OleAut32 ws2_32 ntdll psapi 
diff --git a/dap-sdk/core/include/dap_file_utils.h b/dap-sdk/core/include/dap_file_utils.h
index 9bc55055d72e00a02bdfc5d0d30ac3cca3ab4060..d729966872df42ca6ac729e62441f344d80e281c 100755
--- a/dap-sdk/core/include/dap_file_utils.h
+++ b/dap-sdk/core/include/dap_file_utils.h
@@ -30,7 +30,7 @@
 
 #ifdef _WIN32
 
-#include <Windows.h>
+#include <windows.h>
 
 /* On Win32, the canonical directory separator is the backslash, and
  * the search path separator is the semicolon. Note that also the
@@ -66,6 +66,10 @@ typedef struct dap_list_name_directories{
  * @dir_path directory pathname
  * @return true, if the directory path contains only ASCII symbols
  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 bool dap_valid_ascii_symbols(const char *a_dir_path);
 
 /**
@@ -111,4 +115,8 @@ dap_list_name_directories_t *dap_get_subs(const char *a_path_name);
  */
 bool dap_file_get_contents(const char *filename, char **contents, size_t *length);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif // _FILE_UTILS_H_
diff --git a/dap-sdk/core/include/dap_fnmatch.h b/dap-sdk/core/include/dap_fnmatch.h
new file mode 100644
index 0000000000000000000000000000000000000000..9afaba94581823c6ae1c909b7d18cce9a712c2b5
--- /dev/null
+++ b/dap-sdk/core/include/dap_fnmatch.h
@@ -0,0 +1,62 @@
+/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _FNMATCH_H
+#define _FNMATCH_H  1
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* We #undef these before defining them because some losing systems
+   (HP-UX A.08.07 for example) define these in <unistd.h>.  */
+#undef  FNM_PATHNAME
+#undef  FNM_NOESCAPE
+#undef  FNM_PERIOD
+
+/* Bits set in the FLAGS argument to `fnmatch'.  */
+#define FNM_PATHNAME    (1 << 0) /* No wildcard can ever match `/'.  */
+#define FNM_NOESCAPE    (1 << 1) /* Backslashes don't quote special chars.  */
+#define FNM_PERIOD  (1 << 2) /* Leading `.' is matched only explicitly.  */
+
+#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE
+# define FNM_FILE_NAME   FNM_PATHNAME   /* Preferred GNU name.  */
+# define FNM_LEADING_DIR (1 << 3)   /* Ignore `/...' after a match.  */
+# define FNM_CASEFOLD    (1 << 4)   /* Compare without regard to case.  */
+# define FNM_EXTMATCH    (1 << 5)   /* Use ksh-like extended matching. */
+#endif
+
+/* Value returned by `fnmatch' if STRING does not match PATTERN.  */
+#define FNM_NOMATCH 1
+
+/* This value is returned if the implementation does not support
+   `fnmatch'.  Since this is not the case here it will never be
+   returned but the conformance test suites still require the symbol
+   to be defined.  */
+#ifdef _XOPEN_SOURCE
+# define FNM_NOSYS  (-1)
+#endif
+
+/* Match NAME against the filename pattern PATTERN,
+   returning zero if it matches, FNM_NOMATCH if not.  */
+extern int dap_fnmatch (const char *__pattern, const char *__name, int __flags);
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* fnmatch.h */
diff --git a/dap-sdk/core/include/dap_fnmatch_loop.h b/dap-sdk/core/include/dap_fnmatch_loop.h
new file mode 100644
index 0000000000000000000000000000000000000000..550b5d80164380aea6aecbb092cd3dcbf60004c1
--- /dev/null
+++ b/dap-sdk/core/include/dap_fnmatch_loop.h
@@ -0,0 +1,1223 @@
+/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <stddef.h>
+#include "dap_fnmatch.h"
+
+struct STRUCT
+{
+  const char *pattern;
+  const char *string;
+  int no_leading_period;
+};
+
+#define L_func(CS)  CS
+/* Global variable.  */
+static int posixly_correct;
+#  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+/* Match STRING against the filename pattern PATTERN, returning zero if
+   it matches, nonzero if not.  */
+static int FCT (const char *pattern, const char *string,
+        const char *string_end, int no_leading_period, int flags,
+        struct STRUCT *ends, size_t alloca_used);
+static int EXT (int opt, const char *pattern, const char *string,
+        const char *string_end, int no_leading_period, int flags,
+        size_t alloca_used);
+static const char *END (const char *patternp);
+
+static int
+FCT (const char *pattern, const char *string, const char *string_end,
+     int no_leading_period, int flags, struct STRUCT *ends, size_t alloca_used)
+{
+  const char *p = pattern, *n = string;
+  unsigned char c;
+#ifdef _LIBC
+# if WIDE_CHAR_VERSION
+  const char *collseq = (const char *)
+    _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+# else
+  const unsigned char *collseq = (const unsigned char *)
+    _NL_CURRENT(LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+# endif
+#endif
+
+  while ((c = *p++) != '\0')
+    {
+      int new_no_leading_period = 0;
+      c = FOLD (c);
+
+      switch (c)
+    {
+    case '?':
+      if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+        {
+          int res = EXT (c, p, n, string_end, no_leading_period,
+                 flags, alloca_used);
+          if (res != -1)
+        return res;
+        }
+
+      if (n == string_end)
+        return FNM_NOMATCH;
+      else if (*n == '/' && (flags & FNM_FILE_NAME))
+        return FNM_NOMATCH;
+      else if (*n == '.' && no_leading_period)
+        return FNM_NOMATCH;
+      break;
+
+    case '\\':
+      if (!(flags & FNM_NOESCAPE))
+        {
+          c = *p++;
+          if (c == '\0')
+        /* Trailing \ loses.  */
+        return FNM_NOMATCH;
+          c = FOLD (c);
+        }
+      if (n == string_end || FOLD ((unsigned char) *n) != c)
+        return FNM_NOMATCH;
+      break;
+
+    case '*':
+      if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+        {
+          int res = EXT (c, p, n, string_end, no_leading_period,
+                 flags, alloca_used);
+          if (res != -1)
+        return res;
+        }
+      else if (ends != NULL)
+        {
+          ends->pattern = p - 1;
+          ends->string = n;
+          ends->no_leading_period = no_leading_period;
+          return 0;
+        }
+
+      if (n != string_end && *n == '.' && no_leading_period)
+        return FNM_NOMATCH;
+
+      for (c = *p++; c == '?' || c == '*'; c = *p++)
+        {
+          if (*p == '(' && (flags & FNM_EXTMATCH) != 0)
+        {
+          const char *endp = END (p);
+          if (endp != p)
+            {
+              /* This is a pattern.  Skip over it.  */
+              p = endp;
+              continue;
+            }
+        }
+
+          if (c == '?')
+        {
+          /* A ? needs to match one character.  */
+          if (n == string_end)
+            /* There isn't another character; no match.  */
+            return FNM_NOMATCH;
+          else if (*n == '/'
+               && __builtin_expect (flags & FNM_FILE_NAME, 0))
+            /* A slash does not match a wildcard under
+               FNM_FILE_NAME.  */
+            return FNM_NOMATCH;
+          else
+            /* One character of the string is consumed in matching
+               this ? wildcard, so *??? won't match if there are
+               less than three characters.  */
+            ++n;
+        }
+        }
+
+      if (c == '\0')
+        /* The wildcard(s) is/are the last element of the pattern.
+           If the name is a file name and contains another slash
+           this means it cannot match, unless the FNM_LEADING_DIR
+           flag is set.  */
+        {
+          int result = (flags & FNM_FILE_NAME) == 0 ? 0 : FNM_NOMATCH;
+
+          if (flags & FNM_FILE_NAME)
+        {
+          if (flags & FNM_LEADING_DIR)
+            result = 0;
+          else
+            {
+              if (MEMCHR (n, '/', string_end - n) == NULL)
+            result = 0;
+            }
+        }
+
+          return result;
+        }
+      else
+        {
+          const char *endp;
+          struct STRUCT end;
+
+          end.pattern = NULL;
+          endp = MEMCHR (n, (flags & FNM_FILE_NAME) ? '/' : '\0',
+                 string_end - n);
+          if (endp == NULL)
+        endp = string_end;
+
+          if (c == '['
+          || (__builtin_expect (flags & FNM_EXTMATCH, 0) != 0
+              && (c == '@' || c == '+' || c == '!')
+              && *p == '('))
+        {
+          int flags2 = ((flags & FNM_FILE_NAME)
+                ? flags : (flags & ~FNM_PERIOD));
+
+          for (--p; n < endp; ++n, no_leading_period = 0)
+            if (FCT (p, n, string_end, no_leading_period, flags2,
+                 &end, alloca_used) == 0)
+              goto found;
+        }
+          else if (c == '/' && (flags & FNM_FILE_NAME))
+        {
+          while (n < string_end && *n != '/')
+            ++n;
+          if (n < string_end && *n == '/'
+              && (FCT (p, n + 1, string_end, flags & FNM_PERIOD, flags,
+                   NULL, alloca_used) == 0))
+            return 0;
+        }
+          else
+        {
+          int flags2 = ((flags & FNM_FILE_NAME)
+                ? flags : (flags & ~FNM_PERIOD));
+
+          if (c == '\\' && !(flags & FNM_NOESCAPE))
+            c = *p;
+          c = FOLD (c);
+          for (--p; n < endp; ++n, no_leading_period = 0)
+            if (FOLD ((unsigned char) *n) == c
+            && (FCT (p, n, string_end, no_leading_period, flags2,
+                 &end, alloca_used) == 0))
+              {
+              found:
+            if (end.pattern == NULL)
+              return 0;
+            break;
+              }
+          if (end.pattern != NULL)
+            {
+              p = end.pattern;
+              n = end.string;
+              no_leading_period = end.no_leading_period;
+              continue;
+            }
+        }
+        }
+
+      /* If we come here no match is possible with the wildcard.  */
+      return FNM_NOMATCH;
+
+    case '[':
+      {
+        /* Nonzero if the sense of the character class is inverted.  */
+        const char *p_init = p;
+        const char *n_init = n;
+        int not;
+        char cold;
+        unsigned char fn;
+
+        if (posixly_correct == 0)
+          posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
+
+        if (n == string_end)
+          return FNM_NOMATCH;
+
+        if (*n == '.' && no_leading_period)
+          return FNM_NOMATCH;
+
+        if (*n == L_func('/') && (flags & FNM_FILE_NAME))
+          /* `/' cannot be matched.  */
+          return FNM_NOMATCH;
+
+        not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
+        if (not)
+          ++p;
+
+        fn = FOLD ((unsigned char) *n);
+
+        c = *p++;
+        for (;;)
+          {
+        if (!(flags & FNM_NOESCAPE) && c == L_func('\\'))
+          {
+            if (*p == L_func('\0'))
+              return FNM_NOMATCH;
+            c = FOLD ((unsigned char) *p);
+            ++p;
+
+            goto normal_bracket;
+          }
+        else if (c == L_func('[') && *p == L_func(':'))
+          {
+            /* Leave room for the null.  */
+            char str[CHAR_CLASS_MAX_LENGTH + 1];
+            size_t c1 = 0;
+#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
+            wctype_t wt;
+#endif
+            const char *startp = p;
+
+            for (;;)
+              {
+            if (c1 == CHAR_CLASS_MAX_LENGTH)
+              /* The name is too long and therefore the pattern
+                 is ill-formed.  */
+              return FNM_NOMATCH;
+
+            c = *++p;
+            if (c == L_func(':') && p[1] == L_func(']'))
+              {
+                p += 2;
+                break;
+              }
+            if (c < L_func('a') || c >= L_func('z'))
+              {
+                /* This cannot possibly be a character class name.
+                   Match it as a normal range.  */
+                p = startp;
+                c = L_func('[');
+                goto normal_bracket;
+              }
+            str[c1++] = c;
+              }
+            str[c1] = L_func('\0');
+
+#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
+            wt = IS_CHAR_CLASS (str);
+            if (wt == 0)
+              /* Invalid character class name.  */
+              return FNM_NOMATCH;
+
+# if defined _LIBC && ! WIDE_CHAR_VERSION
+            /* The following code is glibc specific but does
+               there a good job in speeding up the code since
+               we can avoid the btowc() call.  */
+            if (_ISCTYPE ((unsigned char) *n, wt))
+              goto matched;
+# else
+            if (ISWCTYPE (BTOWC ((unsigned char) *n), wt))
+              goto matched;
+# endif
+#else
+            if ((STREQ (str, L_func("alnum")) && ISALNUM ((unsigned char) *n))
+            || (STREQ (str, L_func("alpha")) && ISALPHA ((unsigned char) *n))
+            || (STREQ (str, L_func("blank")) && ISBLANK ((unsigned char) *n))
+            || (STREQ (str, L_func("cntrl")) && ISCNTRL ((unsigned char) *n))
+            || (STREQ (str, L_func("digit")) && ISDIGIT ((unsigned char) *n))
+            || (STREQ (str, L_func("graph")) && ISGRAPH ((unsigned char) *n))
+            || (STREQ (str, L_func("lower")) && ISLOWER ((unsigned char) *n))
+            || (STREQ (str, L_func("print")) && ISPRINT ((unsigned char) *n))
+            || (STREQ (str, L_func("punct")) && ISPUNCT ((unsigned char) *n))
+            || (STREQ (str, L_func("space")) && ISSPACE ((unsigned char) *n))
+            || (STREQ (str, L_func("upper")) && ISUPPER ((unsigned char) *n))
+            || (STREQ (str, L_func("xdigit")) && ISXDIGIT ((unsigned char) *n)))
+              goto matched;
+#endif
+            c = *p++;
+          }
+#ifdef _LIBC
+        else if (c == L_func('[') && *p == L_func('='))
+          {
+            /* It's important that STR be a scalar variable rather
+               than a one-element array, because GCC (at least 4.9.2
+               -O2 on x86-64) can be confused by the array and
+               diagnose a "used initialized" in a dead branch in the
+               findidx function.  */
+            unsigned char str;
+            uint32_t nrules =
+              _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+            const char *startp = p;
+
+            c = *++p;
+            if (c == L_func('\0'))
+              {
+            p = startp;
+            c = L_func('[');
+            goto normal_bracket;
+              }
+            str = c;
+
+            c = *++p;
+            if (c != L_func('=') || p[1] != L_func(']'))
+              {
+            p = startp;
+            c = L_func('[');
+            goto normal_bracket;
+              }
+            p += 2;
+
+            if (nrules == 0)
+              {
+            if ((unsigned char) *n == str)
+              goto matched;
+              }
+            else
+              {
+            const int32_t *table;
+# if WIDE_CHAR_VERSION
+            const int32_t *weights;
+            const wint_t *extra;
+# else
+            const unsigned char *weights;
+            const unsigned char *extra;
+# endif
+            const int32_t *indirect;
+            int32_t idx;
+            const unsigned char *cp = (const unsigned char *) &str;
+
+# if WIDE_CHAR_VERSION
+            table = (const int32_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEWC);
+            weights = (const int32_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTWC);
+            extra = (const wint_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAWC);
+            indirect = (const int32_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTWC);
+# else
+            table = (const int32_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+            weights = (const unsigned char *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+            extra = (const unsigned char *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+            indirect = (const int32_t *)
+              _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+# endif
+
+            idx = FINDIDX (table, indirect, extra, &cp, 1);
+            if (idx != 0)
+              {
+                /* We found a table entry.  Now see whether the
+                   character we are currently at has the same
+                   equivalance class value.  */
+                int len = weights[idx & 0xffffff];
+                int32_t idx2;
+                const unsigned char *np = (const unsigned char *) n;
+
+                idx2 = FINDIDX (table, indirect, extra,
+                        &np, string_end - n);
+                if (idx2 != 0
+                && (idx >> 24) == (idx2 >> 24)
+                && len == weights[idx2 & 0xffffff])
+                  {
+                int cnt = 0;
+
+                idx &= 0xffffff;
+                idx2 &= 0xffffff;
+
+                while (cnt < len
+                       && (weights[idx + 1 + cnt]
+                       == weights[idx2 + 1 + cnt]))
+                  ++cnt;
+
+                if (cnt == len)
+                  goto matched;
+                  }
+              }
+              }
+
+            c = *p++;
+          }
+#endif
+        else if (c == L_func('\0'))
+          {
+            /* [ unterminated, treat as normal character.  */
+            p = p_init;
+            n = n_init;
+            c = L_func('[');
+            goto normal_match;
+          }
+        else
+          {
+            int is_range = 0;
+
+#ifdef _LIBC
+            int is_seqval = 0;
+
+            if (c == L_func('[') && *p == L_func('.'))
+              {
+            uint32_t nrules =
+              _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+            const char *startp = p;
+            size_t c1 = 0;
+
+            while (1)
+              {
+                c = *++p;
+                if (c == L_func('.') && p[1] == L_func(']'))
+                  {
+                p += 2;
+                break;
+                  }
+                if (c == '\0')
+                  return FNM_NOMATCH;
+                ++c1;
+              }
+
+            /* We have to handling the symbols differently in
+               ranges since then the collation sequence is
+               important.  */
+            is_range = *p == L_func('-') && p[1] != L_func('\0');
+
+            if (nrules == 0)
+              {
+                /* There are no names defined in the collation
+                   data.  Therefore we only accept the trivial
+                   names consisting of the character itself.  */
+                if (c1 != 1)
+                  return FNM_NOMATCH;
+
+                if (!is_range && *n == startp[1])
+                  goto matched;
+
+                cold = startp[1];
+                c = *p++;
+              }
+            else
+              {
+                int32_t table_size;
+                const int32_t *symb_table;
+                const unsigned char *extra;
+                int32_t idx;
+                int32_t elem;
+# if WIDE_CHAR_VERSION
+                char *wextra;
+# endif
+
+                table_size =
+                  _NL_CURRENT_WORD (LC_COLLATE,
+                        _NL_COLLATE_SYMB_HASH_SIZEMB);
+                symb_table = (const int32_t *)
+                  _NL_CURRENT (LC_COLLATE,
+                       _NL_COLLATE_SYMB_TABLEMB);
+                extra = (const unsigned char *)
+                  _NL_CURRENT (LC_COLLATE,
+                       _NL_COLLATE_SYMB_EXTRAMB);
+
+                for (elem = 0; elem < table_size; elem++)
+                  if (symb_table[2 * elem] != 0)
+                {
+                  idx = symb_table[2 * elem + 1];
+                  /* Skip the name of collating element.  */
+                  idx += 1 + extra[idx];
+# if WIDE_CHAR_VERSION
+                  /* Skip the byte sequence of the
+                     collating element.  */
+                  idx += 1 + extra[idx];
+                  /* Adjust for the alignment.  */
+                  idx = (idx + 3) & ~3;
+
+                  wextra = (char *) &extra[idx + 4];
+
+                  if (/* Compare the length of the sequence.  */
+                      c1 == wextra[0]
+                      /* Compare the wide char sequence.  */
+                      && WMEMCMP (startp + 1, &wextra[1],
+                          c1) == 0)
+                    /* Yep, this is the entry.  */
+                    break;
+# else
+                  if (/* Compare the length of the sequence.  */
+                      c1 == extra[idx]
+                      /* Compare the byte sequence.  */
+                      && memcmp (startp + 1,
+                         &extra[idx + 1], c1) == 0)
+                    /* Yep, this is the entry.  */
+                    break;
+# endif
+                }
+
+                if (elem < table_size)
+                  {
+                /* Compare the byte sequence but only if
+                   this is not part of a range.  */
+                if (! is_range
+
+# if WIDE_CHAR_VERSION
+                    && WMEMCMP (n, &wextra[1], c1) == 0
+# else
+                    && memcmp (n, &extra[idx + 1], c1) == 0
+# endif
+                    )
+                  {
+                    n += c1 - 1;
+                    goto matched;
+                  }
+
+                /* Get the collation sequence value.  */
+                is_seqval = 1;
+# if WIDE_CHAR_VERSION
+                cold = wextra[1 + wextra[idx]];
+# else
+                idx += 1 + extra[idx];
+                /* Adjust for the alignment.  */
+                idx = (idx + 3) & ~3;
+                cold = *((int32_t *) &extra[idx]);
+# endif
+
+                c = *p++;
+                  }
+                else if (c1 == 1)
+                  {
+                /* No valid character.  Match it as a
+                   single byte.  */
+                if (!is_range && *n == startp[1])
+                  goto matched;
+
+                cold = startp[1];
+                c = *p++;
+                  }
+                else
+                  return FNM_NOMATCH;
+              }
+              }
+            else
+#endif
+              {
+            c = FOLD (c);
+              normal_bracket:
+
+            /* We have to handling the symbols differently in
+               ranges since then the collation sequence is
+               important.  */
+            is_range = (*p == L_func('-') && p[1] != L_func('\0')
+                    && p[1] != L_func(']'));
+
+            if (!is_range && c == fn)
+              goto matched;
+
+            /* This is needed if we goto normal_bracket; from
+               outside of is_seqval's scope.  */
+            //is_seqval = 0;
+            cold = c;
+            c = *p++;
+              }
+
+            if (c == L_func('-') && *p != L_func(']'))
+              {
+#if _LIBC
+            /* We have to find the collation sequence
+               value for C.  Collation sequence is nothing
+               we can regularly access.  The sequence
+               value is defined by the order in which the
+               definitions of the collation values for the
+               various characters appear in the source
+               file.  A strange concept, nowhere
+               documented.  */
+            uint32_t fcollseq;
+            uint32_t lcollseq;
+            unsigned char cend = *p++;
+
+# if WIDE_CHAR_VERSION
+            /* Search in the `names' array for the characters.  */
+            fcollseq = __collseq_table_lookup (collseq, fn);
+            if (fcollseq == ~((uint32_t) 0))
+              /* XXX We don't know anything about the character
+                 we are supposed to match.  This means we are
+                 failing.  */
+              goto range_not_matched;
+
+            if (is_seqval)
+              lcollseq = cold;
+            else
+              lcollseq = __collseq_table_lookup (collseq, cold);
+# else
+            fcollseq = collseq[fn];
+            lcollseq = is_seqval ? cold : collseq[(unsigned char) cold];
+# endif
+
+            is_seqval = 0;
+            if (cend == L_func('[') && *p == L_func('.'))
+              {
+                uint32_t nrules =
+                  _NL_CURRENT_WORD (LC_COLLATE,
+                        _NL_COLLATE_NRULES);
+                const char *startp = p;
+                size_t c1 = 0;
+
+                while (1)
+                  {
+                c = *++p;
+                if (c == L_func('.') && p[1] == L_func(']'))
+                  {
+                    p += 2;
+                    break;
+                  }
+                if (c == '\0')
+                  return FNM_NOMATCH;
+                ++c1;
+                  }
+
+                if (nrules == 0)
+                  {
+                /* There are no names defined in the
+                   collation data.  Therefore we only
+                   accept the trivial names consisting
+                   of the character itself.  */
+                if (c1 != 1)
+                  return FNM_NOMATCH;
+
+                cend = startp[1];
+                  }
+                else
+                  {
+                int32_t table_size;
+                const int32_t *symb_table;
+                const unsigned char *extra;
+                int32_t idx;
+                int32_t elem;
+# if WIDE_CHAR_VERSION
+                char *wextra;
+# endif
+
+                table_size =
+                  _NL_CURRENT_WORD (LC_COLLATE,
+                            _NL_COLLATE_SYMB_HASH_SIZEMB);
+                symb_table = (const int32_t *)
+                  _NL_CURRENT (LC_COLLATE,
+                           _NL_COLLATE_SYMB_TABLEMB);
+                extra = (const unsigned char *)
+                  _NL_CURRENT (LC_COLLATE,
+                           _NL_COLLATE_SYMB_EXTRAMB);
+
+                for (elem = 0; elem < table_size; elem++)
+                  if (symb_table[2 * elem] != 0)
+                    {
+                      idx = symb_table[2 * elem + 1];
+                      /* Skip the name of collating
+                     element.  */
+                      idx += 1 + extra[idx];
+# if WIDE_CHAR_VERSION
+                      /* Skip the byte sequence of the
+                     collating element.  */
+                      idx += 1 + extra[idx];
+                      /* Adjust for the alignment.  */
+                      idx = (idx + 3) & ~3;
+
+                      wextra = (char *) &extra[idx + 4];
+
+                      if (/* Compare the length of the
+                         sequence.  */
+                      c1 == wextra[0]
+                      /* Compare the wide char sequence.  */
+                      && WMEMCMP (startp + 1, &wextra[1],
+                              c1) == 0)
+                    /* Yep, this is the entry.  */
+                    break;
+# else
+                      if (/* Compare the length of the
+                         sequence.  */
+                      c1 == extra[idx]
+                      /* Compare the byte sequence.  */
+                      && memcmp (startp + 1,
+                             &extra[idx + 1], c1) == 0)
+                    /* Yep, this is the entry.  */
+                    break;
+# endif
+                    }
+
+                if (elem < table_size)
+                  {
+                    /* Get the collation sequence value.  */
+                    is_seqval = 1;
+# if WIDE_CHAR_VERSION
+                    cend = wextra[1 + wextra[idx]];
+# else
+                    idx += 1 + extra[idx];
+                    /* Adjust for the alignment.  */
+                    idx = (idx + 3) & ~3;
+                    cend = *((int32_t *) &extra[idx]);
+# endif
+                  }
+                else if (c1 == 1)
+                  {
+                    cend = startp[1];
+                    c = *p++;
+                  }
+                else
+                  return FNM_NOMATCH;
+                  }
+              }
+            else
+              {
+                if (!(flags & FNM_NOESCAPE) && cend == L_func('\\'))
+                  cend = *p++;
+                if (cend == L_func('\0'))
+                  return FNM_NOMATCH;
+                cend = FOLD (cend);
+              }
+
+            /* XXX It is not entirely clear to me how to handle
+               characters which are not mentioned in the
+               collation specification.  */
+            if (
+# if WIDE_CHAR_VERSION
+                lcollseq == 0xffffffff ||
+# endif
+                lcollseq <= fcollseq)
+              {
+                /* We have to look at the upper bound.  */
+                uint32_t hcollseq;
+
+                if (is_seqval)
+                  hcollseq = cend;
+                else
+                  {
+# if WIDE_CHAR_VERSION
+                hcollseq =
+                  __collseq_table_lookup (collseq, cend);
+                if (hcollseq == ~((uint32_t) 0))
+                  {
+                    /* Hum, no information about the upper
+                       bound.  The matching succeeds if the
+                       lower bound is matched exactly.  */
+                    if (lcollseq != fcollseq)
+                      goto range_not_matched;
+
+                    goto matched;
+                  }
+# else
+                hcollseq = collseq[cend];
+# endif
+                  }
+
+                if (lcollseq <= hcollseq && fcollseq <= hcollseq)
+                  goto matched;
+              }
+# if WIDE_CHAR_VERSION
+              range_not_matched:
+# endif
+#else
+            /* We use a boring value comparison of the character
+               values.  This is better than comparing using
+               `strcoll' since the latter would have surprising
+               and sometimes fatal consequences.  */
+            unsigned char cend = *p++;
+
+            if (!(flags & FNM_NOESCAPE) && cend == L_func('\\'))
+              cend = *p++;
+            if (cend == L_func('\0'))
+              return FNM_NOMATCH;
+
+            /* It is a range.  */
+            if (cold <= fn && fn <= cend)
+              goto matched;
+#endif
+
+            c = *p++;
+              }
+          }
+
+        if (c == L_func(']'))
+          break;
+          }
+
+        if (!not)
+          return FNM_NOMATCH;
+        break;
+
+      matched:
+        /* Skip the rest of the [...] that already matched.  */
+        while ((c = *p++) != L_func (']'))
+          {
+        if (c == L_func('\0'))
+          /* [... (unterminated) loses.  */
+          return FNM_NOMATCH;
+
+        if (!(flags & FNM_NOESCAPE) && c == L_func('\\'))
+          {
+            if (*p == L_func('\0'))
+              return FNM_NOMATCH;
+            /* XXX 1003.2d11 is unclear if this is right.  */
+            ++p;
+          }
+        else if (c == L_func('[') && *p == L_func(':'))
+          {
+            int c1 = 0;
+            const char *startp = p;
+
+            while (1)
+              {
+            c = *++p;
+            if (++c1 == CHAR_CLASS_MAX_LENGTH)
+              return FNM_NOMATCH;
+
+            if (*p == L_func(':') && p[1] == L_func(']'))
+              break;
+
+            if (c < L_func('a') || c >= L_func('z'))
+              {
+                p = startp - 2;
+                break;
+              }
+              }
+            p += 2;
+          }
+        else if (c == L_func('[') && *p == L_func('='))
+          {
+            c = *++p;
+            if (c == L_func('\0'))
+              return FNM_NOMATCH;
+            c = *++p;
+            if (c != L_func('=') || p[1] != L_func(']'))
+              return FNM_NOMATCH;
+            p += 2;
+          }
+        else if (c == L_func('[') && *p == L_func('.'))
+          {
+            while (1)
+              {
+            c = *++p;
+            if (c == L_func('\0'))
+              return FNM_NOMATCH;
+
+            if (c == L_func('.') && p[1] == L_func(']'))
+              break;
+              }
+            p += 2;
+          }
+          }
+        if (not)
+          return FNM_NOMATCH;
+      }
+      break;
+
+    case L_func('+'):
+    case L_func('@'):
+    case L_func('!'):
+      if (__builtin_expect (flags & FNM_EXTMATCH, 0) && *p == '(')
+        {
+          int res = EXT (c, p, n, string_end, no_leading_period, flags,
+                 alloca_used);
+          if (res != -1)
+        return res;
+        }
+      goto normal_match;
+
+    case L_func('/'):
+      if (NO_LEADING_PERIOD (flags))
+        {
+          if (n == string_end || c != (unsigned char) *n)
+        return FNM_NOMATCH;
+
+          new_no_leading_period = 1;
+          break;
+        }
+      /* FALLTHROUGH */
+    default:
+    normal_match:
+      if (n == string_end || c != FOLD ((unsigned char) *n))
+        return FNM_NOMATCH;
+    }
+
+      no_leading_period = new_no_leading_period;
+      ++n;
+    }
+
+  if (n == string_end)
+    return 0;
+
+  if ((flags & FNM_LEADING_DIR) && n != string_end && *n == L_func('/'))
+    /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz".  */
+    return 0;
+
+  return FNM_NOMATCH;
+}
+
+
+const char *
+END (const char *pattern)
+{
+  const char *p = pattern;
+
+  while (1)
+    if (*++p == L_func('\0'))
+      /* This is an invalid pattern.  */
+      return pattern;
+    else if (*p == L_func('['))
+      {
+    /* Handle brackets special.  */
+    if (posixly_correct == 0)
+      posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
+
+    /* Skip the not sign.  We have to recognize it because of a possibly
+       following ']'.  */
+    if (*++p == L_func('!') || (posixly_correct < 0 && *p == L_func('^')))
+      ++p;
+    /* A leading ']' is recognized as such.  */
+    if (*p == L_func(']'))
+      ++p;
+    /* Skip over all characters of the list.  */
+    while (*p != L_func(']'))
+      if (*p++ == L_func('\0'))
+        /* This is no valid pattern.  */
+        return pattern;
+      }
+    else if ((*p == L_func('?') || *p == L_func('*') || *p == L_func('+') || *p == L_func('@')
+          || *p == L_func('!')) && p[1] == L_func('('))
+      {
+    p = END (p + 1);
+    if (*p == L_func('\0'))
+      /* This is an invalid pattern.  */
+      return pattern;
+      }
+    else if (*p == L_func(')'))
+      break;
+
+  return p + 1;
+}
+
+
+static int
+EXT (int opt, const char *pattern, const char *string, const char *string_end,
+     int no_leading_period, int flags, size_t alloca_used)
+{
+  const char *startp;
+  int level;
+  struct patternlist
+  {
+    struct patternlist *next;
+    char malloced;
+    char str[0];
+  } *list = NULL;
+  struct patternlist **lastp = &list;
+  size_t pattern_len = STRLEN (pattern);
+  int any_malloced = 0;
+  const char *p;
+  const char *rs;
+  int retval = 0;
+
+  /* Parse the pattern.  Store the individual parts in the list.  */
+  level = 0;
+  for (startp = p = pattern + 1; level >= 0; ++p)
+    if (*p == L_func('\0'))
+      {
+    /* This is an invalid pattern.  */
+    retval = -1;
+    goto out;
+      }
+    else if (*p == L_func('['))
+      {
+    /* Handle brackets special.  */
+    if (posixly_correct == 0)
+      posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
+
+    /* Skip the not sign.  We have to recognize it because of a possibly
+       following ']'.  */
+    if (*++p == L_func('!') || (posixly_correct < 0 && *p == L_func('^')))
+      ++p;
+    /* A leading ']' is recognized as such.  */
+    if (*p == L_func(']'))
+      ++p;
+    /* Skip over all characters of the list.  */
+    while (*p != L_func(']'))
+      if (*p++ == L_func('\0'))
+        {
+          /* This is no valid pattern.  */
+          retval = -1;
+          goto out;
+        }
+      }
+    else if ((*p == L_func('?') || *p == L_func('*') || *p == L_func('+') || *p == L_func('@')
+          || *p == L_func('!')) && p[1] == L_func('('))
+      /* Remember the nesting level.  */
+      ++level;
+    else if (*p == L_func(')'))
+      {
+    if (level-- == 0)
+      {
+        /* This means we found the end of the pattern.  */
+#define NEW_PATTERN \
+        struct patternlist *newp;                         \
+        size_t slen = (opt == '?' || opt == '@'             \
+               ? pattern_len : (p - startp + 1));             \
+        slen = sizeof (struct patternlist) + (slen * sizeof (char));      \
+        int malloced = ! __libc_use_alloca (alloca_used + slen);          \
+        if (__builtin_expect (malloced, 0))                   \
+          {                                   \
+        newp = malloc (slen);                         \
+        if (newp == NULL)                         \
+          {                               \
+            retval = -2;                          \
+            goto out;                             \
+          }                               \
+        any_malloced = 1;                         \
+          }                                   \
+        else                                  \
+          newp = alloca_account (slen, alloca_used);              \
+        newp->next = NULL;                            \
+        newp->malloced = malloced;                        \
+        *((char *) MEMPCPY (newp->str, startp, p - startp)) = L_func('\0');    \
+        *lastp = newp;                            \
+        lastp = &newp->next
+        NEW_PATTERN;
+      }
+      }
+    else if (*p == L_func('|'))
+      {
+    if (level == 0)
+      {
+        NEW_PATTERN;
+        startp = p + 1;
+      }
+      }
+  assert (list != NULL);
+  assert (p[-1] == L_func(')'));
+#undef NEW_PATTERN
+
+  switch (opt)
+    {
+    case '*':
+      if (FCT (p, string, string_end, no_leading_period, flags, NULL,
+           alloca_used) == 0)
+    goto success;
+      /* FALLTHROUGH */
+
+    case '+':
+      do
+    {
+      for (rs = string; rs <= string_end; ++rs)
+        /* First match the prefix with the current pattern with the
+           current pattern.  */
+        if (FCT (list->str, string, rs, no_leading_period,
+             flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD,
+             NULL, alloca_used) == 0
+        /* This was successful.  Now match the rest with the rest
+           of the pattern.  */
+        && (FCT (p, rs, string_end,
+             rs == string
+             ? no_leading_period
+             : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
+             flags & FNM_FILE_NAME
+             ? flags : flags & ~FNM_PERIOD, NULL, alloca_used) == 0
+            /* This didn't work.  Try the whole pattern.  */
+            || (rs != string
+            && FCT (pattern - 1, rs, string_end,
+                rs == string
+                ? no_leading_period
+                : (rs[-1] == '/' && NO_LEADING_PERIOD (flags)
+                   ? 1 : 0),
+                flags & FNM_FILE_NAME
+                ? flags : flags & ~FNM_PERIOD, NULL,
+                alloca_used) == 0)))
+          /* It worked.  Signal success.  */
+          goto success;
+    }
+      while ((list = list->next) != NULL);
+
+      /* None of the patterns lead to a match.  */
+      retval = FNM_NOMATCH;
+      break;
+
+    case '?':
+      if (FCT (p, string, string_end, no_leading_period, flags, NULL,
+           alloca_used) == 0)
+    goto success;
+      /* FALLTHROUGH */
+
+    case '@':
+        do {
+            /* I cannot believe it but `strcat' is actually acceptable
+             here.  Match the entire string with the prefix from the
+             pattern list and the rest of the pattern following the
+             pattern list.  */
+            if(FCT(STRCAT(list->str, p), string, string_end,
+                    no_leading_period,
+                    flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD,
+                    NULL, alloca_used) == 0)
+                /* It worked.  Signal success.  */
+                goto success;
+        } while((list = list->next) != NULL);
+
+        /* None of the patterns lead to a match.  */
+        retval = FNM_NOMATCH;
+        break;
+
+    case '!':
+      for (rs = string; rs <= string_end; ++rs)
+    {
+      struct patternlist *runp;
+
+      for (runp = list; runp != NULL; runp = runp->next)
+        if (FCT (runp->str, string, rs,  no_leading_period,
+             flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD,
+             NULL, alloca_used) == 0)
+          break;
+
+      /* If none of the patterns matched see whether the rest does.  */
+      if (runp == NULL
+          && (FCT (p, rs, string_end,
+               rs == string
+               ? no_leading_period
+               : rs[-1] == '/' && NO_LEADING_PERIOD (flags) ? 1 : 0,
+               flags & FNM_FILE_NAME ? flags : flags & ~FNM_PERIOD,
+               NULL, alloca_used) == 0))
+        /* This is successful.  */
+        goto success;
+    }
+
+      /* None of the patterns together with the rest of the pattern
+     lead to a match.  */
+      retval = FNM_NOMATCH;
+      break;
+
+    default:
+      assert (! "Invalid extended matching operator");
+      retval = -1;
+      break;
+    }
+
+ success:
+ out:
+  if (any_malloced)
+    while (list != NULL)
+      {
+    struct patternlist *old = list;
+    list = list->next;
+    if (old->malloced)
+      free (old);
+      }
+
+  return retval;
+}
+
+
+#undef FOLD
+#undef FCT
+#undef EXT
+#undef END
+#undef STRUCT
+#undef MEMPCPY
+#undef MEMCHR
+#undef STRCOLL
+#undef STRLEN
+#undef STRCAT
+#undef L
+#undef BTOWC
+#undef WIDE_CHAR_VERSION
+#undef FINDIDX
diff --git a/dap-sdk/core/include/dap_strfuncs.h b/dap-sdk/core/include/dap_strfuncs.h
index c581294cb9df00d18df510e4425c3e4f8787414e..79f00942eaeb5050de51017e3d9ab36b2cd165e6 100755
--- a/dap-sdk/core/include/dap_strfuncs.h
+++ b/dap-sdk/core/include/dap_strfuncs.h
@@ -13,6 +13,7 @@
 #include <string.h>
 
 #include "dap_math_ops.h"
+#include "dap_fnmatch.h"
 
 #define dap_return_if_fail(expr)            {if(!(expr)) {return;}}
 #define dap_return_val_if_fail(expr,val)    {if(!(expr)) {return (val);}}
diff --git a/dap-sdk/core/src/dap_common.c b/dap-sdk/core/src/dap_common.c
index c4edcbb8a62a8060a07eb475f1f2d1d03105846a..ea8ffebd01d7250b93e8f5aae6ed0f936c6efa63 100755
--- a/dap-sdk/core/src/dap_common.c
+++ b/dap-sdk/core/src/dap_common.c
@@ -21,6 +21,7 @@
     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/>.
 */
+#define _POSIX_THREAD_SAFE_FUNCTIONS
 #include <stdio.h>
 #include <stdlib.h>
 #include <time.h> /* 'nanosleep' */
@@ -331,16 +332,21 @@ void _log_it(const char *a_log_tag, enum dap_log_level a_ll, const char *a_fmt,
     if ( a_ll < s_dap_log_level || a_ll >= 16 || !a_log_tag )
         return;
     log_str_t *l_log_string = DAP_NEW_Z(log_str_t);
-    strncpy(l_log_string->str, s_ansi_seq_color[a_ll],sizeof (l_log_string->str)-1);
+    size_t offset2 = sizeof(l_log_string->str) - 2;
+    strncpy(l_log_string->str, s_ansi_seq_color[a_ll], offset2);
     l_log_string->offset = s_ansi_seq_color_len[a_ll];
     s_update_log_time(l_log_string->str + l_log_string->offset);
     size_t offset = strlen(l_log_string->str);
-    offset += dap_snprintf(l_log_string->str + offset, sizeof (l_log_string->str) -offset, "%s[%s%s", s_log_level_tag[a_ll], a_log_tag, "] ");
+    offset += dap_snprintf(l_log_string->str + offset, offset2, "%s[%s%s", s_log_level_tag[a_ll], a_log_tag, "] ");
+    offset2 -= offset;
     va_list va;
     va_start( va, a_fmt );
-    offset += dap_vsnprintf(l_log_string->str + offset,sizeof (l_log_string->str) -offset, a_fmt, va);
+    size_t l_offset = dap_vsnprintf(l_log_string->str + offset, offset2, a_fmt, va);
+    offset = (l_offset < offset2) ? offset + l_offset : offset;
+    offset2 = (l_offset < offset2) ? offset2 - offset : 0;
     va_end( va );
-    memcpy(&l_log_string->str[offset], "\n", 1);
+    char *dummy = (offset2 == 0) ? memcpy(&l_log_string->str[sizeof(l_log_string->str) - 6], "...\n\0", 5)
+        : memcpy(&l_log_string->str[offset], "\n", 1);
     pthread_mutex_lock(&s_log_mutex);
     DL_APPEND(s_log_buffer, l_log_string);
     ++s_log_count;
diff --git a/dap-sdk/core/src/dap_file_utils.c b/dap-sdk/core/src/dap_file_utils.c
index 8a666b8900eaafa1ad63be8eb47083da5f3b3b94..db0b71c5849f498fc4dce44096e27631fffaf066 100755
--- a/dap-sdk/core/src/dap_file_utils.c
+++ b/dap-sdk/core/src/dap_file_utils.c
@@ -120,14 +120,15 @@ bool dap_dir_test(const char * a_dir_path)
  */
 int dap_mkdir_with_parents(const char *a_dir_path)
 {
-
-    char *path, *p;
     // validation of a pointer
     if(a_dir_path == NULL || a_dir_path[0] == '\0') {
         errno = EINVAL;
         return -1;
     }
-    path = strdup(a_dir_path);
+    char path[strlen(a_dir_path) + 1];
+    memset(path, '\0', strlen(a_dir_path) + 1);
+    memcpy(path, a_dir_path, strlen(a_dir_path));
+    char *p;
     // skip the root component if it is present, i.e. the "/" in Unix or "C:\" in Windows
 #ifdef _WIN32
     if(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z'))
@@ -158,7 +159,6 @@ int dap_mkdir_with_parents(const char *a_dir_path)
             int result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
 #endif
             if(result == -1) {
-                free(path);
                 errno = ENOTDIR;
                 return -1;
             }
@@ -169,8 +169,6 @@ int dap_mkdir_with_parents(const char *a_dir_path)
                 p++;
         }
     } while(p);
-
-    free(path);
     return 0;
 }
 
diff --git a/dap-sdk/core/src/dap_fnmatch.c b/dap-sdk/core/src/dap_fnmatch.c
new file mode 100644
index 0000000000000000000000000000000000000000..f89fa536eff2959625980c269d6dd60e5f42d784
--- /dev/null
+++ b/dap-sdk/core/src/dap_fnmatch.c
@@ -0,0 +1,435 @@
+/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "dap_fnmatch.h"
+
+
+
+/* For platform which support the ISO C amendement 1 functionality we
+   support user defined character classes.  */
+/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>.  */
+//# include <wchar.h>
+//# include <wctype.h>
+
+# define __libc_use_alloca(n) false
+# define alloca_account(len, avar) ((void) (len), (void) (avar), (void *) 0)
+# define extend_alloca_account(buf, len, newlen, avar) \
+    ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0)
+
+
+
+/* We need some of the locale data (the collation sequence information)
+   but there is no interface to get this information in general.  Therefore
+   we support a correct implementation only in glibc.  */
+#ifdef _LIBC
+# include "../locale/localeinfo.h"
+# include "../locale/coll-lookup.h"
+# include <shlib-compat.h>
+
+# define CONCAT(a,b) __CONCAT(a,b)
+# define mbsrtowcs __mbsrtowcs
+# define fnmatch __fnmatch
+extern int fnmatch (const char *pattern, const char *string, int flags);
+#endif
+
+/* We often have to test for FNM_FILE_NAME and FNM_PERIOD being both set.  */
+#define NO_LEADING_PERIOD(flags) \
+  ((flags & (FNM_FILE_NAME | FNM_PERIOD)) == (FNM_FILE_NAME | FNM_PERIOD))
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+//#if defined _LIBC || !defined __GNU_LIBRARY__
+
+# if defined STDC_HEADERS || !defined isascii
+#  define ISASCII(c) 1
+# else
+#  define ISASCII(c) isascii(c)
+# endif
+
+# ifdef isblank
+#  define ISBLANK(c) (ISASCII (c) && isblank (c))
+# else
+#  define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+# endif
+# ifdef isgraph
+#  define ISGRAPH(c) (ISASCII (c) && isgraph (c))
+# else
+#  define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
+# endif
+
+# define ISPRINT(c) (ISASCII (c) && isprint (c))
+# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
+# define ISALNUM(c) (ISASCII (c) && isalnum (c))
+# define ISALPHA(c) (ISASCII (c) && isalpha (c))
+# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
+# define ISLOWER(c) (ISASCII (c) && islower (c))
+# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
+# define ISSPACE(c) (ISASCII (c) && isspace (c))
+# define ISUPPER(c) (ISASCII (c) && isupper (c))
+# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
+
+# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
+
+# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
+/* The GNU C library provides support for user-defined character classes
+   and the functions from ISO C amendement 1.  */
+#  ifdef CHARCLASS_NAME_MAX
+#   define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
+#  else
+/* This shouldn't happen but some implementation might still have this
+   problem.  Use a reasonable default value.  */
+#   define CHAR_CLASS_MAX_LENGTH 256
+#  endif
+
+#  ifdef _LIBC
+#   define IS_CHAR_CLASS(string) __wctype (string)
+#  else
+#   define IS_CHAR_CLASS(string) wctype (string)
+#  endif
+
+#  ifdef _LIBC
+#   define ISWCTYPE(WC, WT) __iswctype (WC, WT)
+#  else
+#   define ISWCTYPE(WC, WT) iswctype (WC, WT)
+#  endif
+
+#  if (HAVE_MBSTATE_T && HAVE_MBSRTOWCS) || _LIBC
+/* In this case we are implementing the multibyte character handling.  */
+#   define HANDLE_MULTIBYTE 1
+#  endif
+
+# else
+#  define CHAR_CLASS_MAX_LENGTH  6 /* Namely, `xdigit'.  */
+
+#  define IS_CHAR_CLASS(string)                           \
+   (STREQ (string, "alpha") || STREQ (string, "upper")                \
+    || STREQ (string, "lower") || STREQ (string, "digit")             \
+    || STREQ (string, "alnum") || STREQ (string, "xdigit")            \
+    || STREQ (string, "space") || STREQ (string, "print")             \
+    || STREQ (string, "punct") || STREQ (string, "graph")             \
+    || STREQ (string, "cntrl") || STREQ (string, "blank"))
+# endif
+
+
+/* This function doesn't exist on most systems.  */
+
+# if !defined HAVE___STRCHRNUL && !defined _LIBC
+static char *
+__strchrnul (const char *s, int c)
+{
+  char *result = strchr (s, c);
+  if (result == NULL)
+    result = strchr (s, '\0');
+  return result;
+}
+# endif
+
+# if HANDLE_MULTIBYTE && !defined HAVE___STRCHRNUL && !defined _LIBC
+static wchar_t *
+__wcschrnul (const wchar_t *s, wint_t c)
+{
+  wchar_t *result = wcschr (s, c);
+  if (result == NULL)
+    result = wcschr (s, '\0');
+  return result;
+}
+# endif
+
+/* Note that this evaluates C many times.  */
+# ifdef _LIBC
+#  define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
+# else
+#  define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
+# endif
+# define FCT    internal_fnmatch
+# define EXT    ext_match
+# define END    end_pattern
+# define STRUCT fnmatch_struct
+//# define L(CS)  CS
+# ifdef _LIBC
+#  define BTOWC(C)  __btowc (C)
+# else
+#  define BTOWC(C)  btowc (C)
+# endif
+# define STRLEN(S) strlen(S)
+# define STRCAT(D, S) strcat (D, S)
+# define MEMPCPY(D, S, N) mempcpy (D, S, N)
+# define MEMCHR(S, C, N) memchr (S, C, N)
+# define STRCOLL(S1, S2) strcoll (S1, S2)
+# define WIDE_CHAR_VERSION 0
+//# include <locale/weight.h>
+# define FINDIDX findidx
+# include "dap_fnmatch_loop.h"
+
+
+# if HANDLE_MULTIBYTE
+/* Note that this evaluates C many times.  */
+#  ifdef _LIBC
+#   define FOLD(c) ((flags & FNM_CASEFOLD) ? __towlower (c) : (c))
+#  else
+#   define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? towlower (c) : (c))
+#  endif
+#  define CHAR  wchar_t
+#  define UCHAR wint_t
+#  define INT   wint_t
+#  define FCT   internal_fnwmatch
+#  define EXT   ext_wmatch
+#  define END   end_wpattern
+#  define STRUCT fnwmatch_struct
+#  define L(CS) L##CS
+#  define BTOWC(C)  (C)
+#  define STRLEN(S) __wcslen (S)
+#  define STRCAT(D, S) __wcscat (D, S)
+#  define MEMPCPY(D, S, N) __wmempcpy (D, S, N)
+#  define MEMCHR(S, C, N) __wmemchr (S, C, N)
+#  define STRCOLL(S1, S2) wcscoll (S1, S2)
+#  ifdef _LIBC
+#   define WMEMCMP(S1, S2, N) __wmemcmp (S1, S2, N)
+#  else
+#   define WMEMCMP(S1, S2, N) wmemcmp (S1, S2, N)
+#  endif
+#  define WIDE_CHAR_VERSION 1
+/* Change the name the header defines so it doesn't conflict with
+   the <locale/weight.h> version included above.  */
+#  define findidx findidxwc
+#  include <locale/weightwc.h>
+#  undef findidx
+#  define FINDIDX findidxwc
+
+#  undef IS_CHAR_CLASS
+/* We have to convert the wide character string in a multibyte string.  But
+   we know that the character class names consist of alphanumeric characters
+   from the portable character set, and since the wide character encoding
+   for a member of the portable character set is the same code point as
+   its single-byte encoding, we can use a simplified method to convert the
+   string to a multibyte character string.  */
+static wctype_t
+is_char_class (const wchar_t *wcs)
+{
+  char s[CHAR_CLASS_MAX_LENGTH + 1];
+  char *cp = s;
+
+  do
+    {
+      /* Test for a printable character from the portable character set.  */
+#  ifdef _LIBC
+      if (*wcs < 0x20 || *wcs > 0x7e
+      || *wcs == 0x24 || *wcs == 0x40 || *wcs == 0x60)
+    return (wctype_t) 0;
+#  else
+      switch (*wcs)
+    {
+    case L' ': case L'!': case L'"': case L'#': case L'%':
+    case L'&': case L'\'': case L'(': case L')': case L'*':
+    case L'+': case L',': case L'-': case L'.': case L'/':
+    case L'0': case L'1': case L'2': case L'3': case L'4':
+    case L'5': case L'6': case L'7': case L'8': case L'9':
+    case L':': case L';': case L'<': case L'=': case L'>':
+    case L'?':
+    case L'A': case L'B': case L'C': case L'D': case L'E':
+    case L'F': case L'G': case L'H': case L'I': case L'J':
+    case L'K': case L'L': case L'M': case L'N': case L'O':
+    case L'P': case L'Q': case L'R': case L'S': case L'T':
+    case L'U': case L'V': case L'W': case L'X': case L'Y':
+    case L'Z':
+    case L'[': case L'\\': case L']': case L'^': case L'_':
+    case L'a': case L'b': case L'c': case L'd': case L'e':
+    case L'f': case L'g': case L'h': case L'i': case L'j':
+    case L'k': case L'l': case L'm': case L'n': case L'o':
+    case L'p': case L'q': case L'r': case L's': case L't':
+    case L'u': case L'v': case L'w': case L'x': case L'y':
+    case L'z': case L'{': case L'|': case L'}': case L'~':
+      break;
+    default:
+      return (wctype_t) 0;
+    }
+#  endif
+
+      /* Avoid overrunning the buffer.  */
+      if (cp == s + CHAR_CLASS_MAX_LENGTH)
+    return (wctype_t) 0;
+
+      *cp++ = (char) *wcs++;
+    }
+  while (*wcs != L'\0');
+
+  *cp = '\0';
+
+#  ifdef _LIBC
+  return __wctype (s);
+#  else
+  return wctype (s);
+#  endif
+}
+#  define IS_CHAR_CLASS(string) is_char_class (string)
+
+#  include "fnmatch_loop.c"
+# endif
+
+
+int dap_fnmatch (const char *pattern, const char *string, int flags)
+{
+# if HANDLE_MULTIBYTE
+  if (__builtin_expect (MB_CUR_MAX, 1) != 1)
+    {
+      mbstate_t ps;
+      size_t n;
+      const char *p;
+      wchar_t *wpattern_malloc = NULL;
+      wchar_t *wpattern;
+      wchar_t *wstring_malloc = NULL;
+      wchar_t *wstring;
+      size_t alloca_used = 0;
+
+      /* Convert the strings into wide characters.  */
+      memset (&ps, '\0', sizeof (ps));
+      p = pattern;
+#ifdef _LIBC
+      n = __strnlen (pattern, 1024);
+#else
+      n = strlen (pattern);
+#endif
+      if (__glibc_likely (n < 1024))
+    {
+      wpattern = (wchar_t *) alloca_account ((n + 1) * sizeof (wchar_t),
+                         alloca_used);
+      n = mbsrtowcs (wpattern, &p, n + 1, &ps);
+      if (__glibc_unlikely (n == (size_t) -1))
+        /* Something wrong.
+           XXX Do we have to set `errno' to something which mbsrtows hasn't
+           already done?  */
+        return -1;
+      if (p)
+        {
+          memset (&ps, '\0', sizeof (ps));
+          goto prepare_wpattern;
+        }
+    }
+      else
+    {
+    prepare_wpattern:
+      n = mbsrtowcs (NULL, &pattern, 0, &ps);
+      if (__glibc_unlikely (n == (size_t) -1))
+        /* Something wrong.
+           XXX Do we have to set `errno' to something which mbsrtows hasn't
+           already done?  */
+        return -1;
+      if (__glibc_unlikely (n >= (size_t) -1 / sizeof (wchar_t)))
+        {
+          __set_errno (ENOMEM);
+          return -2;
+        }
+      wpattern_malloc = wpattern
+        = (wchar_t *) malloc ((n + 1) * sizeof (wchar_t));
+      assert (mbsinit (&ps));
+      if (wpattern == NULL)
+        return -2;
+      (void) mbsrtowcs (wpattern, &pattern, n + 1, &ps);
+    }
+
+      assert (mbsinit (&ps));
+#ifdef _LIBC
+      n = __strnlen (string, 1024);
+#else
+      n = strlen (string);
+#endif
+      p = string;
+      if (__glibc_likely (n < 1024))
+    {
+      wstring = (wchar_t *) alloca_account ((n + 1) * sizeof (wchar_t),
+                        alloca_used);
+      n = mbsrtowcs (wstring, &p, n + 1, &ps);
+      if (__glibc_unlikely (n == (size_t) -1))
+        {
+          /* Something wrong.
+         XXX Do we have to set `errno' to something which
+         mbsrtows hasn't already done?  */
+        free_return:
+          free (wpattern_malloc);
+          return -1;
+        }
+      if (p)
+        {
+          memset (&ps, '\0', sizeof (ps));
+          goto prepare_wstring;
+        }
+    }
+      else
+    {
+    prepare_wstring:
+      n = mbsrtowcs (NULL, &string, 0, &ps);
+      if (__glibc_unlikely (n == (size_t) -1))
+        /* Something wrong.
+           XXX Do we have to set `errno' to something which mbsrtows hasn't
+           already done?  */
+        goto free_return;
+      if (__glibc_unlikely (n >= (size_t) -1 / sizeof (wchar_t)))
+        {
+          free (wpattern_malloc);
+          __set_errno (ENOMEM);
+          return -2;
+        }
+
+      wstring_malloc = wstring
+        = (wchar_t *) malloc ((n + 1) * sizeof (wchar_t));
+      if (wstring == NULL)
+        {
+          free (wpattern_malloc);
+          return -2;
+        }
+      assert (mbsinit (&ps));
+      (void) mbsrtowcs (wstring, &string, n + 1, &ps);
+    }
+
+      int res = internal_fnwmatch (wpattern, wstring, wstring + n,
+                   flags & FNM_PERIOD, flags, NULL,
+                   alloca_used);
+
+      free (wstring_malloc);
+      free (wpattern_malloc);
+
+      return res;
+    }
+# endif  /* mbstate_t and mbsrtowcs or _LIBC.  */
+
+  return internal_fnmatch (pattern, string, string + strlen (string),
+               flags & FNM_PERIOD, flags, NULL, 0);
+}
+
+# ifdef _LIBC
+#  undef fnmatch
+versioned_symbol (libc, __fnmatch, fnmatch, GLIBC_2_2_3);
+#  if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_2_3)
+strong_alias (__fnmatch, __fnmatch_old)
+compat_symbol (libc, __fnmatch_old, fnmatch, GLIBC_2_0);
+#  endif
+libc_hidden_ver (__fnmatch, fnmatch)
+# endif
+
+//#endif  /* _LIBC or not __GNU_LIBRARY__.  */
diff --git a/dap-sdk/core/test/rpmalloc/test/main.c b/dap-sdk/core/test/rpmalloc/test/main.c
index f8fccef20dfb68072c7381ba149226f7367be704..a6e13766263e3aa7992e36f4865b228ee37b1ead 100644
--- a/dap-sdk/core/test/rpmalloc/test/main.c
+++ b/dap-sdk/core/test/rpmalloc/test/main.c
@@ -805,7 +805,7 @@ main(int argc, char** argv) {
 #endif
 
 #ifdef _WIN32
-#include <Windows.h>
+#include <windows.h>
 
 static void
 test_initialize(void) {
diff --git a/dap-sdk/core/test/rpmalloc/test/thread.c b/dap-sdk/core/test/rpmalloc/test/thread.c
index 17b55035d1a54150db64cee553f516e9cf6f4850..21729f0c37b5e3dacdc8379605fe8147f74f922f 100644
--- a/dap-sdk/core/test/rpmalloc/test/thread.c
+++ b/dap-sdk/core/test/rpmalloc/test/thread.c
@@ -13,7 +13,7 @@
 #endif
 
 #ifdef _WIN32
-#  include <Windows.h>
+#  include <windows.h>
 #  include <process.h>
 
 static unsigned __stdcall
diff --git a/dap-sdk/core/test/rpmalloc/test/timer.c b/dap-sdk/core/test/rpmalloc/test/timer.c
index 2ceb4b7ec9689ef6fe5118015d33fdc1e860c262..db138712d08966ce9b7630a726cfc47c08ae107f 100644
--- a/dap-sdk/core/test/rpmalloc/test/timer.c
+++ b/dap-sdk/core/test/rpmalloc/test/timer.c
@@ -2,7 +2,7 @@
 #include <timer.h>
 
 #if defined(_WIN32)
-#  include <Windows.h>
+#  include <windows.h>
 #elif defined(__APPLE__)
 #  include <mach/mach_time.h>
 static mach_timebase_info_data_t _time_info;
diff --git a/dap-sdk/crypto/include/dap_cert.h b/dap-sdk/crypto/include/dap_cert.h
index 9ba4e00ec17c8232b469a25c2c2e5e89f2b4e0d1..0dee3bf8858f543c7878ac73e8eaf341be497b25 100755
--- a/dap-sdk/crypto/include/dap_cert.h
+++ b/dap-sdk/crypto/include/dap_cert.h
@@ -82,6 +82,7 @@ void dap_cert_dump(dap_cert_t * a_cert);
 dap_pkey_t * dap_cert_to_pkey(dap_cert_t * a_cert);
 
 dap_cert_t * dap_cert_find_by_name(const char * a_cert_name);
+dap_list_t *dap_cert_get_all_mem();
 
 dap_sign_t * dap_cert_sign(dap_cert_t * a_cert, const void * a_data, size_t a_data_size, size_t a_output_size_wished );
 
diff --git a/dap-sdk/crypto/src/dap_cert.c b/dap-sdk/crypto/src/dap_cert.c
index 5f5da67741246f0be603313d4a33cf54a2bc8c82..2916d19b6707d73da4597d8231f928bc3d99400e 100755
--- a/dap-sdk/crypto/src/dap_cert.c
+++ b/dap-sdk/crypto/src/dap_cert.c
@@ -300,6 +300,15 @@ dap_cert_t * dap_cert_find_by_name(const char * a_cert_name)
         }
 }
 
+dap_list_t *dap_cert_get_all_mem()
+{
+    dap_list_t *l_ret = NULL;
+    dap_cert_item_t *l_cert_item = NULL, *l_cert_tmp;
+    HASH_ITER(hh, s_certs, l_cert_item, l_cert_tmp) {
+        l_ret = dap_list_append(l_ret, l_cert_item->cert);
+    }
+    return l_ret;
+}
 
 /**
  * @brief dap_cert_new
diff --git a/dap-sdk/crypto/src/dap_enc_dilithium.c b/dap-sdk/crypto/src/dap_enc_dilithium.c
index 6d59b941887655b5b97393a19d6963add2914304..b8509aabdff1a00d12990ebdadc0c2ce8e426c08 100755
--- a/dap-sdk/crypto/src/dap_enc_dilithium.c
+++ b/dap-sdk/crypto/src/dap_enc_dilithium.c
@@ -40,9 +40,7 @@ void dap_enc_sig_dilithium_key_new_generate(struct dap_enc_key * key, const void
 
     int32_t retcode;
 
-    int dilithium_type = (seed && seed_size >= sizeof(uint8_t)) ? ((uint8_t*)seed)[0] % (DILITHIUM_MAX_SECURITY + 1) :
-                                                              DILITHIUM_MIN_SIZE;
-    dap_enc_sig_dilithium_set_type(dilithium_type);
+    dap_enc_sig_dilithium_set_type(DILITHIUM_MAX_SPEED);
 
 
     //int32_t type = 2;
diff --git a/dap-sdk/crypto/src/dap_enc_tesla.c b/dap-sdk/crypto/src/dap_enc_tesla.c
index e8c03e6ba0521f801f9b1d178fc5b3eb1186f23c..07533bcf4b9def2b0ea65cfb2bb26bdea2c4e3bd 100755
--- a/dap-sdk/crypto/src/dap_enc_tesla.c
+++ b/dap-sdk/crypto/src/dap_enc_tesla.c
@@ -40,9 +40,7 @@ void dap_enc_sig_tesla_key_new_generate(struct dap_enc_key * key, const void *ke
 
     int32_t retcode;
 
-    int tesla_type = (seed && seed_size >= sizeof(uint8_t)) ? ((uint8_t*)seed)[0] % (PROVABLY_MAX_SECURITY + 1) :
-                                                              HEURISTIC_MAX_SECURITY_AND_MAX_SPEED;
-    dap_enc_sig_tesla_set_type(tesla_type);
+    dap_enc_sig_tesla_set_type(HEURISTIC_MAX_SECURITY_AND_MAX_SPEED);
 
     /* type is a param of sign-security
      * type = 0 - Heuristic qTESLA, NIST's security category 1
diff --git a/dap-sdk/crypto/src/sig_picnic/picnic_impl.c b/dap-sdk/crypto/src/sig_picnic/picnic_impl.c
index a98e7fd85aad97e5ece7c90c64a9939c91fc5125..2a0e25ffe8d35f7479ed5b1a189514bfbad34f7d 100755
--- a/dap-sdk/crypto/src/sig_picnic/picnic_impl.c
+++ b/dap-sdk/crypto/src/sig_picnic/picnic_impl.c
@@ -18,7 +18,7 @@
 #include <string.h>
 #include <assert.h>
 #if defined (__WIN32)
-	#include <Windows.h>
+	#include <windows.h>
 	#include <bcrypt.h>
 #elif defined (__APPLE__)
     #include "macos_specific_endian.h"
diff --git a/dap-sdk/net/client/dap_client.c b/dap-sdk/net/client/dap_client.c
index d33a1a96b022361521045842c2cecf0014d7db30..b799fe8080941db2ce65225fc546f9a3312f6650 100644
--- a/dap-sdk/net/client/dap_client.c
+++ b/dap-sdk/net/client/dap_client.c
@@ -138,7 +138,6 @@ void dap_client_set_active_channels (dap_client_t * a_client, const char * a_act
     DAP_CLIENT_PVT(a_client)->active_channels = dap_strdup( a_active_channels);
 }
 
-
 /**
  * @brief dap_client_get_uplink_port
  * @param a_client
@@ -149,6 +148,11 @@ uint16_t dap_client_get_uplink_port(dap_client_t * a_client)
     return DAP_CLIENT_PVT(a_client)->uplink_port;
 }
 
+void dap_client_set_auth_cert(dap_client_t * a_client, dap_cert_t *a_cert)
+{
+    DAP_CLIENT_PVT(a_client)->auth_cert = a_cert;
+}
+
 
 /**
  * @brief dap_client_reset
diff --git a/dap-sdk/net/client/dap_client_pvt.c b/dap-sdk/net/client/dap_client_pvt.c
index fd15bb670d5b3d5555dcf84e74164c65615503e2..1bb6f9708ef684d963df77157f6adcdb09724e0d 100644
--- a/dap-sdk/net/client/dap_client_pvt.c
+++ b/dap-sdk/net/client/dap_client_pvt.c
@@ -56,6 +56,7 @@
 #include "dap_enc.h"
 #include "dap_common.h"
 #include "dap_strfuncs.h"
+#include "dap_cert.h"
 
 //#include "dap_http_client_simple.h"
 #include "dap_client_http.h"
@@ -396,25 +397,37 @@ static void s_stage_status_after(dap_client_pvt_t * a_client_pvt)
     case STAGE_STATUS_IN_PROGRESS: {
         switch (a_client_pvt->stage) {
         case STAGE_ENC_INIT: {
-            log_it(L_INFO, "Go to stage ENC: prepare the request");
-
+            log_it(L_INFO, "Go to stage ENC: prepare the request");         
             a_client_pvt->session_key_open = dap_enc_key_new_generate(DAP_ENC_KEY_TYPE_MSRLN, NULL, 0, NULL, 0, 0);
-
-            size_t l_key_str_size_max = DAP_ENC_BASE64_ENCODE_SIZE(a_client_pvt->session_key_open->pub_key_data_size);
-            char *l_key_str = DAP_NEW_Z_SIZE(char, l_key_str_size_max + 1);
+            if (!a_client_pvt->session_key_open) {
+                log_it(L_ERROR, "Insufficient memory! May be a huge memory leak present");
+                a_client_pvt->stage_status = STAGE_STATUS_ERROR;
+                break;
+            }
+            size_t l_key_size = a_client_pvt->session_key_open->pub_key_data_size;
+            dap_cert_t *l_cert = a_client_pvt->auth_cert;
+            dap_sign_t *l_sign = NULL;
+            size_t l_sign_size = 0;
+            if (l_cert) {
+                l_sign = dap_sign_create(l_cert->enc_key, a_client_pvt->session_key_open->pub_key_data, l_key_size, 0);
+                l_sign_size = dap_sign_get_size(l_sign);
+            }
+            uint8_t l_data[l_key_size + l_sign_size];
+            memcpy(l_data, a_client_pvt->session_key_open->pub_key_data, l_key_size);
+            if (l_sign) {
+                memcpy(l_data + l_key_size, l_sign, l_sign_size);
+            }
+            size_t l_data_str_size_max = DAP_ENC_BASE64_ENCODE_SIZE(l_key_size + l_sign_size);
+            char l_data_str[l_data_str_size_max + 1];
             // DAP_ENC_DATA_TYPE_B64_URLSAFE not need because send it by POST request
-            size_t l_key_str_enc_size = dap_enc_base64_encode(a_client_pvt->session_key_open->pub_key_data,
-                    a_client_pvt->session_key_open->pub_key_data_size,
-                    l_key_str, DAP_ENC_DATA_TYPE_B64);
-
-            log_it(L_DEBUG, "ENC request size %u", l_key_str_enc_size);
+            size_t l_data_str_enc_size = dap_enc_base64_encode(l_data, l_key_size + l_sign_size, l_data_str, DAP_ENC_DATA_TYPE_B64);
+            log_it(L_DEBUG, "ENC request size %u", l_data_str_enc_size);
             int l_res = dap_client_pvt_request(a_client_pvt, DAP_UPLINK_PATH_ENC_INIT "/gd4y5yh78w42aaagh",
-                    l_key_str, l_key_str_enc_size, m_enc_init_response, m_enc_init_error);
+                    l_data_str, l_data_str_enc_size, m_enc_init_response, m_enc_init_error);
             // bad request
             if(l_res<0){
             	a_client_pvt->stage_status = STAGE_STATUS_ERROR;
             }
-            DAP_DELETE(l_key_str);
         }
             break;
         case STAGE_STREAM_CTL: {
@@ -720,7 +733,7 @@ void dap_client_pvt_request_enc(dap_client_pvt_t * a_client_internal, const char
         , dap_client_callback_data_size_t a_response_proc
         , dap_client_callback_int_t a_response_error)
 {
-    bool is_query_enc = false; // it true, then encode a_query string
+    bool is_query_enc = true; // if true, then encode a_query string  [Why do we even need this?]
     log_it(L_DEBUG, "Encrypted request: sub_url '%s' query '%s'", a_sub_url ? a_sub_url : "NULL",
             a_query ? a_query : "NULL");
     size_t l_sub_url_size = a_sub_url ? strlen(a_sub_url) : 0;
diff --git a/dap-sdk/net/client/include/dap_client.h b/dap-sdk/net/client/include/dap_client.h
index 7a81af9734559fdf27c8efb458d548553c22a212..dc49c63796fc313f5084043988a51f75231ba117 100644
--- a/dap-sdk/net/client/include/dap_client.h
+++ b/dap-sdk/net/client/include/dap_client.h
@@ -28,6 +28,7 @@
 #include "dap_events.h"
 #include "dap_stream.h"
 #include "dap_stream_ch.h"
+#include "dap_cert.h"
 
 /**
  * @brief The dap_client_stage enum. Top level of client's state machine
@@ -133,6 +134,7 @@ dap_stream_t * dap_client_get_stream(dap_client_t * a_client);
 dap_stream_ch_t * dap_client_get_stream_ch(dap_client_t * a_client, uint8_t a_ch_id);
 const char * dap_client_get_stream_id(dap_client_t * a_client);
 void dap_client_set_active_channels (dap_client_t * a_client, const char * a_active_channels);
+void dap_client_set_auth_cert(dap_client_t * a_client, dap_cert_t *a_cert);
 
 dap_client_stage_t dap_client_get_stage(dap_client_t * a_client);
 dap_client_stage_status_t dap_client_get_stage_status(dap_client_t * a_client);
diff --git a/dap-sdk/net/client/include/dap_client_pvt.h b/dap-sdk/net/client/include/dap_client_pvt.h
index 9c93e03ad62ae463db9d407657e397b1d629810a..04aa7439242657964d7bd4bf8f5627aa36af1241 100644
--- a/dap-sdk/net/client/include/dap_client_pvt.h
+++ b/dap-sdk/net/client/include/dap_client_pvt.h
@@ -28,6 +28,8 @@
 #include "dap_client.h"
 #include "dap_stream.h"
 #include "dap_events_socket.h"
+#include "dap_cert.h"
+
 typedef struct dap_enc_key dap_enc_key_t;
 typedef struct dap_http_client dap_http_client_t;
 
@@ -47,7 +49,7 @@ typedef struct dap_client_internal
     dap_enc_key_t * session_key; // Symmetric private key for session encryption
     dap_enc_key_t * stream_key; // Stream private key for stream encryption
     char stream_id[25];
-
+    dap_cert_t *auth_cert;
 
     char  * session_key_id;
     //void  *curl;// curl connection descriptor
diff --git a/dap-sdk/net/core/dap_events_socket.c b/dap-sdk/net/core/dap_events_socket.c
index fb7f844171a219e8ca64d4e7de173810dba8a0f8..ca6769a0b385d94c90535bba528b199c475408db 100644
--- a/dap-sdk/net/core/dap_events_socket.c
+++ b/dap-sdk/net/core/dap_events_socket.c
@@ -324,7 +324,7 @@ void dap_events_socket_delete( dap_events_socket_t *a_es, bool preserve_inherito
 #endif
   }
   pthread_mutex_destroy(&a_es->write_hold);
-  free( a_es );
+  DAP_DELETE( a_es );
 }
 
 /**
diff --git a/dap-sdk/net/core/dap_server.c b/dap-sdk/net/core/dap_server.c
index b92ae2a8fb9f00538abd304f974eb122d49de956..3f47e681ec9f61d8287a676eb0a21113593c7687 100644
--- a/dap-sdk/net/core/dap_server.c
+++ b/dap-sdk/net/core/dap_server.c
@@ -517,9 +517,18 @@ static void read_write_cb( dap_client_remote_t *dap_cur, int32_t revents )
                                    dap_cur->buf_out_size - total_sent,
                                    MSG_DONTWAIT | MSG_NOSIGNAL );
         if( bytes_sent < 0 ) {
-          log_it(L_ERROR,"[THREAD %u] Error occured in send() function %s", dap_cur->tn, strerror(errno) );
-          dap_cur->flags |= DAP_SOCK_SIGNAL_CLOSE;
-          break;
+            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
+                dap_cur->pevent.events = EPOLLOUT | EPOLLERR;
+                if( epoll_ctl(dap_cur->efd, EPOLL_CTL_MOD, dap_cur->socket, &dap_cur->pevent) != 0 ) {
+                    log_it( L_ERROR, "epoll_ctl failed..." );
+                    dap_cur->flags |= DAP_SOCK_SIGNAL_CLOSE;
+                    break;
+                }
+            } else {
+                log_it(L_ERROR,"[THREAD %u] Error occured in send() function %s", dap_cur->tn, strerror(errno) );
+                dap_cur->flags |= DAP_SOCK_SIGNAL_CLOSE;
+                break;
+            }
         }
 
         total_sent += (size_t)bytes_sent;
@@ -684,9 +693,7 @@ void  *thread_loop( void *arg )
           log_it( L_ERROR,"Socket error: %u, remove it" , dap_cur->socket );
           dap_cur->flags |= DAP_SOCK_SIGNAL_CLOSE;
       }
-#ifdef _WIN32
-      set_nonblock_socket(dap_cur->socket); // pconst: for winsock2 has no appropriate MSG attributes
-#endif
+      set_nonblock_socket(dap_cur->socket);
       if ( !(dap_cur->flags & DAP_SOCK_SIGNAL_CLOSE) || dap_cur->no_close )
         read_write_cb( dap_cur, events[i].events );
 
diff --git a/dap-sdk/net/server/enc_server/dap_enc_http.c b/dap-sdk/net/server/enc_server/dap_enc_http.c
index e5513c121635f35a4749ae377c8983717ba23bc5..c04ddf05f18018e72f717db26160bf6e7580b79a 100644
--- a/dap-sdk/net/server/enc_server/dap_enc_http.c
+++ b/dap-sdk/net/server/enc_server/dap_enc_http.c
@@ -37,6 +37,7 @@
 
 
 #include "dap_common.h"
+#include "dap_sign.h"
 
 #include "include/dap_http.h"
 #include "dap_http_client.h"
@@ -55,6 +56,8 @@
 
 #define LOG_TAG "dap_enc_http"
 
+static dap_enc_acl_callback_t s_acl_callback = NULL;
+
 int enc_http_init()
 {
     return 0;
@@ -79,6 +82,12 @@ static void _enc_http_write_reply(struct dap_http_simple *cl_st,
 }
 
 void dap_enc_http_json_response_format_enable(bool);
+
+void dap_enc_http_set_acl_callback(dap_enc_acl_callback_t a_callback)
+{
+    s_acl_callback = a_callback;
+}
+
 /**
  * @brief enc_http_proc Enc http interface
  * @param cl_st HTTP Simple client instance
@@ -93,16 +102,29 @@ void enc_http_proc(struct dap_http_simple *cl_st, void * arg)
 
         uint8_t alice_msg[cl_st->request_size];
         size_t decode_len = dap_enc_base64_decode(cl_st->request, cl_st->request_size, alice_msg, DAP_ENC_DATA_TYPE_B64);
-        if(decode_len != MSRLN_PKA_BYTES) {
+        dap_chain_hash_fast_t l_sign_hash = {};
+        if (decode_len < MSRLN_PKA_BYTES) {
             log_it(L_WARNING, "Wrong http_enc request. Key not equal MSRLN_PKA_BYTES");
             *return_code = Http_Status_BadRequest;
             return;
+        } else if (decode_len > MSRLN_PKA_BYTES) {
+            dap_sign_t *l_sign = (dap_sign_t *)&alice_msg[MSRLN_PKA_BYTES];
+            if (dap_sign_verify(l_sign, alice_msg, MSRLN_PKA_BYTES) != 1) {
+                *return_code = Http_Status_Unauthorized;
+                return;
+            }
+            dap_sign_get_pkey_hash(l_sign, &l_sign_hash);
         }
 
         dap_enc_key_t* msrln_key = dap_enc_key_new(DAP_ENC_KEY_TYPE_MSRLN);
         msrln_key->gen_bob_shared_key(msrln_key, alice_msg, MSRLN_PKA_BYTES, (void**)&msrln_key->pub_key_data);
 
         dap_enc_ks_key_t * key_ks = dap_enc_ks_new();
+        if (s_acl_callback) {
+            key_ks->acl_list = s_acl_callback(&l_sign_hash);
+        } else {
+            log_it(L_WARNING, "Callback for ACL is not set, pass anauthorized");
+        }
 
         char encrypt_msg[DAP_ENC_BASE64_ENCODE_SIZE(msrln_key->pub_key_data_size) + 1];
         size_t encrypt_msg_size = dap_enc_base64_encode(msrln_key->pub_key_data, msrln_key->pub_key_data_size, encrypt_msg, DAP_ENC_DATA_TYPE_B64);
@@ -124,6 +146,7 @@ void enc_http_proc(struct dap_http_simple *cl_st, void * arg)
         dap_enc_key_delete(msrln_key);
 
         *return_code = Http_Status_OK;
+
     } else{
         log_it(L_ERROR,"Wrong path '%s' in the request to enc_http module",cl_st->http->url_path);
         *return_code = Http_Status_NotFound;
diff --git a/dap-sdk/net/server/enc_server/include/dap_enc_http.h b/dap-sdk/net/server/enc_server/include/dap_enc_http.h
index b92032188d2fa2f0c729f35a290182e1f442a3d1..4660b5250969586abea87ba461115e6169929bbc 100644
--- a/dap-sdk/net/server/enc_server/include/dap_enc_http.h
+++ b/dap-sdk/net/server/enc_server/include/dap_enc_http.h
@@ -22,6 +22,7 @@
 #define _ENC_HTTP_H_
 #include <stddef.h>
 #include <stdbool.h>
+#include "dap_hash.h"
 
 struct dap_http;
 struct dap_http_client;
@@ -63,6 +64,7 @@ typedef struct enc_http_delegate{
 } enc_http_delegate_t;
 
 typedef void (*dap_enc_http_callback_t) (enc_http_delegate_t *,void *); // Callback for specific client operations
+typedef uint8_t *(* dap_enc_acl_callback_t) (dap_chain_hash_fast_t *);   // Callback for access list for private chain networks
 
 int enc_http_init(void);
 void enc_http_deinit(void);
@@ -70,6 +72,7 @@ void enc_http_deinit(void);
 size_t enc_http_reply(enc_http_delegate_t * dg, void * data, size_t data_size);
 size_t enc_http_reply_f(enc_http_delegate_t * dg, const char * data, ...);
 
+void dap_enc_http_set_acl_callback(dap_enc_acl_callback_t a_callback);
 
 enc_http_delegate_t *enc_http_request_decode(struct dap_http_simple *a_http_simple);
 
diff --git a/dap-sdk/net/server/enc_server/include/dap_enc_ks.h b/dap-sdk/net/server/enc_server/include/dap_enc_ks.h
index 828ac4e583a59a4fec184383cb655d5d3a6af19d..07c51b19ecf378acd9e7e776228aa7b89dbc4fe3 100644
--- a/dap-sdk/net/server/enc_server/include/dap_enc_ks.h
+++ b/dap-sdk/net/server/enc_server/include/dap_enc_ks.h
@@ -25,6 +25,7 @@
 #include "uthash.h"
 #include "dap_enc_key.h"
 #include "stdbool.h"
+#include "dap_hash.h"
 
 #define DAP_ENC_KS_KEY_ID_SIZE 33
 struct dap_http_client;
@@ -34,6 +35,7 @@ typedef struct dap_enc_ks_key{
     dap_enc_key_t *key;
     time_t time_created;
     pthread_mutex_t mutex;
+    uint8_t *acl_list;
     UT_hash_handle hh; // makes this structure hashable with UTHASH library
 } dap_enc_ks_key_t;
 
diff --git a/dap-sdk/net/server/http_server/dap_http_simple.c b/dap-sdk/net/server/http_server/dap_http_simple.c
index 12345d2c3caa2a368288c5e482f397f31a9d4f5d..b8beddd0981899b1c3e943d9ac55d9ef681a22a5 100644
--- a/dap-sdk/net/server/http_server/dap_http_simple.c
+++ b/dap-sdk/net/server/http_server/dap_http_simple.c
@@ -412,10 +412,17 @@ static void s_headers_read( dap_http_client_t *a_http_client, void *a_arg )
     DAP_HTTP_SIMPLE(a_http_client)->reply_byte = DAP_NEW_Z_SIZE(uint8_t, DAP_HTTP_SIMPLE(a_http_client)->reply_size_max );
 
     if( a_http_client->in_content_length ) {
-        if( a_http_client->in_content_length < DAP_HTTP_SIMPLE_REQUEST_MAX )
-            DAP_HTTP_SIMPLE(a_http_client)->request = calloc( 1, a_http_client->in_content_length + 1 );
+        // dbg if( a_http_client->in_content_length < 3){
+        if( a_http_client->in_content_length > 0){
+            DAP_HTTP_SIMPLE(a_http_client)->request_size_max = a_http_client->in_content_length + 1;
+            DAP_HTTP_SIMPLE(a_http_client)->request = DAP_NEW_Z_SIZE(void, DAP_HTTP_SIMPLE(a_http_client)->request_size_max);
+            if(!DAP_HTTP_SIMPLE(a_http_client)->request){
+                DAP_HTTP_SIMPLE(a_http_client)->request_size_max = 0;
+                log_it(L_ERROR, "Too big content-length %u in request", a_http_client->in_content_length);
+            }
+        }
         else
-            log_it( L_ERROR, "Too big content-length %u in request", a_http_client->in_content_length );
+            log_it(L_ERROR, "Not defined content-length %u in request", a_http_client->in_content_length);
     } else {
         log_it( L_DEBUG, "No data section, execution proc callback" );
         queue_http_request_put( DAP_HTTP_SIMPLE(a_http_client) );
@@ -435,8 +442,17 @@ void s_data_read( dap_http_client_t *a_http_client, void * a_arg )
                             a_http_client->client->buf_in_size : ( a_http_client->in_content_length - l_http_simple->request_size );
 
     if( bytes_to_read ) {
-        memcpy( l_http_simple->request_byte + l_http_simple->request_size, a_http_client->client->buf_in, bytes_to_read );
-        l_http_simple->request_size += bytes_to_read;
+        // Oops! The client sent more data than write in the CONTENT_LENGTH header
+        if(l_http_simple->request_size + bytes_to_read > l_http_simple->request_size_max){
+            log_it(L_WARNING, "Oops! Client sent more data length=%u than in content-length=%u in request", l_http_simple->request_size + bytes_to_read, a_http_client->in_content_length);
+            l_http_simple->request_size_max = l_http_simple->request_size + bytes_to_read + 1;
+            // increase input buffer
+            l_http_simple->request = DAP_REALLOC(l_http_simple->request, l_http_simple->request_size_max);
+        }
+        if(l_http_simple->request){// request_byte=request
+            memcpy( l_http_simple->request_byte + l_http_simple->request_size, a_http_client->client->buf_in, bytes_to_read );
+            l_http_simple->request_size += bytes_to_read;
+        }
     }
 
     if( l_http_simple->request_size >= a_http_client->in_content_length ) {
diff --git a/dap-sdk/net/server/http_server/include/dap_http_simple.h b/dap-sdk/net/server/http_server/include/dap_http_simple.h
index 12e3d3461e3c71eed797b6f7f2d62580670806c5..0dda2401f49d982c7528307fce9d706dc802f822 100644
--- a/dap-sdk/net/server/http_server/include/dap_http_simple.h
+++ b/dap-sdk/net/server/http_server/include/dap_http_simple.h
@@ -28,7 +28,7 @@ See more details here <http://www.gnu.org/licenses/>.
 #include "dap_http.h"
 
 //#define DAP_HTTP_SIMPLE_REQUEST_MAX 100000
-
+// number of simultaneous http requests
 #define DAP_HTTP_SIMPLE_REQUEST_MAX 65536
 
 struct dap_http_simple;
@@ -51,6 +51,7 @@ typedef struct dap_http_simple {
   };
 
   size_t request_size;
+  size_t request_size_max;
   size_t reply_size;
   size_t reply_size_max;
   size_t reply_sent;
diff --git a/dap-sdk/net/stream/ch/dap_stream_ch.c b/dap-sdk/net/stream/ch/dap_stream_ch.c
index 6402394bf3fb0db0276b505c77212692d9cac02b..6ce73a49d4b1c22712b4c572e66f7717d08d0c66 100644
--- a/dap-sdk/net/stream/ch/dap_stream_ch.c
+++ b/dap-sdk/net/stream/ch/dap_stream_ch.c
@@ -45,6 +45,13 @@
 
 #define LOG_TAG "dap_stream_ch"
 
+static struct dap_stream_ch_table_t {
+    dap_stream_ch_t *ch;
+    UT_hash_handle hh;
+} *s_ch_table = NULL;
+
+static pthread_mutex_t s_ch_table_lock;
+
 /**
  * @brief stream_ch_init Init stream channel module
  * @return Zero if ok others if no
@@ -59,6 +66,7 @@ int dap_stream_ch_init()
         log_it(L_CRITICAL,"Can't init stream channel packet submodule");
         return -1;
     }
+    pthread_mutex_init(&s_ch_table_lock, NULL);
     log_it(L_NOTICE,"Module stream channel initialized");
     return 0;
 }
@@ -68,6 +76,7 @@ int dap_stream_ch_init()
  */
 void dap_stream_ch_deinit()
 {
+    pthread_mutex_destroy(&s_ch_table_lock);
 }
 
 /**
@@ -82,7 +91,8 @@ dap_stream_ch_t* dap_stream_ch_new(dap_stream_t* a_stream, uint8_t id)
         dap_stream_ch_t* ret = DAP_NEW_Z(dap_stream_ch_t);
         ret->stream = a_stream;
         ret->proc = proc;
-        ret->ready_to_read=true;
+        ret->ready_to_read = true;
+
         pthread_mutex_init(&(ret->mutex),NULL);
         if(ret->proc->new_callback)
             ret->proc->new_callback(ret,NULL);
@@ -92,6 +102,11 @@ dap_stream_ch_t* dap_stream_ch_new(dap_stream_t* a_stream, uint8_t id)
         a_stream->channel_count++;
         pthread_rwlock_unlock(&a_stream->rwlock);
 
+        struct dap_stream_ch_table_t *l_new_ch = DAP_NEW_Z(struct dap_stream_ch_table_t);
+        pthread_mutex_lock(&s_ch_table_lock);
+        HASH_ADD_PTR(s_ch_table, ch, l_new_ch);
+        pthread_mutex_unlock(&s_ch_table_lock);
+
         return ret;
     }else{
         log_it(L_WARNING, "Unknown stream processor with id %uc",id);
@@ -99,24 +114,42 @@ dap_stream_ch_t* dap_stream_ch_new(dap_stream_t* a_stream, uint8_t id)
     }
 }
 
+bool dap_stream_ch_valid(dap_stream_ch_t *a_ch)
+{
+    struct dap_stream_ch_table_t *l_ret;
+    if(!a_ch)
+        return false;
+    pthread_mutex_lock(&s_ch_table_lock);
+    HASH_FIND_PTR(s_ch_table, a_ch, l_ret);
+    pthread_mutex_unlock(&s_ch_table_lock);
+    return l_ret;
+}
+
 /**
  * @brief stream_ch_delete Delete channel instance
  * @param ch Channel delete
  */
-void dap_stream_ch_delete(dap_stream_ch_t*ch)
+void dap_stream_ch_delete(dap_stream_ch_t *a_ch)
 {
-    if(ch->proc)
-        if(ch->proc->delete_callback)
-            ch->proc->delete_callback(ch,NULL);
+    pthread_mutex_lock(&s_ch_table_lock);
+    struct dap_stream_ch_table_t *l_ret;;
+    HASH_FIND_PTR(s_ch_table, a_ch, l_ret);
+    HASH_DEL(s_ch_table, l_ret);
+    pthread_mutex_unlock(&s_ch_table_lock);
 
-    pthread_mutex_destroy(&(ch->mutex));
+    pthread_mutex_lock(&a_ch->mutex);
+    if (a_ch->proc)
+        if (a_ch->proc->delete_callback)
+            a_ch->proc->delete_callback(a_ch, NULL);
+    pthread_mutex_unlock(&a_ch->mutex);
+    pthread_mutex_destroy(&a_ch->mutex);
 
 /* fixed raise, but probably may be memory leak!
     if(ch->internal){
         free(ch->internal);
     }
 */
-    //free(ch);
+    DAP_DELETE(a_ch);
 }
 
 /**
@@ -126,6 +159,9 @@ void dap_stream_ch_delete(dap_stream_ch_t*ch)
  */
 void dap_stream_ch_set_ready_to_read(dap_stream_ch_t * a_ch,bool a_is_ready)
 {
+    if (!dap_stream_ch_valid(a_ch)) {
+        return;
+    }
     pthread_mutex_lock(&a_ch->mutex);
     if( a_ch->ready_to_read != a_is_ready){
         //log_it(L_DEBUG,"Change channel '%c' to %s", (char) ch->proc->id, is_ready?"true":"false");
@@ -149,6 +185,9 @@ void dap_stream_ch_set_ready_to_read(dap_stream_ch_t * a_ch,bool a_is_ready)
  */
 void dap_stream_ch_set_ready_to_write(dap_stream_ch_t * ch,bool is_ready)
 {
+    if (!dap_stream_ch_valid(ch)) {
+        return;
+    }
     pthread_mutex_lock(&ch->mutex);
     if(ch->ready_to_write!=is_ready){
         //log_it(L_DEBUG,"Change channel '%c' to %s", (char) ch->proc->id, is_ready?"true":"false");
@@ -166,3 +205,37 @@ void dap_stream_ch_set_ready_to_write(dap_stream_ch_t * ch,bool is_ready)
     }
     pthread_mutex_unlock(&ch->mutex);
 }
+
+/**
+ * @brief dap_stream_ch_get_ready_to_read
+ * @param a_ch
+ * @return
+ */
+bool dap_stream_ch_get_ready_to_read(dap_stream_ch_t * a_ch)
+{
+    if (!dap_stream_ch_valid(a_ch)) {
+        return false;
+    }
+    bool l_ret;
+    pthread_mutex_lock(&a_ch->mutex);
+    l_ret = a_ch->ready_to_read;
+    pthread_mutex_unlock(&a_ch->mutex);
+    return l_ret;
+}
+
+/**
+ * @brief dap_stream_ch_get_ready_to_write
+ * @param a_ch
+ * @return
+ */
+bool dap_stream_ch_get_ready_to_write(dap_stream_ch_t * a_ch)
+{
+    if (!dap_stream_ch_valid(a_ch)) {
+        return false;
+    }
+    bool l_ret;
+    pthread_mutex_lock(&a_ch->mutex);
+    l_ret = a_ch->ready_to_write;
+    pthread_mutex_unlock(&a_ch->mutex);
+    return l_ret;
+}
diff --git a/dap-sdk/net/stream/ch/dap_stream_ch_pkt.c b/dap-sdk/net/stream/ch/dap_stream_ch_pkt.c
index cb575948c43b1c9604965ca8ec56f881f7cc5bbc..b1600592b17f4572c400430a2aba24b0fb123f24 100644
--- a/dap-sdk/net/stream/ch/dap_stream_ch_pkt.c
+++ b/dap-sdk/net/stream/ch/dap_stream_ch_pkt.c
@@ -79,6 +79,9 @@ size_t dap_stream_ch_pkt_write(struct dap_stream_ch * a_ch,  uint8_t a_type, con
         log_it(L_WARNING,"Zero data size to write out in channel");
         return 0;
     }
+    if (!dap_stream_ch_valid(a_ch)) {
+        return 0;
+    }
     pthread_mutex_lock( &a_ch->mutex);
 
     //log_it(L_DEBUG,"Output: Has %u bytes of %c type for %c channel id",data_size, (char)type, (char) ch->proc->id );
diff --git a/dap-sdk/net/stream/ch/include/dap_stream_ch.h b/dap-sdk/net/stream/ch/include/dap_stream_ch.h
index cde7633792a8928bb6398c0dc67def14e3bb1f00..a9c1f484fee1a16f231be3a3ea13a1d046f5a834 100644
--- a/dap-sdk/net/stream/ch/include/dap_stream_ch.h
+++ b/dap-sdk/net/stream/ch/include/dap_stream_ch.h
@@ -58,34 +58,11 @@ dap_stream_ch_t* dap_stream_ch_new( dap_stream_t * dap_stream,uint8_t id);
 void dap_stream_ch_set_ready_to_read(dap_stream_ch_t * ch,bool is_ready);
 void dap_stream_ch_set_ready_to_write(dap_stream_ch_t * ch,bool is_ready);
 
-/**
- * @brief dap_stream_ch_get_ready_to_read
- * @param a_ch
- * @return
- */
-static inline bool dap_stream_ch_get_ready_to_read(dap_stream_ch_t * a_ch)
-{
-    bool l_ret;
-    pthread_mutex_lock(&a_ch->mutex);
-    l_ret = a_ch->ready_to_read;
-    pthread_mutex_unlock(&a_ch->mutex);
-    return l_ret;
-}
-
-/**
- * @brief dap_stream_ch_get_ready_to_write
- * @param a_ch
- * @return
- */
-static inline bool dap_stream_ch_get_ready_to_write(dap_stream_ch_t * a_ch)
-{
-    bool l_ret;
-    pthread_mutex_lock(&a_ch->mutex);
-    l_ret = a_ch->ready_to_write;
-    pthread_mutex_unlock(&a_ch->mutex);
-    return l_ret;
-}
-
-void dap_stream_ch_delete(dap_stream_ch_t*ch);
+bool dap_stream_ch_get_ready_to_read(dap_stream_ch_t *a_ch);
+bool dap_stream_ch_get_ready_to_write(dap_stream_ch_t *a_ch);
+
+void dap_stream_ch_delete(dap_stream_ch_t *a_ch);
+
+bool dap_stream_ch_valid(dap_stream_ch_t *a_ch);
 
 #endif
diff --git a/dap-sdk/net/stream/session/include/dap_stream_session.h b/dap-sdk/net/stream/session/include/dap_stream_session.h
index 9fdfb91c061fbd4c62356f17eb39cc83863ed7e7..2f8d9e624d23a5eebb694515b06796f31aac87f7 100644
--- a/dap-sdk/net/stream/session/include/dap_stream_session.h
+++ b/dap-sdk/net/stream/session/include/dap_stream_session.h
@@ -30,6 +30,7 @@
 
 #include "uthash.h"
 #include "dap_enc_key.h"
+#include "dap_hash.h"
 
 typedef enum stream_session_type {STREAM_SESSION_TYPE_MEDIA=0,STREAM_SESSION_TYPE_VPN} stream_session_type_t;
 typedef enum stream_session_connection_type {STEAM_SESSION_HTTP = 0, STREAM_SESSION_UDP, STREAM_SESSION_END_TYPE} stream_session_connection_type_t;
@@ -56,6 +57,7 @@ struct dap_stream_session {
 
     stream_session_connection_type_t conn_type;
     stream_session_type_t type;
+    uint8_t *acl;
     UT_hash_handle hh;
     struct in_addr tun_client_addr;
 
diff --git a/dap-sdk/net/stream/stream/dap_stream_ctl.c b/dap-sdk/net/stream/stream/dap_stream_ctl.c
index 80a9427af0390ce774fbe849e93f6706f4399b56..21bd39eb99423262c6199bc573df54b2b48036e8 100644
--- a/dap-sdk/net/stream/stream/dap_stream_ctl.c
+++ b/dap-sdk/net/stream/stream/dap_stream_ctl.c
@@ -50,6 +50,7 @@
 #include "dap_stream_session.h"
 #include "dap_stream_ctl.h"
 #include "http_status_code.h"
+#include "dap_enc_ks.h"
 
 #define LOG_TAG "dap_stream_ctl"
 
@@ -145,6 +146,16 @@ void s_proc(struct dap_http_simple *a_http_simple, void * a_arg)
             dap_random_string_fill(key_str, KEX_KEY_STR_SIZE);
             ss->key = dap_enc_key_new_generate( s_socket_forward_key.type, key_str, KEX_KEY_STR_SIZE,
                                                NULL, 0, s_socket_forward_key.size);
+            dap_http_header_t *l_hdr_key_id = dap_http_header_find(a_http_simple->http->in_headers, "KeyID");
+            if (l_hdr_key_id) {
+                dap_enc_ks_key_t *l_ks_key = dap_enc_ks_find(l_hdr_key_id->value);
+                if (!l_ks_key) {
+                    log_it(L_WARNING, "Key with ID %s not found", l_hdr_key_id->value);
+                    *return_code = Http_Status_BadRequest;
+                    return;
+                }
+                ss->acl = l_ks_key->acl_list;
+            }
             enc_http_reply_f(l_dg,"%u %s",ss->id,key_str);
             *return_code = Http_Status_OK;
 
@@ -154,6 +165,7 @@ void s_proc(struct dap_http_simple *a_http_simple, void * a_arg)
         }else{
             log_it(L_ERROR,"Wrong request: \"%s\"",l_dg->in_query);
             *return_code = Http_Status_BadRequest;
+            return;
         }
 
         unsigned int conn_t = 0;
diff --git a/modules/chain/include/dap_chain.h b/modules/chain/include/dap_chain.h
index 96694458347514411f164876956119eb02eeb6a0..ed25e99e7a4e96db0417ecefa426eb8c4e18ae85 100644
--- a/modules/chain/include/dap_chain.h
+++ b/modules/chain/include/dap_chain.h
@@ -49,6 +49,12 @@ typedef struct dap_chain_atom_iter{
     void * _inheritor;
 } dap_chain_atom_iter_t;
 
+typedef enum dap_chain_atom_verify_res{
+    ATOM_ACCEPT,
+    ATOM_PASS,
+    ATOM_REJECT,
+    ATOM_MOVE_TO_THRESHOLD
+} dap_chain_atom_verify_res_t;
 
 typedef dap_chain_t* (*dap_chain_callback_new_t)(void);
 
@@ -56,7 +62,8 @@ typedef void (*dap_chain_callback_t)(dap_chain_t *);
 typedef int (*dap_chain_callback_new_cfg_t)(dap_chain_t*, dap_config_t *);
 typedef void (*dap_chain_callback_ptr_t)(dap_chain_t *, void * );
 
-typedef int (*dap_chain_callback_atom_t)(dap_chain_t *, dap_chain_atom_ptr_t );
+typedef dap_chain_atom_verify_res_t (*dap_chain_callback_atom_t)(dap_chain_t *, dap_chain_atom_ptr_t );
+typedef dap_chain_atom_verify_res_t (*dap_chain_callback_atom_verify_t)(dap_chain_t *, dap_chain_atom_ptr_t );
 typedef int (*dap_chain_callback_atom_size_t)(dap_chain_t *, dap_chain_atom_ptr_t ,size_t);
 typedef size_t (*dap_chain_callback_atom_get_hdr_size_t)(void);
 typedef size_t (*dap_chain_callback_atom_hdr_get_size_t)(dap_chain_atom_ptr_t );
@@ -112,7 +119,7 @@ typedef struct dap_chain{
     dap_chain_callback_t callback_delete;
 
     dap_chain_callback_atom_t callback_atom_add;
-    dap_chain_callback_atom_t callback_atom_verify;
+    dap_chain_callback_atom_verify_t callback_atom_verify;
 
     dap_chain_datum_callback_datum_pool_proc_add_t callback_datums_pool_proc;
     dap_chain_datum_callback_datum_pool_proc_add_with_group_t callback_datums_pool_proc_with_group;
diff --git a/modules/channel/chain-net/dap_stream_ch_chain_net.c b/modules/channel/chain-net/dap_stream_ch_chain_net.c
index 22d437782c12cd4189d5b922ef8503aa5c509f0e..74dcd903c674c4f4e93b15f8ca5c9da53562adbc 100644
--- a/modules/channel/chain-net/dap_stream_ch_chain_net.c
+++ b/modules/channel/chain-net/dap_stream_ch_chain_net.c
@@ -43,6 +43,7 @@
 
 #include "dap_common.h"
 #include "dap_strfuncs.h"
+#include "dap_cert.h"
 #include "uthash.h"
 #include "dap_http_client.h"
 #include "dap_chain_global_db.h"
@@ -192,8 +193,27 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
         pthread_mutex_lock(&l_ch_chain_net->mutex);
         dap_stream_ch_pkt_t *l_ch_pkt = (dap_stream_ch_pkt_t *) a_arg;
         dap_stream_ch_chain_net_pkt_t *l_ch_chain_net_pkt = (dap_stream_ch_chain_net_pkt_t *) l_ch_pkt->data;
-        size_t l_ch_chain_net_pkt_data_size = (size_t) l_ch_pkt->hdr.size - sizeof (l_ch_chain_net_pkt->hdr);
-        if(l_ch_chain_net_pkt) {
+        uint8_t l_acl_idx = dap_chain_net_acl_idx_by_id(l_ch_chain_net_pkt->hdr.net_id);
+        bool l_error = false;
+        char l_err_str[64];
+        if (l_acl_idx == (uint8_t)-1) {
+            log_it(L_ERROR, "Invalid net id in packet");
+            strcpy(l_err_str, "ERROR_NET_INVALID_ID");
+            l_error = true;
+        }
+        if (!l_error && a_ch->stream->session->acl && !a_ch->stream->session->acl[l_acl_idx]) {
+            log_it(L_WARNING, "Unauthorized request attempt to network %s",
+                   dap_chain_net_by_id(l_ch_chain_net_pkt->hdr.net_id)->pub.name);
+            strcpy(l_err_str, "ERROR_NET_NOT_AUTHORIZED");
+            l_error = true;
+        }
+        if (l_error) {
+            dap_stream_ch_chain_net_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_ERROR ,
+                                              l_ch_chain_net_pkt->hdr.net_id, l_err_str, strlen(l_err_str) + 1);
+            dap_stream_ch_set_ready_to_write(a_ch, true);
+        }
+        //size_t l_ch_chain_net_pkt_data_size = (size_t) l_ch_pkt->hdr.size - sizeof (l_ch_chain_net_pkt->hdr);
+        if (!l_error && l_ch_chain_net_pkt) {
             size_t l_ch_chain_net_pkt_data_size = l_ch_pkt->hdr.size - sizeof(dap_stream_ch_chain_net_pkt_hdr_t);
             switch (l_ch_pkt->hdr.type) {
                 case DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_DBG: {
@@ -239,7 +259,7 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                             dap_stream_ch_chain_net_pkt_write(a_ch, DAP_STREAM_CH_CHAIN_NET_PKT_TYPE_ERROR ,
                                                               l_ch_chain_net_pkt->hdr.net_id, l_err_str,sizeof (l_err_str));
                             dap_stream_ch_set_ready_to_write(a_ch, true);
-                            log_it(L_WARNING,"Invalid net id in packet");
+                            log_it(L_ERROR, "Invalid net id in packet");
                         } else {
                             if (dap_db_set_cur_node_addr_exp( l_addr->uint64, l_net->pub.name ))
                                 log_it(L_NOTICE,"Set up cur node address 0x%016llX",l_addr->uint64);
@@ -284,7 +304,6 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                     }
                 }
                 break;
-
             }
             if(l_ch_chain_net->notify_callback)
                 l_ch_chain_net->notify_callback(l_ch_chain_net,l_ch_pkt->hdr.type, l_ch_chain_net_pkt,
diff --git a/modules/channel/chain/dap_stream_ch_chain.c b/modules/channel/chain/dap_stream_ch_chain.c
index 7f93f8111a2ece71cf4d45ca96c086a3f1399d6a..92bd61bb91ee01e28937779ac9abf2ddb295668f 100644
--- a/modules/channel/chain/dap_stream_ch_chain.c
+++ b/modules/channel/chain/dap_stream_ch_chain.c
@@ -130,8 +130,27 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
     if(l_ch_chain) {
         dap_stream_ch_pkt_t * l_ch_pkt = (dap_stream_ch_pkt_t *) a_arg;
         dap_stream_ch_chain_pkt_t * l_chain_pkt = (dap_stream_ch_chain_pkt_t *) l_ch_pkt->data;
+        uint8_t l_acl_idx = dap_chain_net_acl_idx_by_id(l_chain_pkt->hdr.net_id);
+        bool l_error = false;
+        char l_err_str[64];
+        if (l_acl_idx == (uint8_t)-1) {
+            log_it(L_ERROR, "Invalid net id in packet");
+            strcpy(l_err_str, "ERROR_NET_INVALID_ID");
+            l_error = true;
+        }
+        if (!l_error && a_ch->stream->session->acl && !a_ch->stream->session->acl[l_acl_idx]) {
+            log_it(L_WARNING, "Unauthorized request attempt to network %s",
+                   dap_chain_net_by_id(l_chain_pkt->hdr.net_id)->pub.name);
+            strcpy(l_err_str, "ERROR_NET_NOT_AUTHORIZED");
+            l_error = true;
+        }
+        if (l_error) {
+            dap_stream_ch_chain_pkt_write_error(a_ch, l_chain_pkt->hdr.net_id,
+                    l_chain_pkt->hdr.chain_id, l_chain_pkt->hdr.cell_id, l_err_str);
+            dap_stream_ch_set_ready_to_write(a_ch, true);
+        }
         size_t l_chain_pkt_data_size = l_ch_pkt->hdr.size - sizeof(l_chain_pkt->hdr);
-        if(l_chain_pkt) {
+        if (!l_error && l_chain_pkt) {
             switch (l_ch_pkt->hdr.type) {
             case DAP_STREAM_CH_CHAIN_PKT_TYPE_SYNCED_ALL: {
                 log_it(L_INFO, "In:  SYNCED_ALL pkt");
@@ -336,8 +355,8 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                     if(l_chain_pkt_data_size > 0) {
                         dap_chain_atom_ptr_t l_atom_copy = DAP_CALLOC(1, l_chain_pkt_data_size);
                         memcpy(l_atom_copy, l_chain_pkt->data, l_chain_pkt_data_size);
-                        if(l_chain->callback_atom_add(l_chain, l_atom_copy) == 0 &&
-                                dap_chain_has_file_store(l_chain)) {
+                        dap_chain_atom_verify_res_t l_atom_add_res = l_chain->callback_atom_add(l_chain, l_atom_copy);
+                        if(l_atom_add_res == ATOM_ACCEPT && dap_chain_has_file_store(l_chain)) {
                             // append to file
                             dap_chain_cell_id_t l_cell_id;
                             l_cell_id.uint64 = l_chain_pkt->hdr.cell_id.uint64;
@@ -353,6 +372,8 @@ void s_stream_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
                             // delete cell and close file
                             dap_chain_cell_delete(l_cell);
                         }
+                        if(l_atom_add_res == ATOM_PASS)
+                            DAP_DELETE(l_atom_copy);
                     } else {
                         log_it(L_WARNING, "Empty chain packet");
                         dap_stream_ch_chain_pkt_write_error(a_ch, l_chain_pkt->hdr.net_id,
diff --git a/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c b/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
index a9bc51329619874adb7c770098624a7646ed7a93..44be5c56f1fe14ee13efcf88f766a14fbe4d1a48 100644
--- a/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
+++ b/modules/consensus/dag-pos/dap_chain_cs_dag_pos.c
@@ -273,7 +273,7 @@ static int s_callback_event_verify(dap_chain_cs_dag_t * a_dag, dap_chain_cs_dag_
             }
             if (! l_is_enough_balance ){
                 char *l_addr_str = dap_chain_addr_to_str(&l_addr);
-                log_it(L_WARNING, "Verify of event is false, because bal=0 for addr=%s", l_addr_str);
+                log_it(L_WARNING, "Verify of event is false, because bal is not enough for addr=%s", l_addr_str);
                 DAP_DELETE(l_addr_str);
                 return 0; //-1;
             }
diff --git a/modules/consensus/none/dap_chain_cs_none.c b/modules/consensus/none/dap_chain_cs_none.c
index 5626597d1e2fb19c3a85424fb4179f448dd6ada8..59af3853fdb8f111e211d8e975e235949373d166 100644
--- a/modules/consensus/none/dap_chain_cs_none.c
+++ b/modules/consensus/none/dap_chain_cs_none.c
@@ -68,8 +68,8 @@ typedef struct dap_chain_gdb_private
 static int dap_chain_gdb_ledger_load(dap_chain_gdb_t *a_gdb, dap_chain_net_t *a_net);
 
 // Atomic element organization callbacks
-static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t); //    Accept new event in gdb
-static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t); //    Verify new event in gdb
+static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t); //    Accept new event in gdb
+static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t); //    Verify new event in gdb
 static size_t s_chain_callback_atom_hdr_get_size(dap_chain_atom_ptr_t); //    Get gdb event size
 static size_t s_chain_callback_atom_get_static_hdr_size(void); //    Get gdb event header size
 
@@ -322,7 +322,7 @@ static size_t s_chain_callback_datums_pool_proc_with_group(dap_chain_t * a_chain
  * @param a_datums
  * @param a_datums_size
  */
-static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
+static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
 {
     dap_chain_gdb_t * l_gdb = DAP_CHAIN_GDB(a_chain);
     dap_chain_gdb_private_t *l_gdb_priv = PVT(l_gdb);
@@ -344,11 +344,11 @@ static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t
 
             // don't save bad transactions to base
             if(dap_chain_ledger_tx_add(a_chain->ledger, l_tx) != 1)
-                return -1;
+                return ATOM_REJECT;
             //}else
             //    return -2;
         }break;
-        default: return -1;
+        default: return ATOM_REJECT;
     }
 
     dap_chain_gdb_datum_hash_item_t * l_hash_item = DAP_NEW_Z(dap_chain_gdb_datum_hash_item_t);
@@ -361,7 +361,7 @@ static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t
         log_it(L_DEBUG,"Load mode, doesnt save item %s:%s", l_hash_item->key, l_gdb_priv->group_datums);
 
     DL_APPEND(l_gdb_priv->hash_items, l_hash_item);
-    return 0;
+    return ATOM_ACCEPT;
 }
 
 /**
@@ -370,11 +370,11 @@ static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t
  * @param a_atom
  * @return
  */
-static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
+static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
 {
     (void) a_chain;
     (void) a_atom;
-    return 0;
+    return ATOM_ACCEPT;
 }
 
 /**
diff --git a/modules/global-db/dap_chain_global_db_driver_cdb.c b/modules/global-db/dap_chain_global_db_driver_cdb.c
index 88221e95d522ec66d094e9a1011ea756c765a9ea..5adb80c769556202b7ef59097e40dbfd349b38b6 100644
--- a/modules/global-db/dap_chain_global_db_driver_cdb.c
+++ b/modules/global-db/dap_chain_global_db_driver_cdb.c
@@ -32,7 +32,7 @@
 
 #include "dap_common.h"
 #include "dap_hash.h"
-#include "dap_strfuncs.h"
+#include "dap_strfuncs.h" // #include <dap_fnmatch.h>
 #include "dap_chain_global_db_driver_cdb.h"
 #include "dap_file_utils.h"
 
@@ -471,11 +471,9 @@ dap_list_t* dap_db_driver_cdb_get_groups_by_mask(const char *a_group_mask)
     pthread_mutex_lock(&cdb_mutex);
     HASH_ITER(hh, s_cdb, cur_cdb, tmp)
     {
-        if(strstr(cur_cdb->local_group, a_group_mask)) {
-            if(!strstr(cur_cdb->local_group, ".del")) {
+        if(!dap_fnmatch(a_group_mask, cur_cdb->local_group, 0))
+            if(dap_fnmatch("*.del", cur_cdb->local_group, 0))
                 l_ret_list = dap_list_prepend(l_ret_list, dap_strdup(cur_cdb->local_group));
-            }
-        }
     }
     pthread_mutex_unlock(&cdb_mutex);
     return l_ret_list;
diff --git a/modules/global-db/dap_chain_global_db_hist.c b/modules/global-db/dap_chain_global_db_hist.c
index 49195daf7d666cc0f26302906a3b6ec37dd91500..c2683b156035b3ac7eab75ef6fed3f7d9cf45301 100644
--- a/modules/global-db/dap_chain_global_db_hist.c
+++ b/modules/global-db/dap_chain_global_db_hist.c
@@ -1130,11 +1130,13 @@ bool dap_db_history_truncate(void)
  */
 uint64_t dap_db_log_get_group_history_last_id(const char *a_history_group_name)
 {
+    uint64_t result = 0;
     dap_store_obj_t *l_last_obj = dap_chain_global_db_get_last(a_history_group_name);
     if(l_last_obj) {
-        return l_last_obj->id;
-    }else
-        return 0;
+        result = l_last_obj->id;
+        dap_store_obj_free(l_last_obj, 1);
+    }
+    return result;
 }
 
 /**
@@ -1253,7 +1255,7 @@ static void *s_list_thread_proc(void *arg)
             while(l_dap_db_log_list->group_cur < l_dap_db_log_list->group_number) {
                 l_dap_db_log_list->group_cur++;
                 // check for empty group
-                if(l_dap_db_log_list->group_number_items[l_dap_db_log_list->group_cur] < 1) {
+                if( !(l_dap_db_log_list->group_number) || (l_dap_db_log_list->group_number_items[l_dap_db_log_list->group_cur] < 1)) {
                     continue;
                 }
                 break;
diff --git a/modules/mempool/dap_chain_mempool.c b/modules/mempool/dap_chain_mempool.c
index 28ce8491f0bd1248a68cef2b09d6b98480fb1624..9b4c4ebe29990324b568e1323b28a9d8ef8a9eac 100644
--- a/modules/mempool/dap_chain_mempool.c
+++ b/modules/mempool/dap_chain_mempool.c
@@ -557,7 +557,7 @@ dap_chain_hash_fast_t* dap_chain_proc_tx_create_cond(dap_chain_net_t * a_net,
     if(a_net->pub.default_chain)
         l_chain = a_net->pub.default_chain;
     else
-        dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_TX);
+        l_chain = dap_chain_net_get_chain_by_chain_type(a_net, CHAIN_TYPE_TX);
 
     if(!l_chain)
             return NULL;
diff --git a/modules/net/CMakeLists.txt b/modules/net/CMakeLists.txt
index 990616476d68e2d2a843e53ed33de33bacf1e261..9c3c877a0e8c713e8567edfa7f758ec57af7ceaf 100644
--- a/modules/net/CMakeLists.txt
+++ b/modules/net/CMakeLists.txt
@@ -45,7 +45,7 @@ endif()
 
 if(UNIX)
     target_link_libraries(${PROJECT_NAME} dap_core dap_crypto dap_client dap_stream_ch_chain dap_stream_ch_chain_net dap_chain
-      dap_chain_crypto dap_chain_wallet dap_chain_net_srv dap_chain_net_srv_vpn dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake
+      dap_chain_wallet dap_chain_net_srv dap_chain_mempool dap_chain_global_db dap_chain_net_srv_stake
       resolv
       )
 endif()
diff --git a/modules/net/dap_chain_net.c b/modules/net/dap_chain_net.c
index 086bb90dd2ddcd2eed231f47bfb9aa66d474901b..5a9025d903b6abd49d6bdfa932643094081b727e 100644
--- a/modules/net/dap_chain_net.c
+++ b/modules/net/dap_chain_net.c
@@ -60,6 +60,8 @@
 #include "dap_config.h"
 #include "dap_hash.h"
 #include "dap_cert.h"
+#include "dap_cert_file.h"
+#include "dap_enc_http.h"
 #include "dap_chain_common.h"
 #include "dap_chain_net.h"
 #include "dap_chain_net_srv.h"
@@ -141,6 +143,7 @@ typedef struct dap_chain_net_pvt{
 
     dap_chain_net_state_t state;
     dap_chain_net_state_t state_target;
+    uint16_t acl_idx;
 } dap_chain_net_pvt_t;
 
 typedef struct dap_chain_net_item{
@@ -174,7 +177,7 @@ static int s_net_states_proc(dap_chain_net_t * l_net);
 static void * s_net_proc_thread ( void * a_net);
 static void s_net_proc_thread_start( dap_chain_net_t * a_net );
 static void s_net_proc_kill( dap_chain_net_t * a_net );
-int s_net_load(const char * a_net_name);
+int s_net_load(const char * a_net_name, uint16_t a_acl_idx);
 
 static void s_gbd_history_callback_notify (void * a_arg,const char a_op_code, const char * a_prefix, const char * a_group,
                                                      const char * a_key, const void * a_value,
@@ -185,6 +188,23 @@ static int s_cli_net(int argc, char ** argv, void *arg_func, char **str_reply);
 
 static bool s_seed_mode = false;
 
+static uint8_t *dap_chain_net_set_acl(dap_chain_hash_fast_t *a_pkey_hash);
+
+char *dap_chain_net_get_gdb_group_acl(dap_chain_net_t *a_net)
+{
+    if (a_net) {
+        const char l_path[] = "network/";
+        char l_cfg_path[strlen(a_net->pub.name) + strlen(l_path) + 1];
+        strcpy(l_cfg_path, l_path);
+        strcat(l_cfg_path, a_net->pub.name);
+        dap_config_t *l_cfg = dap_config_open(l_cfg_path);
+        const char *l_auth_gdb = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_gdb");
+        if (l_auth_gdb) {
+            return dap_strdup_printf("%s.%s", a_net->pub.gdb_groups_prefix, l_auth_gdb);
+        }
+    }
+    return NULL;
+}
 
 /**
  * @brief s_net_set_go_sync
@@ -497,6 +517,7 @@ static int s_net_states_proc(dap_chain_net_t * l_net)
                 l_pvt_net->links_success = 0;
                 l_pvt_net->flags &= ~F_DAP_CHAIN_NET_GO_SYNC;
                 l_pvt_net->state = NET_STATE_ONLINE;
+                break;
             }
             if (l_links_count < l_pvt_net->links_addrs_count) {
                 l_pvt_net->links_count++;
@@ -1013,9 +1034,15 @@ int dap_chain_net_init()
         "net -net <chain net name> stats tx [-from <From time>] [-to <To time>] [-prev_sec <Seconds>] \n"
             "\tTransactions statistics. Time format is <Year>-<Month>-<Day>_<Hours>:<Minutes>:<Seconds> or just <Seconds> \n"
         "net -net <chain net name> sync < all | gdb | chains >\n"
-            "\tSyncronyze gdb, chains or everything\n\n"
+            "\tSyncronyze gdb, chains or everything\n"
         "net -net <chain net name> link < list | add | del | info | establish >\n"
-            "\tList,add,del, dump or establish links\n\n"
+            "\tList, add, del, dump or establish links\n"
+        "net -net <chain net name> ca add {-cert <cert name> | -hash <cert hash>}\n"
+            "\tAdd certificate to list of authority cetificates in GDB group\n"
+        "net -net <chain net name> ca list\n"
+            "\tPrint list of authority cetificates from GDB group\n"
+        "net -net <chain net name> ca del -hash <cert hash>\n"
+            "\tDelete certificate from list of authority cetificates in GDB group by it's hash\n"
                                         );
     s_seed_mode = dap_config_get_item_bool_default(g_config,"general","seed_mode",false);
     dap_chain_global_db_add_history_group_prefix("global", GROUP_LOCAL_HISTORY);
@@ -1028,6 +1055,8 @@ int dap_chain_net_init()
     s_required_links_count = dap_config_get_item_int32_default(g_config, "general", "require_links", s_required_links_count);
 
     dap_chain_net_load_all();
+
+    dap_enc_http_set_acl_callback(dap_chain_net_set_acl);
     return 0;
 }
 
@@ -1037,6 +1066,7 @@ void dap_chain_net_load_all()
     DIR * l_net_dir = opendir( l_net_dir_str);
     if ( l_net_dir ){
         struct dirent * l_dir_entry;
+        uint16_t l_acl_idx = 0;
         while ( (l_dir_entry = readdir(l_net_dir) )!= NULL ){
             if (l_dir_entry->d_name[0]=='\0' || l_dir_entry->d_name[0]=='.')
                 continue;
@@ -1059,13 +1089,42 @@ void dap_chain_net_load_all()
             char* l_dot_pos = strchr(l_dir_entry->d_name,'.');
             if ( l_dot_pos )
                 *l_dot_pos = '\0';
-            s_net_load(l_dir_entry->d_name );
+            s_net_load(l_dir_entry->d_name, l_acl_idx++);
         }
         closedir(l_net_dir);
     }
     DAP_DELETE (l_net_dir_str);
 }
 
+void s_set_reply_text_node_status(char **a_str_reply, dap_chain_net_t * a_net){
+    const char* l_node_address_text_block = NULL;
+    dap_chain_node_addr_t l_cur_node_addr = { 0 };
+    l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr(a_net) ? dap_chain_net_get_cur_addr(a_net)->uint64 : dap_db_get_cur_node_addr(a_net->pub.name);
+    if(!l_cur_node_addr.uint64)
+        l_node_address_text_block = dap_strdup_printf(", cur node address not defined");
+    else
+        l_node_address_text_block = dap_strdup_printf(", cur node address " NODE_ADDR_FP_STR,NODE_ADDR_FP_ARGS_S(l_cur_node_addr));
+
+    const char* l_sync_current_link_text_block = NULL;
+    if(PVT(a_net)->state == NET_STATE_LINKS_PREPARE ||
+       PVT(a_net)->state == NET_STATE_LINKS_CONNECTING ||
+       PVT(a_net)->state == NET_STATE_SYNC_GDB ||
+       PVT(a_net)->state == NET_STATE_SYNC_CHAINS)
+        l_sync_current_link_text_block = dap_strdup_printf(", processing link %u from %u",
+                                                           PVT(a_net)->links_count, PVT(a_net)->links_addrs_count);
+
+    dap_chain_node_cli_set_reply_text(a_str_reply,
+                                      "Network \"%s\" has state %s (target state %s)%s%s",
+                                      a_net->pub.name, c_net_states[PVT(a_net)->state],
+            c_net_states[PVT(a_net)->state_target],
+            (l_sync_current_link_text_block)? l_sync_current_link_text_block: "",
+            l_node_address_text_block
+            );
+
+    DAP_DELETE(l_sync_current_link_text_block);
+    DAP_DELETE(l_node_address_text_block);
+}
+
 /**
  * @brief s_cli_net
  * @param argc
@@ -1076,6 +1135,7 @@ void dap_chain_net_load_all()
  */
 static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
 {
+    UNUSED(arg_func);
     int arg_index = 1;
     dap_chain_net_t * l_net = NULL;
 
@@ -1111,11 +1171,13 @@ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
         const char *l_go_str = NULL;
         const char *l_get_str = NULL;
         const char *l_stats_str = NULL;
+        const char *l_ca_str = NULL;
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "sync", &l_sync_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "link", &l_links_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "go", &l_go_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "get", &l_get_str);
         dap_chain_node_cli_find_option_val(argv, arg_index, argc, "stats", &l_stats_str);
+        dap_chain_node_cli_find_option_val(argv, arg_index, argc, "ca", &l_ca_str);
 
         if ( l_stats_str ){
             if ( strcmp(l_stats_str,"tx") == 0 ) {
@@ -1204,26 +1266,7 @@ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
 
         } else if ( l_get_str){
             if ( strcmp(l_get_str,"status") == 0 ) {
-                // get current node address
-                dap_chain_node_addr_t l_cur_node_addr = { 0 };
-                l_cur_node_addr.uint64 = dap_chain_net_get_cur_addr(l_net) ? dap_chain_net_get_cur_addr(l_net)->uint64 : dap_db_get_cur_node_addr(l_net->pub.name);
-                if(!l_cur_node_addr.uint64) {
-                    dap_chain_node_cli_set_reply_text(a_str_reply,
-                            "Network \"%s\" has state %s (target state %s), active links %u from %u, cur node address not defined",
-                            l_net->pub.name, c_net_states[PVT(l_net)->state],
-                            c_net_states[PVT(l_net)->state_target], PVT(l_net)->links_count,
-                            PVT(l_net)->links_addrs_count
-                            );
-                }
-                else {
-                    dap_chain_node_cli_set_reply_text(a_str_reply,
-                            "Network \"%s\" has state %s (target state %s), active links %u from %u, cur node address " NODE_ADDR_FP_STR,
-                            l_net->pub.name, c_net_states[PVT(l_net)->state],
-                            c_net_states[PVT(l_net)->state_target], PVT(l_net)->links_count,
-                            PVT(l_net)->links_addrs_count,
-                            NODE_ADDR_FP_ARGS_S(l_cur_node_addr)
-                            );
-                }
+                s_set_reply_text_node_status(a_str_reply, l_net);
                 ret = 0;
             }
         } else if ( l_links_str ){
@@ -1267,6 +1310,91 @@ static int s_cli_net( int argc, char **argv, void *arg_func, char **a_str_reply)
                                                   "Subcommand \"sync\" requires one of parameter: all,gdb,chains\n");
                 ret = -2;
             }
+        } else if (l_ca_str) {
+            if (strcmp(l_ca_str, "add") == 0 ) {
+                const char *l_cert_string = NULL, *l_hash_string = NULL;
+                dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-cert", &l_cert_string);
+                dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-hash", &l_hash_string);
+                if (!l_cert_string && !l_hash_string) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "One of -cert or -hash parameters is mandatory");
+                    return -6;
+                }
+                if (l_cert_string) {
+                    dap_cert_t * l_cert = dap_cert_find_by_name(l_cert_string);
+                    if (l_cert == NULL) {
+                        dap_chain_node_cli_set_reply_text(a_str_reply, "Can't find \"%s\" certificate", l_cert_string);
+                        return -7;
+                    }
+                    if (l_cert->enc_key == NULL) {
+                        dap_chain_node_cli_set_reply_text(a_str_reply, "No key found in \"%s\" certificate", l_cert_string );
+                        return -8;
+                    }
+                    // Get publivc key hash
+                    size_t l_pub_key_size = 0;
+                    uint8_t *l_pub_key = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pub_key_size);;
+                    if (l_pub_key == NULL) {
+                        dap_chain_node_cli_set_reply_text(a_str_reply, "Can't serialize public key of certificate \"%s\"", l_cert_string);
+                        return -9;
+                    }
+                    dap_chain_hash_fast_t l_pkey_hash;
+                    dap_hash_fast(l_pub_key, l_pub_key_size, &l_pkey_hash);
+                    l_hash_string = dap_chain_hash_fast_to_str_new(&l_pkey_hash);
+                }
+                const char c = '1';
+                char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net);
+                if (!l_gdb_group_str) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network");
+                    return -11;
+                }
+                ret = dap_chain_global_db_gr_set(dap_strdup(l_hash_string), (void *)&c, 1, dap_chain_net_get_gdb_group_acl(l_net));
+                DAP_DELETE(l_gdb_group_str);
+                if (!ret) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Can't save public key hash in database");
+                    return -10;
+                }
+                return 0;
+            } else if (strcmp(l_ca_str, "list") == 0 ) {
+                char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net);
+                if (!l_gdb_group_str) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network");
+                    return -11;
+                }
+                size_t l_objs_count;
+                dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_gdb_group_str, &l_objs_count);
+                DAP_DELETE(l_gdb_group_str);
+                dap_string_t *l_reply = dap_string_new("");
+                for (size_t i = 0; i < l_objs_count; i++) {
+                    dap_string_append(l_reply, l_objs[i].key);
+                    dap_string_append(l_reply, "\n");
+                }
+                dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+                *a_str_reply = l_reply->len ? l_reply->str : dap_strdup("No entries found");
+                dap_string_free(l_reply, false);
+                return 0;
+            } else if (strcmp(l_ca_str, "del") == 0 ) {
+                const char *l_hash_string = NULL;
+                dap_chain_node_cli_find_option_val(argv, arg_index, argc, "-hash", &l_hash_string);
+                if (!l_hash_string) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Format should be 'net ca del -hash <hash string>");
+                    return -6;
+                }
+                char *l_gdb_group_str = dap_chain_net_get_gdb_group_acl(l_net);
+                if (!l_gdb_group_str) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Database ACL group not defined for this network");
+                    return -11;
+                }
+                ret = dap_chain_global_db_gr_del((char *)l_hash_string, l_gdb_group_str);
+                DAP_DELETE(l_gdb_group_str);
+                if (!ret) {
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "Cant't find certificate public key hash in database");
+                    return -10;
+                }
+                return 0;
+            } else {
+                dap_chain_node_cli_set_reply_text(a_str_reply,
+                                                  "Subcommand \"ca\" requires one of parameter: add, list, del\n");
+                ret = -5;
+            }
         } else {
             dap_chain_node_cli_set_reply_text(a_str_reply,"Command requires one of subcomand: sync, links\n");
             ret = -1;
@@ -1298,7 +1426,7 @@ static int callback_compare_prioritity_list(const void * a_item1, const void * a
  * @param a_net_name
  * @return
  */
-int s_net_load(const char * a_net_name)
+int s_net_load(const char * a_net_name, uint16_t a_acl_idx)
 {
     dap_config_t *l_cfg=NULL;
     dap_string_t *l_cfg_path = dap_string_new("network/");
@@ -1320,6 +1448,7 @@ int s_net_load(const char * a_net_name)
             return -1;
         }
         PVT(l_net)->load_mode = true;
+        PVT(l_net)->acl_idx = a_acl_idx;
         l_net->pub.gdb_groups_prefix = dap_strdup (
                     dap_config_get_item_str_default(l_cfg , "general" , "gdb_groups_prefix",
                                                     dap_config_get_item_str(l_cfg , "general" , "name" ) ) );
@@ -1351,7 +1480,7 @@ int s_net_load(const char * a_net_name)
                 while(l_groups) {
                     char *l_group_name = l_groups->data;
                     // do not use groups with names like *.del
-                    if(!strstr(l_group_name, ".del")) {
+                    if(dap_fnmatch("*.del", l_group_name, 0)) {
                         const char *l_history_group = dap_chain_global_db_add_history_extra_group(l_group_name,
                                                         PVT(l_net)->gdb_sync_nodes_addrs,
                                                         &PVT(l_net)->gdb_sync_nodes_addrs_count);
@@ -1839,6 +1968,22 @@ dap_chain_net_t * dap_chain_net_by_id( dap_chain_net_id_t a_id)
 
 }
 
+/**
+ * @brief dap_chain_net_by_id
+ * @param a_id
+ * @return
+ */
+uint16_t dap_chain_net_acl_idx_by_id(dap_chain_net_id_t a_id)
+{
+    dap_chain_net_item_t * l_net_item = NULL;
+    HASH_FIND(hh,s_net_items_ids,&a_id,sizeof (a_id), l_net_item );
+    if (l_net_item)
+        return PVT(l_net_item->chain_net)->acl_idx;
+    else
+        return -1;
+
+}
+
 
 /**
  * @brief dap_chain_net_id_by_name
@@ -2397,3 +2542,77 @@ void dap_chain_net_dump_datum(dap_string_t * a_str_out, dap_chain_datum_t * a_da
         }break;
     }
 }
+
+static bool s_net_check_acl(dap_chain_net_t *a_net, dap_chain_hash_fast_t *a_pkey_hash)
+{
+    const char l_path[] = "network/";
+    char l_cfg_path[strlen(a_net->pub.name) + strlen(l_path) + 1];
+    strcpy(l_cfg_path, l_path);
+    strcat(l_cfg_path, a_net->pub.name);
+    dap_config_t *l_cfg = dap_config_open(l_cfg_path);
+    const char *l_auth_type = dap_config_get_item_str(l_cfg, "auth", "type");
+    bool l_authorized = true;
+    if (l_auth_type && !strcmp(l_auth_type, "ca")) {
+        if (dap_hash_fast_is_blank(a_pkey_hash)) {
+            return false;
+        }
+        l_authorized = false;
+        const char *l_auth_hash_str = dap_chain_hash_fast_to_str_new(a_pkey_hash);
+        uint16_t l_acl_list_len = 0;
+        char **l_acl_list = dap_config_get_array_str(l_cfg, "auth", "acl_accept_ca_list", &l_acl_list_len);
+        for (uint16_t i = 0; i < l_acl_list_len; i++) {
+            if (!strcmp(l_acl_list[i], l_auth_hash_str)) {
+                l_authorized = true;
+                break;
+            }
+        }
+        if (!l_authorized) {
+            const char *l_acl_gdb = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_gdb");
+            if (l_acl_gdb) {
+                size_t l_objs_count;
+                dap_global_db_obj_t *l_objs = dap_chain_global_db_gr_load(l_acl_gdb, &l_objs_count);
+                for (size_t i = 0; i < l_objs_count; i++) {
+                    if (!strcmp(l_objs[i].key, l_auth_hash_str)) {
+                        l_authorized = true;
+                        break;
+                    }
+                }
+                dap_chain_global_db_objs_delete(l_objs, l_objs_count);
+            }
+        }
+        if (!l_authorized) {
+            const char *l_acl_chains = dap_config_get_item_str(l_cfg, "auth", "acl_accept_ca_chains");
+            if (l_acl_chains && !strcmp(l_acl_chains, "all")) {
+                dap_list_t *l_certs = dap_cert_get_all_mem();
+                for (dap_list_t *l_tmp = l_certs; l_tmp; l_tmp = dap_list_next(l_tmp)) {
+                    dap_cert_t *l_cert = (dap_cert_t *)l_tmp->data;
+                    size_t l_pkey_size;
+                    uint8_t *l_pkey_ser = dap_enc_key_serealize_pub_key(l_cert->enc_key, &l_pkey_size);
+                    dap_chain_hash_fast_t l_cert_hash;
+                    dap_hash_fast(l_pkey_ser, l_pkey_size, &l_cert_hash);
+                    if (!memcmp(l_pkey_ser, a_pkey_hash, sizeof(dap_chain_hash_fast_t))) {
+                        l_authorized = true;
+                        DAP_DELETE(l_pkey_ser);
+                        break;
+                    }
+                    DAP_DELETE(l_pkey_ser);
+                }
+            }
+        }
+    }
+    return l_authorized;
+}
+
+static uint8_t *dap_chain_net_set_acl(dap_chain_hash_fast_t *a_pkey_hash)
+{
+    uint16_t l_net_count;
+    dap_chain_net_t **l_net_list = dap_chain_net_list(&l_net_count);
+    if (l_net_count) {
+        uint8_t *l_ret = DAP_NEW_SIZE(uint8_t, l_net_count);
+        for (uint16_t i = 0; i < l_net_count; i++) {
+            l_ret[i] = s_net_check_acl(l_net_list[i], a_pkey_hash);
+        }
+        return l_ret;
+    }
+    return NULL;
+}
diff --git a/modules/net/dap_chain_net_bugreport.c b/modules/net/dap_chain_net_bugreport.c
deleted file mode 100644
index 3c284980b6282a707852367cf79caf3495d1d2d1..0000000000000000000000000000000000000000
--- a/modules/net/dap_chain_net_bugreport.c
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Authors:
- * Alexander Lysikov <alexander.lysikov@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * Kelvin Project https://github.com/kelvinblockchain
- * Copyright  (c) 2020
- * All rights reserved.
-
- 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 <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdint.h>
-
-#include "dap_common.h"
-#include "dap_string.h"
-#include "dap_strfuncs.h"
-#include "dap_file_utils.h"
-#include "dap_config.h"
-#include "rand/dap_rand.h"
-
-#include "http_status_code.h"
-#include "dap_http_simple.h"
-#include "dap_enc_http.h"
-#include "dap_chain_net_bugreport.h"
-
-#define LOG_TAG "chain_net_bugreport"
-
-#define BUGREPORT_URL "/bugreport"
-
-static int64_t bugreport_write_to_file(byte_t *a_request_byte, size_t a_request_size)
-{
-    int64_t l_report_number = -2;
-    if(!a_request_byte || !a_request_size)
-        return -1;
-    char *l_dir_str = dap_strdup_printf("%s/var/bugreport", g_sys_dir_path);
-    dap_mkdir_with_parents(l_dir_str);
-
-    const time_t l_timer = time(NULL);
-    struct tm l_tm;
-    localtime_r(&l_timer, &l_tm);
-    randombytes(&l_report_number, sizeof(int64_t));
-    if(l_report_number<0)
-        l_report_number = -l_report_number;
-    // create unique number for bugreport
-    l_report_number -= l_report_number%1000000000000ll;
-    l_report_number+=(int64_t)(l_tm.tm_year - 100)*10000000000;
-    l_report_number+=(int64_t)(l_tm.tm_mon)*100000000;
-    l_report_number+=(int64_t)(l_tm.tm_mday)*1000000;
-    l_report_number+=(int64_t)(l_tm.tm_hour)*10000;
-    l_report_number+=(int64_t)(l_tm.tm_min)*100;
-    l_report_number+=(int64_t)(l_tm.tm_sec);
-    char *l_filename_str = dap_strdup_printf("%s/%02d-%02d-%02d_%02d:%02d:%02d_%08lld.brt", l_dir_str,
-            l_tm.tm_year - 100, l_tm.tm_mon, l_tm.tm_mday,
-            l_tm.tm_hour, l_tm.tm_min, l_tm.tm_sec,
-            l_report_number);
-    FILE *l_fp;
-    if((l_fp = fopen(l_filename_str, "wb")) != NULL) {
-        if(fwrite(a_request_byte, 1, a_request_size, l_fp) != a_request_size)
-            l_report_number = -3;
-        fclose(l_fp);
-    }
-    DAP_DELETE(l_filename_str);
-    DAP_DELETE(l_dir_str);
-    return l_report_number;
-}
-
-/**
- * @brief bugreport_http_proc
- * @param a_http_simple
- * @param a_arg
- */
-static void bugreport_http_proc(struct dap_http_simple *a_http_simple, void * a_arg)
-{
-    // data:text/html,<form action=http://192.168.100.92:8079/bugreport/ method=post><input name=a></form>
-    // data:text/html,<form action=http://cdb.klvn.io/bugreport/ method=post><input name=a></form>
-    log_it(L_DEBUG, "bugreport_http_proc request");
-    http_status_code_t * return_code = (http_status_code_t*) a_arg;
-    //if(dap_strcmp(cl_st->http->url_path, BUGREPORT_URL) == 0 )
-    if(dap_strcmp(a_http_simple->http->action, "GET") == 0) {
-        size_t l_url_len = dap_strlen(a_http_simple->http->url_path);
-        if(!l_url_len) {
-                    a_http_simple->reply = dap_strdup_printf("Unique Bug Report number required)");
-                    *return_code = Http_Status_NotFound;
-                }
-        else{
-            *return_code = Http_Status_OK;
-        }
-        a_http_simple->reply_size = strlen(a_http_simple->reply);
-        *return_code = Http_Status_NotFound;
-    }
-    if(dap_strcmp(a_http_simple->http->action, "POST") == 0) {
-        //a_http_simple->request_byte;
-        //a_http_simple->request_size;
-        //a_http_simple->http->in_content_length;
-
-        int64_t l_bugreport_number = bugreport_write_to_file(a_http_simple->request_byte, a_http_simple->request_size);
-        if(l_bugreport_number >= 0) {
-            a_http_simple->reply = dap_strdup_printf("Bug Report #%020lld saved successfully)", l_bugreport_number);
-        }
-        else {
-            a_http_simple->reply = dap_strdup_printf("Bug Report not saved( code=%lld", l_bugreport_number);
-        }
-        a_http_simple->reply_size = strlen(a_http_simple->reply);
-        *return_code = Http_Status_OK;
-
-    } else {
-        log_it(L_ERROR, "Wrong action '%s' for the request. Must be 'POST'", a_http_simple->http->action);
-        a_http_simple->reply = dap_strdup_printf("Wrong action '%s' for the request. Must be 'POST'",
-                a_http_simple->http->action);
-        a_http_simple->reply_size = strlen(a_http_simple->reply);
-        *return_code = Http_Status_NotFound;
-    }
-}
-
-/**
- * @brief dap_chain_net_bugreport_add_proc
- * @param sh HTTP server instance
- */
-void dap_chain_net_bugreport_add_proc(struct dap_http * sh)
-{
-    const char * url = BUGREPORT_URL;
-    dap_http_simple_proc_add(sh, url, 14096, bugreport_http_proc);
-}
-
diff --git a/modules/net/dap_chain_net_news.c b/modules/net/dap_chain_net_news.c
deleted file mode 100644
index 8c4984ae9bda0160d89c2ad63366971542d85f9b..0000000000000000000000000000000000000000
--- a/modules/net/dap_chain_net_news.c
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Authors:
- * Alexander Lysikov <alexander.lysikov@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * Kelvin Project https://github.com/kelvinblockchain
- * Copyright  (c) 2020
- * All rights reserved.
-
- 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 <stddef.h>
-
-#include "dap_common.h"
-#include "dap_string.h"
-#include "dap_strfuncs.h"
-#include "dap_file_utils.h"
-#include "dap_config.h"
-
-#include "http_status_code.h"
-#include "dap_http_simple.h"
-#include "dap_enc_http.h"
-//#include "<dap_chain_global_db_driver.h>
-#include "dap_chain_global_db.h"
-#include "dap_chain_net_news.h"
-#define LOG_TAG "chain_net_news"
-
-#define NEWS_URL "/news"
-#define GROUP_NEWS "cdb.news"
-#define DEFAULT_LANG "en"
-
-/* Set news in the selected language
- * a_lang - a language like "en", "ru", "fr"
- * a_data_news - news data
- * a_data_news_len length of news
- */
-int dap_chain_net_news_write(const char *a_lang, char *a_data_news, size_t a_data_news_len)
-{
-    if(!a_data_news || !a_data_news_len)
-        return -2;
-    if(!a_lang)
-        a_lang = DEFAULT_LANG;
-    if(dap_chain_global_db_gr_set(dap_strdup(a_lang), a_data_news, a_data_news_len, GROUP_NEWS))
-        return 0;
-    return -1;
-}
-
-/* Get news in the selected language
- * a_lang - a language like "en", "ru", "fr"
- */
-byte_t* dap_chain_net_news_read(const char *a_lang, size_t *a_news_len)
-{
-    if(!a_lang)
-        return NULL;
-    byte_t *l_ret_data = NULL;
-    size_t l_data_len_num = 0;
-    dap_store_obj_t *l_obj = dap_chain_global_db_obj_gr_get(a_lang, &l_data_len_num, GROUP_NEWS);
-    if(l_obj && l_obj->value_len) {
-        l_ret_data = DAP_NEW_Z_SIZE(byte_t, l_obj->value_len);
-        memcpy(l_ret_data, l_obj->value, l_obj->value_len);
-        if(a_news_len)
-            *a_news_len = l_obj->value_len;
-    }
-    dap_store_obj_free(l_obj, l_data_len_num);
-    return l_ret_data;
-}
-
-/**
- * @brief news_http_proc
- * @param a_http_simple
- * @param a_arg
- */
-static void news_http_proc(struct dap_http_simple *a_http_simple, void * a_arg)
-{
-    log_it(L_DEBUG, "news_http_proc request");
-    http_status_code_t * return_code = (http_status_code_t*) a_arg;
-    const char *l_lang = DEFAULT_LANG;
-    if(dap_strcmp(a_http_simple->http->url_path, NEWS_URL)) {
-        l_lang = a_http_simple->http->url_path;
-    }
-
-    if(l_lang)
-    {
-        size_t l_news_data_len = 0;
-        // get news in the selected language
-        byte_t *l_news_data = dap_chain_net_news_read(l_lang, &l_news_data_len);
-        // get news in the default language
-        if(!l_news_data && dap_strcmp(a_http_simple->http->in_query_string, "LocalNewsOnly"))
-            l_news_data = dap_chain_net_news_read(DEFAULT_LANG, &l_news_data_len);
-        if( l_news_data){
-            a_http_simple->reply = l_news_data ;
-            a_http_simple->reply_size = l_news_data_len;
-        }else{
-            a_http_simple->reply = dap_strdup("no news");
-            a_http_simple->reply_size = dap_strlen((char*) a_http_simple->reply) + 1;
-        }
-        *return_code = Http_Status_OK;
-    }
-    else {
-        log_it(L_ERROR, "Wrong request. Must be %s/<lang_code>, example http:/<addr>%s/en", NEWS_URL, NEWS_URL);
-        a_http_simple->reply = dap_strdup_printf("Wrong request. Must be %s/<lang_code>, example http:/<addr>%s/en",
-        NEWS_URL, NEWS_URL);
-        a_http_simple->reply_size = strlen(a_http_simple->reply);
-        *return_code = Http_Status_NotFound;
-    }
-}
-
-/**
- * @brief dap_chain_net_news_add_proc
- * @param sh HTTP server instance
- */
-void dap_chain_net_news_add_proc(struct dap_http * sh)
-{
-    const char * url = NEWS_URL;
-    dap_http_simple_proc_add(sh, url, 14096, news_http_proc);
-}
-
diff --git a/modules/net/dap_chain_net_news.h b/modules/net/dap_chain_net_news.h
index 08bd6ebb2be1eb3024e75431db97c0f7a60d02a2..409063d83e76198351aee2916537c25dcac049ef 100644
--- a/modules/net/dap_chain_net_news.h
+++ b/modules/net/dap_chain_net_news.h
@@ -24,16 +24,4 @@
 
 #include "dap_http.h"
 
-/* Set news in the selected language
- * a_lang - a language like "en", "ru", "fr"
- * a_data_news - news data
- * a_data_news_len length of news
- */
-int dap_chain_net_news_write(const char *a_lang, char *a_data_news, size_t a_data_news_len);
-
-/* Get news in the selected language
- * a_lang - a language like "en", "ru", "fr"
- */
-byte_t* dap_chain_net_news_read(const char *a_lang, size_t *a_news_len);
-
-void dap_chain_net_news_add_proc(struct dap_http * sh);
+int dap_chain_net_news_init(dap_http_t * a_http);
diff --git a/modules/net/dap_chain_node_cli.c b/modules/net/dap_chain_node_cli.c
index 8ff96efa9d66ebc2989019ef650f744a4102e7c4..3ecd46abc29c7c3e132f1f95be4751f774164818 100644
--- a/modules/net/dap_chain_node_cli.c
+++ b/modules/net/dap_chain_node_cli.c
@@ -948,11 +948,6 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
     dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, NULL, "Transaction history (for address or by hash)",
             "tx_history  [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n");
 
-    // vpn client
-    dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, NULL, "VPN client control",
-    "vpn_client [start -addr <server address> -port <server port>| stop | status] -net <net name>\n");
-
-
     // Log
     dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, NULL, "Print log info",
                 "print_log [ts_after <timestamp >] [limit <line numbers>]\n" );
@@ -965,11 +960,6 @@ int dap_chain_node_cli_init(dap_config_t * g_config)
     dap_chain_node_cli_cmd_item_create ("exit", com_exit, NULL, "Stop application and exit",
                 "exit\n" );
 
-//#ifndef _WIN32
-    // News
-    dap_chain_node_cli_cmd_item_create("news", com_news, NULL, "Add News for VPN clients. Language code is a text code like \"en\", \"ru\", \"fr\"",
-            "news [-text <news text> | -file <filename with news>] -lang <language code> \n");
-//#endif
     // create thread for waiting of clients
     pthread_t l_thread_id;
 
diff --git a/modules/net/dap_chain_node_cli_cmd.c b/modules/net/dap_chain_node_cli_cmd.c
index afbd4469e650b599851c52441c2c6f92f89a0128..8e79e0e6a3eb1db6076d153ea77991f539a40ab7 100644
--- a/modules/net/dap_chain_node_cli_cmd.c
+++ b/modules/net/dap_chain_node_cli_cmd.c
@@ -77,7 +77,6 @@
 #include "dap_chain_net_srv.h"
 #ifndef _WIN32
 #include "dap_chain_net_news.h"
-#include "dap_chain_net_vpn_client.h"
 #endif
 #include "dap_chain_cell.h"
 
@@ -1969,6 +1968,35 @@ int com_token_decl_sign(int argc, char ** argv, void *arg_func, char ** a_str_re
     }
 }
 
+void s_com_mempool_list_print_for_chain(const dap_chain_net_t * a_net, const dap_chain_t * a_chain, dap_string_t * a_str_tmp){
+    char * l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(a_chain);
+    if(!l_gdb_group_mempool){
+        dap_string_append_printf(a_str_tmp, "%s.%s: chain not found\n", a_net->pub.name, a_chain->name);
+    }else{
+        size_t l_objs_size = 0;
+        dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool, &l_objs_size);
+        if(l_objs_size > 0)
+            dap_string_append_printf(a_str_tmp, "%s.%s: Found %u records :\n", a_net->pub.name, a_chain->name,
+                    l_objs_size);
+        else
+            dap_string_append_printf(a_str_tmp, "%s.%s: Not found records\n", a_net->pub.name, a_chain->name);
+        for(size_t i = 0; i < l_objs_size; i++) {
+            dap_chain_datum_t * l_datum = (dap_chain_datum_t*) l_objs[i].value;
+            char buf[50];
+            time_t l_ts_create = (time_t) l_datum->header.ts_create;
+            dap_string_append_printf(a_str_tmp, "%s: type_id=%s  data_size=%u ts_create=%s", // \n included in timestamp
+                    l_objs[i].key, c_datum_type_str[l_datum->header.type_id],
+                    l_datum->header.data_size, ctime_r(&l_ts_create, buf));
+
+            dap_chain_net_dump_datum(a_str_tmp, l_datum);
+        }
+
+        dap_chain_global_db_objs_delete(l_objs, l_objs_size);
+    }
+
+    DAP_DELETE(l_gdb_group_mempool);
+}
+
 /**
  * @brief com_token_decl_list
  * @param argc
@@ -1994,43 +2022,14 @@ int com_mempool_list(int argc, char ** argv, void *arg_func, char ** a_str_reply
     }
 
     if(l_net) {
-        char * l_gdb_group_mempool = NULL, *l_gdb_group_mempool_tmp;
-        if(l_chain) {
-            l_gdb_group_mempool = dap_chain_net_get_gdb_group_mempool(l_chain);
-            l_gdb_group_mempool_tmp = l_gdb_group_mempool;
-        }
         dap_string_t * l_str_tmp = dap_string_new(NULL);
 
-        DL_FOREACH(l_net->pub.chains, l_chain) {
-            if(!l_gdb_group_mempool) {
-                l_gdb_group_mempool_tmp = dap_chain_net_get_gdb_group_mempool(l_chain);
-            }
-            size_t l_objs_size = 0;
-            dap_global_db_obj_t * l_objs = dap_chain_global_db_gr_load(l_gdb_group_mempool_tmp, &l_objs_size);
-            if(l_objs_size > 0)
-                dap_string_append_printf(l_str_tmp, "%s.%s: Found %u records :\n", l_net->pub.name, l_chain->name,
-                        l_objs_size);
-            else
-                dap_string_append_printf(l_str_tmp, "%s.%s: Not found records\n", l_net->pub.name, l_chain->name);
-            for(size_t i = 0; i < l_objs_size; i++) {
-                dap_chain_datum_t * l_datum = (dap_chain_datum_t*) l_objs[i].value;
-                char buf[50];
-                time_t l_ts_create = (time_t) l_datum->header.ts_create;
-                dap_string_append_printf(l_str_tmp, "%s: type_id=%s  data_size=%u ts_create=%s", // \n included in timestamp
-                        l_objs[i].key, c_datum_type_str[l_datum->header.type_id],
-                        l_datum->header.data_size, ctime_r(&l_ts_create, buf));
+        if(l_chain)
+            s_com_mempool_list_print_for_chain(l_net, l_chain, l_str_tmp);
+        else
+            DL_FOREACH(l_net->pub.chains, l_chain)
+                    s_com_mempool_list_print_for_chain(l_net, l_chain, l_str_tmp);
 
-                dap_chain_net_dump_datum(l_str_tmp, l_datum);
-            }
-            // Clean up
-            dap_chain_global_db_objs_delete(l_objs, l_objs_size);
-            if (l_gdb_group_mempool_tmp)
-                DAP_DELETE(l_gdb_group_mempool_tmp);
-            // only one time if group defined
-            if(l_gdb_group_mempool) {
-                break;
-            }
-        }
         dap_chain_node_cli_set_reply_text(a_str_reply, l_str_tmp->str);
         dap_string_free(l_str_tmp, false);
 
@@ -3756,206 +3755,3 @@ int com_print_log(int argc, char ** argv, void *arg_func, char **str_reply)
     return 0;
 }
 
-/**
- * Add News for VPN clients
- * news [-text <news text> | -file <filename with news>] -lang <language code>
- */
-int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply)
-{
-    int arg_index = 1;
-    const char * l_str_lang = NULL;
-    const char * l_str_text = NULL;
-    const char * l_str_file = NULL;
-    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-lang", &l_str_lang);
-    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-text", &l_str_text);
-    dap_chain_node_cli_find_option_val(a_argv, arg_index, a_argc, "-file", &l_str_file);
-    if(!l_str_text && !l_str_file) {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "no source of news, add parameter -text or -file");
-        return -1;
-    }
-    char *l_data_news;
-    size_t l_data_news_len = 0;
-    const char *l_from = NULL;
-
-    if(l_str_text) {
-        l_data_news = dap_strdup(l_str_text);
-        l_data_news_len = dap_strlen(l_str_text);
-        l_from = "text";
-    }
-    else if(l_str_file) {
-        if(dap_file_get_contents(l_str_file, &l_data_news,&l_data_news_len)) {
-            l_from = "file";
-        }
-        else{
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Can't read file %s", l_str_file);
-                return -2;
-            }
-    }
-
-    int l_res = dap_chain_net_news_write(l_str_lang, l_data_news, l_data_news_len);
-    if(l_res){
-        dap_chain_node_cli_set_reply_text(a_str_reply, "Error, News cannot be added from %s", l_from);
-        return -3;
-    }
-    dap_chain_node_cli_set_reply_text(a_str_reply, "News added from %s successfully", l_from);
-    return 0;
-}
-
-/**
- * vpn_client command
- *
- * VPN client control
- */
-int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
-{
-#ifndef _WIN32
-    enum {
-        CMD_NONE, CMD_INIT, CMD_START, CMD_STOP, CMD_STATUS
-    };
-    int l_arg_index = 1;
-    // find net
-    dap_chain_net_t *l_net = NULL;
-    if(dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0)
-        return -2;
-
-    int cmd_num = CMD_NONE;
-    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "init", NULL)) {
-        cmd_num = CMD_INIT;
-    }
-    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
-            cmd_num = CMD_START;
-        }
-    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) {
-        cmd_num = CMD_STOP;
-    }
-    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "status", NULL)) {
-        cmd_num = CMD_STATUS;
-    }
-    if(cmd_num == CMD_NONE) {
-        if(!a_argv[1])
-            dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters");
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "parameter %s not recognized", a_argv[1]);
-        return -1;
-    }
-
-    switch (cmd_num)
-    {
-    case CMD_INIT: {
-            const char * l_str_token = NULL; // token name
-            const char * l_str_value_datoshi = NULL;
-            const char * l_str_wallet = NULL; // wallet name
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_str_wallet);
-            if(!l_str_wallet)
-                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-w", &l_str_wallet);
-
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_str_token);
-            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_str_value_datoshi);
-
-            if(!l_str_wallet) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Wallet not defined, use -w <wallet_name> or -wallet <wallet_name> parameter");
-                break;
-            }
-            if(!l_str_token) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Token not defined, use -token <token_name> parameter");
-                break;
-            }
-            if(!l_str_value_datoshi) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi not defined, use -value <value of datoshi> parameter");
-                break;
-            }
-            uint64_t l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 10);
-            if(!l_a_value_datoshi)
-                l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 16);
-            if(!l_a_value_datoshi) {
-                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi have to be more then 0");
-                break;
-            }
-            int l_res = dap_chain_net_vpn_client_update(l_net, l_str_wallet, l_str_token, l_a_value_datoshi);
-            if(!l_res)
-                dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully");
-            else{
-                if(l_res==-3)
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully, but probably not enough founds in the wallet");
-                else
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not init");
-            }
-            return l_res;
-    }
-        break;
-    case CMD_START: {
-        const char * l_str_addr = NULL; // for example, "192.168.100.93"
-        const char * l_str_port = NULL; // for example, "8079"
-        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_str_addr);
-        if(!l_str_addr) {
-            dap_chain_node_cli_set_reply_text(a_str_reply,
-                    "VPN server address not defined, use -addr <vpn server ipv4 address> parameter");
-            break;
-        }
-        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-port", &l_str_port);
-        int l_srv_port = (l_str_port) ? (int) strtoll(l_str_port, 0, 10) : 0;
-        if(!l_srv_port) {
-            dap_chain_node_cli_set_reply_text(a_str_reply,
-                    "VPN server port not defined, use -port <vpn server port>  parameter");
-            break;
-        }
-        int l_res = dap_chain_net_vpn_client_start(l_net, l_str_addr, NULL, l_srv_port);
-        switch (l_res) {
-        case 0:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started successfully");
-            break;
-        case 1:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client already started");
-            break;
-        case -2:
-        case -3:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't connect to VPN server");
-            break;
-        default:
-            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't start VPN client");
-            break;
-        }
-        return l_res;
-    }
-        break;
-    case CMD_STOP: {
-        int res = dap_chain_net_vpn_client_stop();
-        if(!res)
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped successfully");
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped");
-        return res;
-    }
-        break;
-    case CMD_STATUS:
-        {
-        char *l_wallet_name = NULL, *l_str_token = NULL;
-        uint64_t l_value_datoshi = 0;
-        dap_chain_net_vpn_client_get_wallet_info(l_net, &l_wallet_name, &l_str_token, &l_value_datoshi);
-
-        const char *l_status_txt = "";
-        switch (dap_chain_net_vpn_client_status()) {
-        case VPN_CLIENT_STATUS_NOT_STARTED:
-            l_status_txt = "VPN client not started";
-            break;
-        case VPN_CLIENT_STATUS_STARTED:
-            l_status_txt = "VPN client started";
-            break;
-        case VPN_CLIENT_STATUS_STOPPED:
-            l_status_txt = "VPN client stopped";
-            break;
-        case VPN_CLIENT_STATUS_CONN_LOST:
-            l_status_txt = "VPN client lost connection";
-            break;
-        default:
-            l_status_txt = "VPN client status unknown";
-            break;
-        }
-        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nused:\nwallet:%s\nreceipt:%u*1e-9 %s", l_status_txt,
-                l_wallet_name, l_value_datoshi, l_str_token);
-        break;
-    }
-    }
-#endif
-    return 0;
-}
diff --git a/modules/net/dap_chain_node_client.c b/modules/net/dap_chain_node_client.c
index 84d03e43b69081b38ff4302c70bf58ffb51650be..1c3fe94d75e7944768ed291173f5714867dc825a 100644
--- a/modules/net/dap_chain_node_client.c
+++ b/modules/net/dap_chain_node_client.c
@@ -477,6 +477,8 @@ dap_chain_node_client_t* dap_chain_client_connect(dap_chain_node_info_t *a_node_
     l_node_client->remote_node_addr.uint64 = a_node_info->hdr.address.uint64;
     dap_client_set_active_channels(l_node_client->client, a_active_channels);
 
+    //dap_client_set_auth_cert(l_node_client->client, dap_cert_find_by_name("auth")); // TODO provide the certificate choice
+
     int hostlen = 128;
     char host[hostlen];
     if(a_node_info->hdr.ext_addr_v4.s_addr)
diff --git a/modules/net/dap_dns_server.c b/modules/net/dap_dns_server.c
index 33e4b54ef1e92c52e58646c4b552fe3189fc6aa7..143abd7ecdfeddde2e86c5058688b4a139c993ad 100644
--- a/modules/net/dap_dns_server.c
+++ b/modules/net/dap_dns_server.c
@@ -322,6 +322,9 @@ void dap_dns_server_start() {
 }
 
 void dap_dns_server_stop() {
+    if(!s_dns_server)
+        return;
+
     dap_dns_zone_hash_t *current_zone, *tmp;
     HASH_ITER(hh, s_dns_server->hash_table, current_zone, tmp) {
         HASH_DEL(s_dns_server->hash_table, current_zone);
diff --git a/modules/net/include/dap_chain_net.h b/modules/net/include/dap_chain_net.h
index b2cdeef5cbf3823738ad56f9e1b65860c1904a62..941019b63282482e9f5c6cd61dc7d59b69c32412 100644
--- a/modules/net/include/dap_chain_net.h
+++ b/modules/net/include/dap_chain_net.h
@@ -102,6 +102,7 @@ void dap_chain_net_proc_mempool (dap_chain_net_t * a_net);
 
 dap_chain_net_t * dap_chain_net_by_name( const char * a_name);
 dap_chain_net_t * dap_chain_net_by_id( dap_chain_net_id_t a_id);
+uint16_t dap_chain_net_acl_idx_by_id(dap_chain_net_id_t a_id);
 dap_chain_net_id_t dap_chain_net_id_by_name( const char * a_name);
 dap_ledger_t * dap_chain_ledger_by_net_name( const char * a_net_name);
 
@@ -114,8 +115,6 @@ dap_chain_cell_id_t * dap_chain_net_get_cur_cell( dap_chain_net_t * l_net);
 dap_list_t* dap_chain_net_get_link_node_list(dap_chain_net_t * l_net, bool a_is_only_cur_cell);
 dap_list_t* dap_chain_net_get_node_list(dap_chain_net_t * l_net);
 
-void dap_chain_net_links_connect(dap_chain_net_t * a_net);
-
 typedef enum dap_chain_net_tx_search_type {
     /// Search local, in memory, possible load data from drive to memory
     TX_SEARCH_TYPE_LOCAL,
diff --git a/modules/net/include/dap_chain_net_bugreport.h b/modules/net/include/dap_chain_net_bugreport.h
index 00f4c2d9f6d8d5271b8f80a76591e107232a1020..c930015f0b79897ba7502452b74da80595b89872 100644
--- a/modules/net/include/dap_chain_net_bugreport.h
+++ b/modules/net/include/dap_chain_net_bugreport.h
@@ -24,4 +24,4 @@
 
 #include "dap_http.h"
 
-void dap_chain_net_bugreport_add_proc(struct dap_http * sh);
+int dap_chain_net_bugreport_init(dap_http_t * a_http);
diff --git a/modules/net/include/dap_chain_node_cli_cmd.h b/modules/net/include/dap_chain_node_cli_cmd.h
index dc5f569568a615c76df8732a082f3a29e8208b84..a5be2cd0f863195bf39ccb6ea53c1f5ac78f9a03 100644
--- a/modules/net/include/dap_chain_node_cli_cmd.h
+++ b/modules/net/include/dap_chain_node_cli_cmd.h
@@ -133,14 +133,6 @@ int com_stats(int argc, char ** argv, void *arg_func, char **str_reply);
 
 int com_exit(int argc, char ** argv, void *arg_func, char **str_reply);
 
-//#ifndef _WIN32
-// Add News for VPN clients
-int com_news(int a_argc, char ** a_argv, void *a_arg_func, char **a_str_reply);
-//#endif
-
-// vpn_client command
-int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
-
 int com_mempool_delete(int argc, char ** argv, void *arg_func, char ** a_str_reply);
 int com_mempool_list(int argc, char ** argv, void *arg_func, char ** a_str_reply);
 int com_mempool_proc(int argc, char ** argv, void *arg_func, char ** a_str_reply);
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn.c b/modules/service/vpn/dap_chain_net_srv_vpn.c
index 666a5dc2e3f855b525743730dbd1d8cc58d88116..10db028050013b28ac642d57ce77e109cad90cdd 100644
--- a/modules/service/vpn/dap_chain_net_srv_vpn.c
+++ b/modules/service/vpn/dap_chain_net_srv_vpn.c
@@ -510,7 +510,6 @@ static int s_callback_response_success(dap_chain_net_srv_t * a_srv, uint32_t a_u
     pthread_rwlock_init(&l_usage_client->rwlock,NULL);
 
     memcpy(l_usage_client->receipt, l_receipt, l_receipt_size);
-
     pthread_rwlock_wrlock(&s_clients_rwlock);
     HASH_ADD(hh, s_clients,usage_id,sizeof(a_usage_id),l_usage_client);
 
@@ -686,7 +685,7 @@ void s_new(dap_stream_ch_t* a_stream_ch, void* a_arg)
  */
 void srv_ch_vpn_delete(dap_stream_ch_t* ch, void* arg)
 {
-    log_it(L_DEBUG, "ch_sf_delete() for %s", ch->stream->conn->hostaddr);
+    log_it(L_DEBUG, "ch_sf_delete() for %s", ch->stream->conn->s_ip);
     dap_chain_net_srv_ch_vpn_t * l_ch_vpn = CH_VPN(ch);
     dap_chain_net_srv_vpn_t * l_srv_vpn =(dap_chain_net_srv_vpn_t *) l_ch_vpn->net_srv->_inhertor;
     pthread_mutex_lock(&(l_ch_vpn->mutex));
@@ -703,7 +702,6 @@ void srv_ch_vpn_delete(dap_stream_ch_t* ch, void* arg)
             l_is_unleased = true;
         pthread_rwlock_unlock(& s_raw_server_rwlock);
     }
-
     pthread_rwlock_wrlock(&s_clients_rwlock);
     if(s_ch_vpn_addrs) {
         HASH_DEL(s_ch_vpn_addrs, l_ch_vpn);
@@ -720,7 +718,6 @@ void srv_ch_vpn_delete(dap_stream_ch_t* ch, void* arg)
     HASH_FIND(hh,s_clients, &l_ch_vpn->usage_id,sizeof(l_ch_vpn->usage_id),l_usage_client );
     if (l_usage_client){
         pthread_rwlock_wrlock(&l_usage_client->rwlock);
-
         l_usage_client->ch_vpn = NULL; // NULL the channel, nobody uses that indicates
         pthread_rwlock_unlock(&l_usage_client->rwlock);
     }
@@ -929,7 +926,7 @@ void s_ch_packet_in(dap_stream_ch_t* a_ch, void* a_arg)
 
     //if ( pkt->hdr.type == DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_CLIENT )
     //    dap_chain_net_vpn_client_pkt_in( a_ch, l_pkt);
-    if(l_pkt->hdr.type == DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_DATA) {
+    if(l_pkt->hdr.type != DAP_STREAM_CH_PKT_TYPE_NET_SRV_VPN_CLIENT) {
         static bool client_connected = false;
         ch_vpn_pkt_t * l_vpn_pkt = (ch_vpn_pkt_t *) l_pkt->data;
         size_t l_vpn_pkt_size = l_pkt->hdr.size - sizeof (l_vpn_pkt->header);
@@ -1611,7 +1608,7 @@ void* srv_ch_sf_thread_raw(void *arg)
                             s_update_limits(l_ch_vpn->ch,l_srv_session,l_usage, l_read_ret);
                         }
                     }
-                    pthread_rwlock_unlock(&s_clients_rwlock);
+                    pthread_rwlock_unlock(&s_clients_rwlock);\
                 }
             }/*else {
              log_it(L_CRITICAL,"select() has no tun handler in the returned set");
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c
deleted file mode 100644
index 648b707b267e9aa3957dd463eab892da7cc36551..0000000000000000000000000000000000000000
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Authors:
- * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * CellFrame       https://cellframe.net
- * Sources         https://gitlab.demlabs.net/cellframe
- * Copyright  (c) 2017-2019
- * All rights reserved.
-
- This file is part of CellFrame SDK the open source project
-
-    CellFrame SDK 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.
-
-    CellFrame SDK 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 CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
-*/
-#include "utlist.h"
-#include "dap_common.h"
-#include "dap_config.h"
-#include "dap_enc_http.h"
-#include "dap_enc_base64.h"
-#include "dap_http.h"
-
-#include "dap_chain.h"
-#include "dap_chain_net.h"
-#include "dap_chain_ledger.h"
-#include "dap_chain_wallet.h"
-#include "dap_chain_datum_tx.h"
-#include "dap_chain_datum_tx_in.h"
-#include "dap_chain_datum_tx_in_cond.h"
-#include "dap_chain_datum_tx_out_cond.h"
-#include "dap_chain_datum_tx_out.h"
-#include "dap_chain_datum_tx_pkey.h"
-#include "dap_chain_datum_tx_receipt.h"
-#include "dap_chain_datum_tx_sig.h"
-#include "dap_chain_global_db.h"
-
-#include "dap_chain_node_cli.h"
-
-#include "dap_chain_mempool.h"
-#include "dap_pkey.h"
-
-#include "dap_chain_net_srv_vpn.h"
-#include "dap_chain_net_srv_vpn_cdb.h"
-#include "dap_chain_net_srv_vpn_cdb_auth.h"
-#include "dap_chain_net_srv_vpn_cdb_server_list.h"
-
-
-#define LOG_TAG "dap_chain_net_srv_vpn_cdb"
-
-#define DB_URL "/db"
-#define NODELIST_URL "/nodelist"
-
-typedef struct tx_cond_template{
-    char * wallet_name;
-    dap_chain_wallet_t * wallet;
-
-    long double value_coins;
-    uint128_t value_datoshi;
-
-    char token_ticker[DAP_CHAIN_TICKER_SIZE_MAX];
-    char * net_name;
-    dap_chain_net_t * net;
-    dap_ledger_t * ledger;
-    time_t min_time; // Minimum time between transactions
-    struct tx_cond_template *prev, *next;
-} tx_cond_template_t;
-
-static tx_cond_template_t *s_tx_cond_templates = NULL;
-const char *c_wallets_path = NULL;
-
-static int s_cli_vpn_cdb(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_init
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_init(dap_http_t * a_http)
-{
-    int ret=0;
-    c_wallets_path = dap_chain_wallet_get_path(g_config);
-    if (dap_config_get_item_bool_default( g_config,
-                                                                "cdb",
-                                                                "servers_list_enabled",
-                                                                false)) {
-
-        if (dap_chain_net_srv_vpn_cdb_server_list_init() != 0) {
-            log_it(L_CRITICAL,"Can't init vpn servers list");
-            return -10;
-        }
-    }
-
-    dap_chain_node_cli_cmd_item_create ("vpn_cdb", s_cli_vpn_cdb, NULL, "VPN Central DataBase (CDB) commands",
-        "vpn_cdb user create --login <Login> --password <Password> [--first_name <First Name] [--last_name <Last Name>] [--email <Email>]"
-        "[--acive_days <Setup active day thats left for user >]\n"
-            "\tCreate user with login, password and some more optional fields\n\n"
-        "vpn_cdb user update --login <Login> [--password <Password>] [--first_name <First Name] [--last_name <Last Name>] [--email <Email>]"
-                             "[--active_days <Setup active days that left for user >]\n"
-            "\tUpdate existent user\n"
-        "vpn_cdb user delete --login <Login>\n"
-            "\tDelete user by login\n"
-        "vpn_cdb user show --login <Login>\n"
-            "\tShow user fields by login\n"
-        "vpn_cdb user check --login <Login> --password <Password>\n"
-            "\tCompare <Password> with stored in GDB for <Login>\n"
-        "vpn_cdb user list\n"
-            "\tShow all users\n"
-        "vpn_cdb serial generate -n <number of serial keys>] [-acive_days <active days that left for serial>]\n"
-            "\tGenerate new serial keys\n"
-        "vpn_cdb serial list [-n <How many show serial keys>] [-shift <How many skip serial keys>] [-nototal]\n"
-            "\tShow serial keys\n"
-        "vpn_cdb serial update -serial <serial keys> -acive_days <active days that left for serial>\n"
-            "\tEdit serial key\n"
-                                        );
-
-    // Load all chain networks
-    if (dap_config_get_item_bool_default( g_config,
-                                                        "cdb",
-                                                        "servers_list_enabled",
-                                                        false)) {
-        dap_chain_net_srv_vpn_cdb_server_list_add_proc ( a_http, NODELIST_URL);
-    }
-    if (dap_config_get_item_bool_default( g_config,"cdb_auth","enabled",false) ){
-
-        ret = dap_chain_net_srv_vpn_cdb_auth_init(  dap_config_get_item_str_default(g_config,"cdb_auth","domain","cdb"),
-                                              dap_config_get_item_str_default(g_config,"cdb_auth","mode","passwd"),
-                                              dap_config_get_item_bool_default(g_config,"cdb_auth","registration_open",false));
-        if(ret<0)
-            return ret;
-        dap_chain_net_srv_vpn_cdb_auth_add_proc( a_http , DB_URL );
-
-        // Produce transaction for authorized users
-        if (dap_config_get_item_bool_default( g_config,
-                                                            "cdb_auth",
-                                                            "tx_cond_create",
-                                                            false)) {
-            // Parse tx cond templates
-            uint16_t l_tx_cond_tpls_count = 0;
-
-            /* ! IMPORTANT ! This fetch is single-action and cannot be further reused, since it modifies the stored config data
-             * ! it also must NOT be freed within this module !
-             */
-            char **l_tx_cond_tpls = dap_config_get_array_str(g_config, "cdb_auth", "tx_cond_templates", &l_tx_cond_tpls_count);
-            if (l_tx_cond_tpls_count == 0) {
-                log_it( L_ERROR, "No condition tpl, can't setup auth callback");
-                return -5;
-            }
-
-            for (size_t i = 0 ; i < l_tx_cond_tpls_count; i++) {
-                tx_cond_template_t *l_tx_cond_template = DAP_NEW_Z(tx_cond_template_t);
-
-                // Parse template entries
-                short l_step = 0;
-                char *ctx;
-                for (char *l_tpl_token = strtok_r(l_tx_cond_tpls[i], ":", &ctx); l_tpl_token || l_step == 5; l_tpl_token = strtok_r(NULL, ":", &ctx), ++l_step) {
-                    switch (l_step) {
-                    case 0:
-                        if(!(l_tx_cond_template->wallet = dap_chain_wallet_open(l_tpl_token, c_wallets_path))) {
-                            log_it(L_ERROR, "Can't open wallet \"%s\"", l_tpl_token);
-                            DAP_DELETE(l_tx_cond_template);
-                            break;
-                        }
-                        l_tx_cond_template->wallet_name = l_tpl_token;
-                        continue;
-                    case 1:
-                        if (!(l_tx_cond_template->value_coins = strtold(l_tpl_token, NULL))) {
-                            log_it(L_ERROR, "Error parsing tpl: text on 2nd position \"%s\" is not a number", l_tpl_token);
-                            DAP_DELETE(l_tx_cond_template->wallet);
-                            DAP_DELETE(l_tx_cond_template);
-                            l_step = 0;
-                            break;
-                        }
-                        l_tx_cond_template->value_datoshi = dap_chain_coins_to_balance(l_tx_cond_template->value_coins);
-                        continue;
-                    case 2:
-                        if (!(l_tx_cond_template->min_time = (time_t)atoll(l_tpl_token))) {
-                            log_it(L_ERROR, "Error parsing tpl: text on 3d position \"%s\" is not a number", l_tpl_token);
-                            DAP_DELETE(l_tx_cond_template->wallet);
-                            DAP_DELETE(l_tx_cond_template);
-                            l_step = 0;
-                            break;
-                        }
-                        continue;
-                    case 3:
-                        dap_stpcpy(l_tx_cond_template->token_ticker, l_tpl_token);
-                        continue;
-                    case 4:
-                        if (!(l_tx_cond_template->net = dap_chain_net_by_name(l_tpl_token))
-                                || !(l_tx_cond_template->ledger = dap_chain_ledger_by_net_name(l_tpl_token)))
-                        {
-                            log_it(L_ERROR, "Can't open network \"%s\" or ledger in it", l_tpl_token);
-                            DAP_DELETE(l_tx_cond_template->wallet);
-                            DAP_DELETE(l_tx_cond_template);
-                            l_step = 0;
-                            break;
-                        }
-                        l_tx_cond_template->net_name = l_tpl_token;
-                        continue;
-                    case 5:
-                        log_it(L_INFO, "Condition template correct, added to list");
-                        DL_APPEND(s_tx_cond_templates, l_tx_cond_template);
-                        break;
-                    default:
-                        break;
-                    }
-                    log_it(L_DEBUG, "Done with tpl item %d", i);
-                    break; // double break exits tokenizer loop and steps to next tpl item
-                }
-            }
-            if (!s_tx_cond_templates) ret = -1;
-        } else {
-            log_it(L_INFO, "No conditional transactions, provide VPN service for free");
-        }
-    }
-    return ret;
-}
-
-static int s_cli_vpn_cdb(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
-{
-    const char *l_user_str = NULL;
-    const char *l_serial_add_param_str = NULL;
-    int l_arg_index = 1;
-    int l_ret = -1;
-
-    int l_user_pos = dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "user", &l_user_str);
-    int l_serial_pos = dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "serial", &l_serial_add_param_str);
-
-
-    // Selected 'user' subcommand
-    if ( l_user_str ){
-        l_ret = 0;
-        return dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(l_user_str,l_arg_index,  a_argc,  a_argv,a_str_reply);
-    }
-    // Selected 'serial' subcoummand
-    else if(l_serial_add_param_str) {
-        l_ret = 0;
-        return dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(l_serial_add_param_str, l_arg_index, a_argc, a_argv, a_str_reply);
-    }
-    else {
-        if(l_user_pos || l_user_pos)
-            dap_chain_node_cli_set_reply_text(a_str_reply, "require additional subcommand, see 'help vpn_cdb'");
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "unknown subcommand, use 'user' or 'serial'", l_user_str);
-    }
-    return l_ret;
-}
-
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_deinit
- */
-void dap_chain_net_srv_vpn_cdb_deinit()
-{
-
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_after
- * @param a_delegate
- * @param a_login
- * @param a_pkey_b64
- */
-void dap_chain_net_srv_vpn_cdb_auth_after(enc_http_delegate_t* a_delegate, const char * a_login, const char * a_pkey_b64 )
-{
-#ifndef __ANDROID__
-
-    dap_enc_key_t *l_client_key = NULL;
-    byte_t *l_pkey_raw = NULL;
-    size_t l_pkey_raw_size = 0;
-    log_it( L_DEBUG, "Authorized, now need to create conditioned transaction if not present key_len=%d", dap_strlen( a_pkey_b64));
-    {
-        size_t l_pkey_b64_length = dap_strlen( a_pkey_b64);
-        l_pkey_raw = DAP_NEW_Z_SIZE(byte_t,l_pkey_b64_length);
-        memset(l_pkey_raw, 0, l_pkey_b64_length);
-        l_pkey_raw_size = dap_enc_base64_decode(a_pkey_b64, l_pkey_b64_length, l_pkey_raw, DAP_ENC_DATA_TYPE_B64_URLSAFE);
-        char *l_pkey_gdb_group =  dap_strdup_printf( "cdb.%s.pkey", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
-        log_it(L_DEBUG, "Pkey group '%s'", l_pkey_gdb_group);
-        dap_chain_global_db_gr_set( dap_strdup(a_login), l_pkey_raw, l_pkey_raw_size, l_pkey_gdb_group);
-
-        l_client_key = dap_enc_key_new(DAP_ENC_KEY_TYPE_SIG_TESLA);
-        int l_res = dap_enc_key_deserealize_pub_key(l_client_key, l_pkey_raw, l_pkey_raw_size);
-        // bad pkey
-        if(l_res){
-            log_it(L_WARNING, "dap_enc_key_deserealize_priv_key='%d'", l_res);
-            DAP_DELETE(l_pkey_raw);
-            l_pkey_raw_size = 0;
-            l_pkey_raw = NULL;
-        }
-        DAP_DELETE(l_pkey_gdb_group);
-    }
-
-    tx_cond_template_t *l_tpl;
-    DL_FOREACH(s_tx_cond_templates, l_tpl) {
-        size_t l_gdb_group_size = 0;
-
-        // Try to load from gdb
-        //char *l_tx_cond_gdb_group =  dap_strdup_printf( "%s.%s.tx_cond", l_tpl->net->pub.name, DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
-        char *l_tx_cond_gdb_group =  dap_strdup_printf( "cdb.%s.tx_cond", DAP_CHAIN_NET_SRV_VPN_CDB_GDB_PREFIX);
-        log_it(L_DEBUG, "Checkout group %s", l_tx_cond_gdb_group);
-        // get key for tx_cond
-        char *l_user_key;
-        {
-            dap_chain_hash_fast_t l_hash = { 0 };
-            char *l_key_hash_str = NULL;
-            if(dap_hash_fast(a_pkey_b64, dap_strlen(a_pkey_b64), &l_hash))
-                l_key_hash_str = dap_chain_hash_fast_to_str_new(&l_hash);
-            l_user_key = dap_strdup_printf("%s/%s", a_login, l_key_hash_str);
-            DAP_DELETE(l_key_hash_str);
-        }
-        log_it(L_DEBUG, "\ndbg l_user_key=%s\n", l_user_key);
-        dap_chain_hash_fast_t *l_tx_cond_hash = (dap_chain_hash_fast_t*) dap_chain_global_db_gr_get(l_user_key, &l_gdb_group_size, l_tx_cond_gdb_group);
-
-        // Check for entry size
-        if (l_gdb_group_size && l_gdb_group_size != sizeof(dap_chain_hash_fast_t)) {
-            log_it(L_ERROR, "Wrong size of tx condition on database (%zd but expected %zd), may be old entry",
-                   l_gdb_group_size, sizeof(dap_chain_hash_fast_t));
-        }
-
-        time_t l_tx_cond_ts = 0;
-        // If loaded lets check is it spent or not
-        if ( l_tx_cond_hash ){
-            log_it(L_DEBUG, "2791: Search for unspent tx, net %s", l_tpl->net_name);
-            dap_chain_datum_tx_t *l_tx = dap_chain_net_get_tx_by_hash(l_tpl->net, l_tx_cond_hash, TX_SEARCH_TYPE_NET_UNSPENT);
-            if ( !l_tx ){ // If not found - all outs are used. Create new one
-                // pass all chains
-                l_tx = dap_chain_net_get_tx_by_hash(l_tpl->net, l_tx_cond_hash, TX_SEARCH_TYPE_NET);
-                DAP_DELETE(l_tx_cond_hash);
-                l_tx_cond_hash = NULL;
-                if ( l_tx ){
-                    l_tx_cond_ts =(time_t) l_tx->header.ts_created;
-                    log_it(L_DEBUG, "2791: got some tx, created %d", l_tx->header.ts_created);
-                }
-            }
-        }
-
-        // Try to create condition
-        if (! l_tx_cond_hash ) {
-            dap_chain_wallet_t *l_wallet_from = l_tpl->wallet;
-            log_it(L_DEBUG, "Create tx from wallet %s", l_wallet_from->name);
-            dap_enc_key_t *l_key_from = dap_chain_wallet_get_key(l_wallet_from, 0);
-
-
-            //dap_chain_cell_id_t *xccell = dap_chain_net_get_cur_cell(l_tpl->net);
-            //uint64_t uint64 =dap_chain_net_get_cur_cell(l_tpl->net)->uint64;
-
-
-            // where to take coins for service
-            dap_chain_addr_t *l_addr_from = dap_chain_wallet_get_addr(l_wallet_from, l_tpl->net->pub.id);
-            dap_chain_net_srv_price_unit_uid_t l_price_unit = { .enm = SERV_UNIT_SEC };
-            dap_chain_net_srv_uid_t l_srv_uid = { .uint64 = DAP_CHAIN_NET_SRV_VPN_ID };
-            l_tx_cond_hash = dap_chain_proc_tx_create_cond(l_tpl->net, l_key_from, l_client_key, l_addr_from, l_tpl->token_ticker,
-                                                       (uint64_t) l_tpl->value_datoshi, 0, l_price_unit, l_srv_uid, 0, l_pkey_raw, l_pkey_raw_size);
-            char *l_addr_from_str = dap_chain_addr_to_str( l_addr_from );
-            DAP_DELETE( l_addr_from);
-            if (!l_tx_cond_hash) {
-                log_it(L_ERROR, "Can't create condition for user");
-            } else {
-                // save transaction for login
-                dap_chain_global_db_gr_set(l_user_key, l_tx_cond_hash, sizeof(dap_chain_hash_fast_t), l_tx_cond_gdb_group);
-                log_it(L_NOTICE, "User \"%s\": created conditioned transaction from %s(%s) on "
-                                , a_login, l_tpl->wallet_name, l_addr_from_str);
-            }
-            DAP_DELETE( l_addr_from_str );
-        }
-        DAP_DELETE(l_user_key);
-        // dbg
-        //dap_ledger_t * l_ledger = dap_chain_ledger_by_net_name( l_tpl->net->pub.name);
-        //dap_chain_datum_tx_t *l_tx = dap_chain_ledger_tx_find_by_hash( l_ledger, l_tx_cond_hash);
-
-        // If we loaded or created hash
-        if( l_tx_cond_hash ){
-            char * l_tx_cond_hash_str = dap_chain_hash_fast_to_str_new(l_tx_cond_hash);
-            enc_http_reply_f(a_delegate,"\t<tx_cond_tpl>\n");
-            //enc_http_reply_f(a_delegate,"\t\t<net>%s</net>\n",l_tpl->net_name);
-            enc_http_reply_f(a_delegate,"\t\t<net>0x%x</net>\n",l_tpl->net->pub.id.uint64);
-            enc_http_reply_f(a_delegate,"\t\t<token>%s</token>\n",l_tpl->token_ticker);
-            enc_http_reply_f(a_delegate,"\t\t<tx_cond>%s</tx_cond>\n",l_tx_cond_hash_str);
-            enc_http_reply_f(a_delegate,"\t</tx_cond_tpl>\n");
-            DAP_DELETE(l_tx_cond_hash);
-            DAP_DELETE(l_tx_cond_hash_str);
-        }
-
-        DAP_DELETE(l_tx_cond_gdb_group);
-    }
-    if (l_client_key)
-        dap_enc_key_delete(l_client_key);
-#endif
-}
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
deleted file mode 100644
index e44fbaf2f51da62c68e7c5885f0a5342098ad010..0000000000000000000000000000000000000000
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_auth.c
+++ /dev/null
@@ -1,1220 +0,0 @@
-/*
- * Authors:
- * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * CellFrame       https://cellframe.net
- * Sources         https://gitlab.demlabs.net/cellframe
- * Copyright  (c) 2017-2020
- * All rights reserved.
-
- This file is part of CellFrame SDK the open source project
-
-    CellFrame SDK 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.
-
-    CellFrame SDK 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 CellFrame SDK based project.  If not, see <http://www.gnu.org/licenses/>.
-*/
-
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/wait.h>
-#include <rand/dap_rand.h>
-
-#include <time.h>
-
-#include "dap_common.h"
-#include "dap_string.h"
-#include "dap_strfuncs.h"
-#include "dap_client_remote.h"
-
-#include "dap_http.h"
-#include "dap_http_client.h"
-#include "dap_http_simple.h"
-
-#include "dap_enc.h"
-#include "dap_enc_key.h"
-#include "dap_enc_ks.h"
-#include "dap_enc_http.h"
-#include "dap_enc_base64.h"
-#include "dap_server.h"
-
-#include "dap_chain_node_cli.h"
-#include "dap_chain_global_db.h"
-
-#include "http_status_code.h"
-
-#include "dap_chain_net_srv_vpn_cdb.h"
-#include "dap_chain_net_srv_vpn_cdb_auth.h"
-
-#define LOG_TAG "dap_chain_net_srv_vpn_cdb_auth"
-
-#define OP_CODE_LOGIN_INCORRECT_PSWD "0xf2"
-#define OP_CODE_LOGIN_INCORRECT_SIGN "0xf2"
-#define OP_CODE_NOT_FOUND_LOGIN_IN_DB "0xf3"
-#define OP_CODE_SUBSCRIBE_EXPIRIED "0xf4"
-#define OP_CODE_INCORRECT_SYMOLS "0xf6"
-#define OP_CODE_LOGIN_INACTIVE  "0xf7"
-#define OP_CODE_SERIAL_ACTIVED  "0xf8"
-
-
-dap_enc_http_callback_t s_callback_success = NULL;
-
-static char * s_domain = NULL;
-static char * s_group_users = NULL;
-static char * s_group_serials = NULL;
-static char * s_group_serials_activated = NULL;
-
-static char * s_group_password = NULL;
-static char * s_group_first_name = NULL;
-static char * s_group_last_name = NULL;
-static char * s_group_email = NULL;
-static char * s_group_ts_updated = NULL;
-static char * s_group_ts_last_login = NULL;
-static char * s_group_cookies = NULL;
-static char * s_group_cookie = NULL;
-static char * s_group_ts_active_till = NULL;
-
-static char * s_salt_str = "Ijg24GAS56h3hg7hj245b";
-
-static bool s_is_registration_open = false;
-static bool s_mode_passwd = true;
-
-static int s_input_validation(const char * str);
-static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg);
-static void s_http_enc_proc_key(enc_http_delegate_t *a_delegate, void * a_arg);
-static void s_http_proc(dap_http_simple_t *a_http_simple, void * arg );
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_init
- * @param a_domain
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_init (const char * a_domain, const char * a_mode, bool a_is_registration_open)
-{
-    s_is_registration_open = a_is_registration_open;
-
-    s_domain = dap_strdup(a_domain);
-
-    // Prefix for gdb groups
-    s_group_users = dap_strdup_printf("cdb.%s.users",s_domain);
-    s_group_serials = dap_strdup_printf("cdb.%s.serials",s_domain);
-    s_group_serials_activated = dap_strdup_printf("cdb.%s.serials_activated",s_domain);
-
-    // Cookie -> login
-    s_group_cookies = dap_strdup_printf("cdb.%s.cookies",s_domain);
-
-    // mode: passwd or serial
-    if(!dap_strcmp(a_mode, "serial"))
-        s_mode_passwd = false;
-    else if(!dap_strcmp(a_mode, "passwd"))
-        s_mode_passwd = true;
-    else{
-        log_it( L_ERROR, "Unknown cdb mode=%s", a_mode);
-        return -1;
-    }
-
-    // Login -> Password, First Name, Last Name, Email, Cookie,Timestamp Last Update, Timestamp Last Login
-    s_group_password = dap_strdup_printf("%s.password",s_group_users);
-    s_group_first_name = dap_strdup_printf("%s.first_name",s_group_users);
-    s_group_last_name = dap_strdup_printf("%s.last_name",s_group_users);
-    s_group_email = dap_strdup_printf("%s.email",s_group_users);
-    s_group_cookie  = dap_strdup_printf("%s.cookie",s_group_users);
-    s_group_ts_updated = dap_strdup_printf("%s.ts_updated",s_group_users);
-    s_group_ts_last_login = dap_strdup_printf("%s.ts_last_login",s_group_users);
-    s_group_ts_active_till = dap_strdup_printf("%s.ts_active_till",s_group_users);
-
-    return 0;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_deinit
- */
-void dap_chain_net_srv_vpn_cdb_auth_deinit()
-{
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_set_callback
- * @param a_callback_success
- */
-void dap_chain_net_srv_vpn_cdb_auth_set_callback(dap_enc_http_callback_t a_callback_success)
-{
-    s_callback_success = a_callback_success;
-}
-
-/*
- * Convert XXXXXXXXXXXXXXXX -> XXXX-XXXX-XXXX-XXXX
- */
-static char* make_fullserial(const char * a_serial)
-{
-    if(dap_strlen(a_serial)!=16)
-        return dap_strdup(a_serial);
-    return dap_strdup_printf("%c%c%c%c-%c%c%c%c-%c%c%c%c-%c%c%c%c",
-            a_serial[0], a_serial[1], a_serial[2], a_serial[3],
-            a_serial[4], a_serial[5], a_serial[6], a_serial[7],
-            a_serial[8], a_serial[9], a_serial[10], a_serial[11],
-            a_serial[12], a_serial[13], a_serial[14], a_serial[15]
-            );
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_check_password
- * @param a_login
- * @param a_password
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_check_login(const char * a_login, const char * a_password)
-{
-    int l_ret;
-
-    size_t l_tmp_size=0;
-    dap_chain_hash_fast_t *l_gdb_password_hash;
-    if ( (l_gdb_password_hash = (dap_chain_hash_fast_t*) dap_chain_global_db_gr_get (
-             a_login,&l_tmp_size  ,s_group_password ) ) ==NULL ){
-        // No user in database
-        return -1;
-    }
-
-    char * l_hash_str = dap_strdup_printf("%s%s",a_password, s_salt_str );
-    dap_chain_hash_fast_t l_password_hash = {0};
-    dap_hash_fast(l_hash_str,dap_strlen(l_hash_str), &l_password_hash );
-    DAP_DELETE(l_hash_str);
-
-    l_ret = (memcmp(&l_password_hash, l_gdb_password_hash,sizeof (l_password_hash) ) == 0)? 0: -2;
-    DAP_DELETE(l_gdb_password_hash);
-
-    // if password check passed lets see is it active or not
-    if ( l_ret == 0){
-        time_t *l_ts_active_till= (time_t*) dap_chain_global_db_gr_get( a_login, &l_tmp_size, s_group_ts_active_till );
-        if ( l_ts_active_till ){
-            if ( *l_ts_active_till < time(NULL) )
-                l_ret = -4;
-        }else
-            l_ret = -3;
-    }
-    return l_ret;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_activate_serial
- * @param a_login
- * @param a_password
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_activate_serial(const char * a_serial_raw, const char * a_serial, const char * a_sign, const char * a_pkey)
-{
-    int l_ret = -1;
-    if(!a_sign || !a_pkey)
-        return -2;//OP_CODE_LOGIN_INCORRECT_SIGN
-    dap_serial_key_t *l_serial_key = dap_chain_net_srv_vpn_cdb_auth_get_serial_param(a_serial, NULL);
-    // not found
-    if(!l_serial_key)
-        return -1;//OP_CODE_NOT_FOUND_LOGIN_IN_DB
-    // already activated
-    if(l_serial_key->header.activated) {
-        l_ret = 0;// OK
-    }
-    else {
-        // check sign
-        int l_res = 0;
-        byte_t *l_pkey_raw = NULL;
-        size_t l_pkey_raw_size = 0;
-        {
-            // deserealize pkey
-            dap_enc_key_t *l_client_key = NULL;
-            size_t l_pkey_length = dap_strlen(a_pkey);
-            l_pkey_raw = DAP_NEW_Z_SIZE(byte_t, l_pkey_length);
-            memset(l_pkey_raw, 0, l_pkey_length);
-            l_pkey_raw_size = dap_enc_base64_decode(a_pkey, l_pkey_length, l_pkey_raw, DAP_ENC_DATA_TYPE_B64_URLSAFE);
-            l_client_key = dap_enc_key_new(DAP_ENC_KEY_TYPE_SIG_TESLA);
-            l_res = dap_enc_key_deserealize_pub_key(l_client_key, l_pkey_raw, l_pkey_raw_size);
-            // verify sign
-            if(!l_res) {
-                byte_t *l_sign_raw = NULL;
-                size_t l_sign_length = dap_strlen(a_sign);
-                l_sign_raw = DAP_NEW_Z_SIZE(byte_t, l_sign_length*2);
-                size_t l_sign_raw_size = dap_enc_base64_decode(a_sign, l_sign_length, l_sign_raw, DAP_ENC_DATA_TYPE_B64_URLSAFE);
-                dap_sign_t *l_sign = (dap_sign_t*)l_sign_raw;//dap_sign_pack(l_client_key, l_sign_raw, l_sign_raw_size, l_pkey_raw, l_pkey_length);
-                size_t as = dap_sign_get_size(l_sign);
-                size_t l_serial_len = dap_strlen(a_serial_raw);
-                l_res = dap_sign_verify(l_sign, a_serial_raw, l_serial_len);
-                DAP_DELETE(l_sign_raw);
-            }
-            //dap_enc_key_deserealize_sign
-        }
-
-        // activate serial key
-        if(l_res==1) {
-            // added pkey to serial
-            l_serial_key->header.ext_size = l_pkey_raw_size;
-            l_serial_key = DAP_REALLOC(l_serial_key,dap_serial_key_len(l_serial_key));
-            l_serial_key->header.activated = time(NULL);
-            memcpy(l_serial_key->ext, l_pkey_raw, l_pkey_raw_size);
-            // save updated serial
-            if(dap_chain_global_db_gr_set(dap_strdup(l_serial_key->header.serial), l_serial_key,
-                    dap_serial_key_len(l_serial_key),
-                    s_group_serials_activated)) {
-                dap_chain_global_db_gr_del(l_serial_key->header.serial, s_group_serials);
-                l_ret = 0;// OK
-            }
-        }
-        else{
-            return -2;//OP_CODE_LOGIN_INCORRECT_SIGN
-        }
-        DAP_DELETE(l_pkey_raw);
-    }
-    DAP_DELETE(l_serial_key);
-    return l_ret;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_check_password
- * @param a_login
- * @param a_password
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_check_serial(const char * a_serial, const char * a_pkey_b64)
-{
-    int l_ret = 0;
-    dap_serial_key_t *l_serial_key = dap_chain_net_srv_vpn_cdb_auth_get_serial_param(a_serial, NULL);
-    // not found
-    if(!l_serial_key)
-        return -1;
-    // inactive serial key
-    if(!l_serial_key->header.activated) {
-        l_ret = -3;
-    }
-    // check time expired
-    else if(l_serial_key->header.expired) {
-        if((l_serial_key->header.activated + l_serial_key->header.expired) < time(NULL))
-            l_ret = -4;
-    }
-    DAP_DELETE(l_serial_key);
-    return l_ret;
-}
-
-/**
- * @brief s_input_validation
- * @param str
- * @return
- */
-static int s_input_validation(const char * str)
-{
-        // The compiler will stack "multiple" "strings" "end" "to" "end"
-        // into "multiplestringsendtoend", so we don't need one giant line.
-        static const char *nospecial="0123456789"
-                "abcdefghijklmnopqrstuvwxyz"
-                "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-                ".=@?_!#$%-";// /+
-        while(*str) // Loop until (*url) == 0.  (*url) is about equivalent to url[0].
-        {
-                // Can we find the character at *url in the string 'nospecial'?
-                // If not, it's a special character and we should return 0.
-                if(strchr(nospecial, *str) == NULL){
-                    return(0);
-                }
-                str++; // Jump to the next character.  Adding one to a pointer moves it ahead one element.
-        }
-
-        return(1); // Return 1 for success.
-}
-
-/**
- * Generate serial number like xxx-xxx-xxx
- * without symbols 0,1,L,I,O
- * a_group_sepa may be NULL
- */
-static char* generate_serial(int a_group_count, int a_group_len, const char *a_group_sepa)
-{
-    size_t l_group_sepa_len = a_group_sepa ? strlen(a_group_sepa) : 0;
-    char *l_serial = DAP_NEW_Z_SIZE(char, a_group_count * (a_group_len + l_group_sepa_len));
-    int l_serial_pos = 0;
-    for(int l_group_count = 0; l_group_count < a_group_count; l_group_count++) {
-        for(int l_group_len = 0; l_group_len < a_group_len; l_group_len++) {
-            uint32_t l_max_len = 'Z' - 'A' + 5; //['Z' - 'A' - 3]alpha + [10 - 2]digit
-            uint32_t l_value = random_uint32_t(l_max_len);
-            char l_sym;
-            if(l_value < 8)
-                l_sym = '2' + l_value;
-            // replace unused characters I,O,L
-            else if(l_value == 'I' - 'A' + 8)
-                l_sym = 'X';
-            else if(l_value == 'L' - 'A' + 8)
-                l_sym = 'Y';
-            else if(l_value == 'O' - 'A' + 8)
-                l_sym = 'Z';
-            else
-                l_sym = 'A' + l_value - 8;
-            l_serial[l_serial_pos] = l_sym;
-            l_serial_pos++;
-        }
-        // copy separator to serial
-        if(l_group_sepa_len && l_group_count < a_group_count - 1) {
-            dap_stpcpy(l_serial + l_serial_pos, a_group_sepa);
-            l_serial_pos += l_group_sepa_len;
-        }
-    }
-    return l_serial;
-}
-
-
-size_t dap_serial_key_len(dap_serial_key_t *a_serial_key)
-{
-    if(!a_serial_key)
-        return 0;
-    return sizeof(dap_serial_key_t) + a_serial_key->header.ext_size;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial
- * @param a_user_str
- * @param a_arg_index
- * @param a_argc
- * @param a_argv
- * @param a_str_reply
- * @param a_group_out
- * @return
- */
-dap_serial_key_t* dap_chain_net_srv_vpn_cdb_auth_get_serial_param(const char *a_serial_str, const char **a_group_out)
-{
-    const char *l_group_out = s_group_serials_activated;
-    if(!a_serial_str)
-        return NULL;
-    size_t l_serial_data_len = 0;
-    dap_serial_key_t *l_serial_key = (dap_serial_key_t*)dap_chain_global_db_gr_get(a_serial_str, &l_serial_data_len, s_group_serials_activated);
-    if(!l_serial_key){
-        l_serial_key = (dap_serial_key_t*)dap_chain_global_db_gr_get(a_serial_str, &l_serial_data_len, s_group_serials);
-        l_group_out = s_group_serials;
-    }
-    if(l_serial_data_len>=sizeof(dap_serial_key_t)){
-        if(a_group_out)
-            *a_group_out = l_group_out;
-        return l_serial_key;
-    }
-    DAP_DELETE(l_serial_key);
-    return NULL;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial
- * @param a_user_str
- * @param a_arg_index
- * @param a_argc
- * @param a_argv
- * @param a_str_reply
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_serial_str, int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply)
-{
-    int l_ret = 0;
-    // Command 'serial list'
-    if(!dap_strcmp(a_serial_str, "list")) {
-        const char * l_serial_count_str = NULL;
-        const char * l_serial_shift_str = NULL;
-        int l_serial_nototal = dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-nototal", NULL);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-n", &l_serial_count_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-shift", &l_serial_shift_str);
-        size_t l_serial_count = l_serial_count_str ? strtoll(l_serial_count_str, NULL, 10) : 0;
-        size_t l_serial_shift = l_serial_shift_str ? strtoll(l_serial_shift_str, NULL, 10)+1 : 1;
-        size_t l_total = dap_chain_global_db_driver_count(s_group_serials, l_serial_shift);
-        l_serial_count = l_serial_count ? min(l_serial_count, l_total - l_serial_shift) : l_total;
-        dap_store_obj_t *l_obj = dap_chain_global_db_driver_cond_read(s_group_serials, l_serial_shift, &l_serial_count);
-        if(l_serial_count > 0) {
-            dap_string_t *l_keys = l_serial_count > 1 ? dap_string_new("serial keys:\n") : dap_string_new("serial key: ");
-            for(size_t i = 0; i < l_serial_count; i++) {
-                if((l_obj + i)->value_len < sizeof(dap_serial_key_t))
-                    continue;
-                dap_serial_key_t *l_serial = (dap_serial_key_t*) (l_obj + i)->value;
-                dap_string_append(l_keys, l_serial->header.serial);
-                //if(i < l_serial_count - 1)
-                    dap_string_append(l_keys, "\n");
-            }
-            if(!l_serial_nototal){
-                char *l_total_str = dap_strdup_printf("total %u keys", l_total);
-                dap_string_append(l_keys, l_total_str);
-                DAP_DELETE(l_total_str);
-                //dap_chain_node_cli_set_reply_text(a_str_reply, "\ntotal %u keys", l_total);
-                //return 0;
-            }
-            dap_chain_node_cli_set_reply_text(a_str_reply, "%s", l_keys->str);
-            dap_string_free(l_keys, true);
-            dap_store_obj_free(l_obj, l_serial_count);
-        }
-        else
-            dap_chain_node_cli_set_reply_text(a_str_reply, "keys not found");
-        return 0;
-    }
-    else
-    // Command 'serial generate'
-    if(!dap_strcmp(a_serial_str, "generate")) {
-        const char * l_serial_count_str = NULL;
-        const char * l_active_days_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-n", &l_serial_count_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-active_days", &l_active_days_str);
-        uint32_t l_serial_count = l_serial_count_str ? strtoll(l_serial_count_str, NULL, 10) : 1;
-        size_t l_active_days = l_active_days_str ? strtoll(l_active_days_str, NULL, 10) : 0;
-        if(l_serial_count < 1)
-            l_serial_count = 1;
-        dap_string_t *l_keys = l_serial_count > 1 ? dap_string_new("serial keys:\n") : dap_string_new("serial key: ");
-        for(uint32_t i = 0; i < l_serial_count; i++) {
-            dap_serial_key_t l_serial;
-            memset(&l_serial, 0, sizeof(dap_serial_key_t));
-            while(1) {
-                char *l_serial_str = generate_serial(4, 4, "-");
-                uint8_t *l_serial_str_prev = dap_chain_global_db_gr_get(l_serial_str, NULL, s_group_serials);
-                if(l_serial_str_prev)
-                    DAP_DELETE(l_serial_str_prev);
-                else{
-                    strncpy(l_serial.header.serial, l_serial_str, sizeof(l_serial.header.serial));
-                    if(l_active_days)
-                        l_serial.header.expired = l_active_days * 86400;// days to sec
-                    break;
-                }
-            };
-            l_serial.header.ext_size = 0;
-
-            if(dap_chain_global_db_gr_set(dap_strdup(l_serial.header.serial), &l_serial, sizeof(l_serial), s_group_serials)) {
-                dap_string_append(l_keys, l_serial.header.serial);
-                if(i < l_serial_count - 1)
-                    dap_string_append(l_keys, "\n");
-            }
-        }
-        dap_chain_node_cli_set_reply_text(a_str_reply, "generated new %s", l_keys->str);
-        dap_string_free(l_keys, true);
-        // save gdb
-        dap_chain_global_db_flush();
-        return 0;
-    }
-    else
-    // Command 'serial update'
-    if(!dap_strcmp(a_serial_str, "update")) {
-        const char * l_serial_number_str = NULL;
-        const char * l_active_days_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-serial", &l_serial_number_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "-active_days", &l_active_days_str);
-        size_t l_active_days = l_active_days_str ? strtoll(l_active_days_str, NULL, 10) : 0;
-        if(!l_serial_number_str) {
-            dap_chain_node_cli_set_reply_text(a_str_reply, "option '-serial XXXX-XXXX-XXXX-XXXX' is not defined");
-        }
-        else if(!l_active_days_str) {
-            dap_chain_node_cli_set_reply_text(a_str_reply, "option '-active_days <active days that left for serial after activation>' is not defined");
-        }
-        else {
-            const char *l_group;
-            dap_serial_key_t *l_serial_key = dap_chain_net_srv_vpn_cdb_auth_get_serial_param(l_serial_number_str, &l_group);
-            if(l_serial_key){
-                l_serial_key->header.expired = l_active_days;
-                // save updated serial
-                if(dap_chain_global_db_gr_set(dap_strdup(l_serial_key->header.serial), l_serial_key, dap_serial_key_len(l_serial_key), l_group)) {
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "serial '%s' successfully updated", l_serial_key->header.serial);
-                    DAP_DELETE(l_serial_key);
-                    // save gdb
-                    dap_chain_global_db_flush();
-                    return 0;
-                }
-                else{
-                    dap_chain_node_cli_set_reply_text(a_str_reply, "serial '%s' can't updated", l_serial_key->header.serial);
-                }
-                DAP_DELETE(l_serial_key);
-            }
-            else{
-                dap_chain_node_cli_set_reply_text(a_str_reply, "serial '%s' not found", l_serial_number_str);
-            }
-            return 0;
-        }
-    }
-    else {
-        dap_chain_node_cli_set_reply_text(a_str_reply, "unknown subcommand %s, use 'generate', 'list' or 'update'", a_serial_str);
-    }
-    return -1;
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user
- * @param a_user_str
- * @param a_arg_index
- * @param a_argc
- * @param a_argv
- * @param a_str_reply
- * @return
- */
-int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(const char *a_user_str, int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply)
-{
-    int l_ret = 0;
-    dap_string_t * l_ret_str = dap_string_new("");
-    // Command 'user create'
-    bool l_is_user_create = (dap_strcmp(a_user_str, "create") == 0 );
-    bool l_is_user_update = (dap_strcmp(a_user_str, "update") == 0 );
-    if ( l_is_user_create  || l_is_user_update ){
-        const char * l_login_str = NULL;
-        const char * l_password_str = NULL;
-        const char * l_first_name_str = NULL;
-        const char * l_last_name_str = NULL;
-        const char * l_email_str = NULL;
-        const char * l_active_days_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--login", &l_login_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--password", &l_password_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--first_name", &l_first_name_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--last_name", &l_last_name_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--email", &l_email_str);
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--active_days", &l_active_days_str);
-
-        if ( ( l_is_user_create && l_login_str && l_password_str ) ||
-             ( l_is_user_update && l_login_str && ( l_password_str || l_first_name_str || l_last_name_str || l_email_str ) ) ){
-
-            if (l_password_str){
-                char * l_hash_str = dap_strdup_printf("%s%s",l_password_str, s_salt_str );
-                dap_chain_hash_fast_t *l_password_hash = DAP_NEW_Z(dap_chain_hash_fast_t);
-                dap_hash_fast(l_hash_str,dap_strlen(l_hash_str), l_password_hash );
-                DAP_DELETE(l_hash_str);
-                dap_chain_global_db_gr_set(dap_strdup(l_login_str), l_password_hash,sizeof(*l_password_hash),s_group_password );
-            }
-
-            if ( l_first_name_str )
-                dap_chain_global_db_gr_set(dap_strdup(l_login_str), dap_strdup(l_first_name_str),strlen(l_first_name_str)+1,s_group_first_name );
-
-            if ( l_last_name_str )
-                dap_chain_global_db_gr_set(dap_strdup(l_login_str), dap_strdup(l_last_name_str),strlen(l_last_name_str)+1,s_group_last_name );
-
-            if ( l_email_str )
-                dap_chain_global_db_gr_set(dap_strdup(l_login_str), dap_strdup(l_email_str),strlen(l_email_str)+1,s_group_email );
-
-            // Update timestamp
-            dap_chain_time_t *l_time = DAP_NEW_Z(dap_chain_time_t);
-            *l_time = dap_chain_time_now();
-            dap_chain_global_db_gr_set(dap_strdup(l_login_str), l_time,sizeof (*l_time),s_group_ts_updated );
-            l_time = NULL; // to prevent usage uleased memory that could be free in any moment
-
-            if ( l_active_days_str ){
-                uint64_t l_active_days = strtoull(l_active_days_str,NULL,10);
-                if ( l_active_days ){
-                    l_time = DAP_NEW_Z(dap_chain_time_t);
-                    *l_time = dap_chain_time_now() + (dap_chain_time_t) l_active_days*86400ull;
-                    dap_chain_global_db_gr_set(dap_strdup(l_login_str), l_time,sizeof (*l_time) ,s_group_ts_active_till );
-                }else
-                    dap_string_append_printf(l_ret_str,"WARNING: Wrong --active_time format\n");
-            }
-
-            if (l_is_user_create){
-                dap_string_append_printf(l_ret_str,"OK: Created user '%s'\n",l_login_str );
-                l_ret = 0;
-            }else if (l_is_user_update){
-                dap_string_append_printf(l_ret_str,"OK: Updated user '%s'\n",l_login_str );
-                l_ret = 0;
-            }else{
-                dap_string_append_printf(l_ret_str,"OK: Unknown action success\n");
-                l_ret = 0;
-            }
-        }else{
-            if (l_is_user_create){
-                dap_string_append_printf(l_ret_str,"ERROR: Need at least --login and --password options\n" );
-                l_ret = -2;
-            }else if (l_is_user_update){
-                dap_string_append_printf(l_ret_str,"ERROR: Need at least --login and one of next options: --password, --first_name, --last_name or --email\n" );
-                l_ret = -3;
-            }else{
-                dap_string_append_printf(l_ret_str,"ERROR: Unknown error in options\n");
-                l_ret = -4;
-            }
-        }
-
-    }else if ( dap_strcmp(a_user_str, "delete") == 0 ){
-        const char * l_login_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--login", &l_login_str);
-        if ( l_login_str ) {
-            if ( dap_chain_global_db_gr_del( dap_strdup( l_login_str),s_group_password ) ){
-                dap_chain_global_db_gr_del( dap_strdup( l_login_str),s_group_last_name );
-                dap_chain_global_db_gr_del( dap_strdup( l_login_str),s_group_first_name );
-                dap_chain_global_db_gr_del( dap_strdup( l_login_str),s_group_email );
-                dap_chain_global_db_gr_del( dap_strdup( l_login_str),s_group_cookie );
-
-                // Find if present cookie and delete it
-                size_t l_cookie_size = 0;
-                char * l_cookie = (char*) dap_chain_global_db_gr_get(l_login_str,&l_cookie_size, s_group_cookie );
-                if ( l_cookie ){
-                    dap_chain_global_db_gr_del( l_cookie,s_group_cookies );
-                    log_it(L_WARNING,"Deleted user but its cookie is active in table. Deleted that but better also to close session");
-                    // TODO close session when cookie deleted
-                }
-
-                dap_string_append_printf(l_ret_str,"OK: Deleted user '%s'\n",l_login_str );
-                l_ret = 0;
-            }else{
-                l_ret = -6;
-                dap_string_append_printf(l_ret_str,"ERROR: Can't find login '%s' in database\n", l_login_str );
-            }
-        }else{
-            l_ret = -5;
-            dap_string_append_printf(l_ret_str,"ERROR: Need --login option\n" );
-        }
-    }else if ( dap_strcmp(a_user_str, "check") == 0 ){
-        const char * l_login_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--login", &l_login_str);
-        const char * l_password_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--password", &l_password_str);
-        if ( l_login_str && l_password_str) {
-            int l_check = dap_chain_net_srv_vpn_cdb_auth_check_login (l_login_str, l_password_str);
-            if ( l_check == 0){
-                dap_string_append_printf(l_ret_str,"OK: Passed password check for '%s'\n",l_login_str );
-                l_ret = 0;
-            }else if (l_check == -1){
-                l_ret = -7;
-                dap_string_append_printf(l_ret_str,"ERROR: Can't find login '%s' in database\n", l_login_str );
-            }else if (l_check == -2){
-                l_ret = -8;
-                dap_string_append_printf(l_ret_str,"ERROR: Wrong password for login '%s'\n", l_login_str );
-            }else if (l_check == -3){
-                l_ret = -10;
-                dap_string_append_printf(l_ret_str,"ERROR: Login '%s' is not activated\n", l_login_str );
-            }else if (l_check == -4){
-                l_ret = -11;
-                dap_string_append_printf(l_ret_str,"ERROR: Login '%s' activation is overdue\n", l_login_str );
-            }else {
-                l_ret = -9;
-                dap_string_append_printf(l_ret_str,"ERROR: Unknown error in password check for login '%s'\n", l_login_str );
-            }
-        }else{
-            l_ret = -5;
-            dap_string_append_printf(l_ret_str,"ERROR: Need --login option\n" );
-        }
-    }else if ( dap_strcmp(a_user_str, "show") == 0 ){
-        const char * l_login_str = NULL;
-        dap_chain_node_cli_find_option_val(a_argv, a_arg_index, a_argc, "--login", &l_login_str);
-        if ( l_login_str ) {
-            size_t l_password_hash_size=0;
-            dap_chain_hash_fast_t *l_password_hash;
-            if ( (l_password_hash = (dap_chain_hash_fast_t*) dap_chain_global_db_gr_get (
-                     l_login_str,&l_password_hash_size  ,s_group_password ) ) !=NULL ){
-                dap_string_append_printf(l_ret_str,"OK: Find user '%s'\n",l_login_str );
-
-                size_t l_first_name_size=0;
-                char * l_first_name =(char *) dap_chain_global_db_gr_get (  l_login_str,&l_first_name_size  ,s_group_first_name ) ;
-                if ( l_first_name ){
-                    dap_string_append_printf(l_ret_str,"\tFirst_name: %s\n", l_first_name);
-                    DAP_DELETE( l_first_name );
-                }
-
-                size_t l_last_name_size=0;
-                char * l_last_name =(char *) dap_chain_global_db_gr_get (  l_login_str,&l_last_name_size  ,s_group_last_name ) ;
-                if (l_last_name){
-                    dap_string_append_printf(l_ret_str,"\tLast_name: %s\n", l_last_name);
-                    DAP_DELETE( l_last_name );
-                }
-
-                size_t l_email_size=0;
-                char * l_email =(char *) dap_chain_global_db_gr_get (  l_login_str,&l_email_size  ,s_group_email ) ;
-                if (l_email){
-                    dap_string_append_printf(l_ret_str,"\tEmail: %s\n", l_email);
-                    DAP_DELETE( l_email );
-                }
-
-                size_t l_ts_active_till_size = 0;
-                time_t *l_ts_active_till = (time_t*) dap_chain_global_db_gr_get(l_login_str, &l_ts_active_till_size, s_group_ts_active_till);
-                if(l_ts_active_till_size) {
-                    double l_dt_days = difftime(*l_ts_active_till, time(NULL)) / 86400;
-
-                    if(l_dt_days < 1) {
-                        if(l_dt_days < 0)
-                            l_dt_days = 0;
-                        dap_string_append_printf(l_ret_str, "\tActive hours: %.2lf\n", l_dt_days * 24);
-                    }
-                    else
-                        dap_string_append_printf(l_ret_str, "\tActive days: %.2lf\n", l_dt_days);
-                    DAP_DELETE(l_ts_active_till);
-                }
-
-                l_ret = 0;
-            }else{
-                l_ret = -6;
-                dap_string_append_printf(l_ret_str,"ERROR: Can't find login '%s' in database\n", l_login_str );
-            }
-        }else{
-            l_ret = -5;
-            dap_string_append_printf(l_ret_str,"ERROR: Need --login option\n" );
-        }
-    }else if ( dap_strcmp(a_user_str, "list") == 0 ){
-        size_t l_users_size = 0;
-        dap_global_db_obj_t* l_users = dap_chain_global_db_gr_load(s_group_password,&l_users_size);
-        if (l_users_size){
-            dap_string_append_printf(l_ret_str,"OK: %zd users in DB\n",l_users_size);
-            for ( size_t i = 0; i < l_users_size; i++ ){
-                dap_string_append_printf(l_ret_str,"\t%s\n",l_users[i].key);
-            }
-            dap_chain_global_db_objs_delete(l_users, l_users_size);
-        }else{
-            dap_string_append_printf(l_ret_str,"OK: 0 users in DB\n");
-        }
-    }else {
-        dap_string_append_printf(l_ret_str,"ERROR: Unknown command 'user %s'\n", a_user_str );
-        l_ret = -1;
-    }
-    dap_chain_node_cli_set_reply_text( a_str_reply, l_ret_str->str );
-    dap_string_free( l_ret_str, false );
-    return l_ret;
-}
-
-
-void dap_chain_net_srv_vpn_cdb_auth_add_proc(dap_http_t * a_http, const char * a_url)
-{
-    dap_http_simple_proc_add(a_http,a_url,24000, s_http_proc);
-}
-
-/**
- * @brief s_http_proc Process auth request
- * @param sh HTTP simple client instance
- * @param arg Return if ok
- */
-static void s_http_proc(dap_http_simple_t *a_http_simple, void * arg )
-{
-    http_status_code_t * return_code = (http_status_code_t*)arg;
-    enc_http_delegate_t * l_delegate;
-    strcpy(a_http_simple->reply_mime,"application/octet-stream");
-
-    l_delegate = enc_http_request_decode(a_http_simple);
-    if(l_delegate){
-        if(strcmp(l_delegate->url_path, "auth") == 0) {
-            s_http_enc_proc(l_delegate, arg);
-        }
-        else if(strcmp(l_delegate->url_path, "auth_key") == 0) {
-            s_http_enc_proc_key(l_delegate, arg);
-        }
-        else {
-
-            if(l_delegate->url_path)
-                log_it(L_ERROR,"Wrong auth request %s",l_delegate->url_path);
-            else
-                log_it(L_ERROR,"Wrong auth request: nothing after / ");
-
-            *return_code = Http_Status_BadRequest;
-        }
-
-        enc_http_reply_encode(a_http_simple,l_delegate);
-        enc_http_delegate_delete(l_delegate);
-    }else{
-        *return_code = Http_Status_Unauthorized;
-        log_it(L_WARNING,"No KeyID in the request");
-    }
-}
-
-/**
- * @brief s_http_enc_proc Auth http interface
- * @param a_delegate HTTP Simple client instance
- * @param a_arg Pointer to bool with okay status (true if everything is ok, by default)
- */
-static void s_http_enc_proc(enc_http_delegate_t *a_delegate, void * a_arg)
-{
-    http_status_code_t * l_return_code = (http_status_code_t*)a_arg;
-
-    if((a_delegate->request)&&(strcmp(a_delegate->action,"POST")==0)){
-        if(a_delegate->in_query==NULL){
-            log_it(L_WARNING,"Empty auth action");
-            *l_return_code = Http_Status_BadRequest;
-            return;
-        }else{
-            if(strcmp(a_delegate->in_query,"logout")==0 ){
-                if(dap_chain_global_db_gr_del(dap_strdup( a_delegate->cookie), s_group_cookies)){
-                    enc_http_reply_f(a_delegate,
-                                     "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
-                                     "<return>Successfuly logouted</return>\n"
-                                     );
-                    *l_return_code = Http_Status_OK;
-                }else{
-                    log_it(L_NOTICE,"Logout action: cookie %s is already logouted (by timeout?)", a_delegate->cookie);
-                    enc_http_reply_f(a_delegate,
-                                     "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
-                                     "<err_str>No session in table</err_str>\n"
-                                     );
-                    *l_return_code = Http_Status_OK;
-                }
-
-            }else if(strcmp(a_delegate->in_query,"login")==0 || strcmp(a_delegate->in_query,"serial")==0 ){
-                char l_login[128]={0};
-                char l_password[256]={0};
-                char l_pkey[6001]={0};//char l_pkey[4096]={0};
-
-                char l_domain[64], l_domain2[64];
-
-                //log_it(L_DEBUG, "request_size=%d request_str='%s'\n",a_delegate->request_size, a_delegate->request_str);
-
-                // password mode
-                if(s_mode_passwd) {
-                    if(sscanf(a_delegate->request_str, "%127s %255s %63s %6000s %63s", l_login, l_password, l_domain,
-                            l_pkey, l_domain2) >= 4 ||
-                            sscanf(a_delegate->request_str, "%127s %255s %6000s ", l_login, l_password, l_pkey) >= 3) {
-                        log_it(L_INFO, "Trying to login with username '%s'", l_login);
-
-                        if(s_input_validation(l_login) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in username");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            return;
-                        }
-                        if(s_input_validation(l_password) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in password");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            return;
-                        }
-                        if(s_input_validation(l_pkey) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in base64 pkey string");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            return;
-                        }
-
-                        int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check_login(l_login, l_password);
-                        switch (l_login_result) {
-                        case 0: {
-                            size_t l_tmp_size;
-                            char * l_first_name = (char*) dap_chain_global_db_gr_get(l_login, &l_tmp_size,
-                                    s_group_first_name);
-                            char * l_last_name = (char*) dap_chain_global_db_gr_get(l_login, &l_tmp_size,
-                                    s_group_last_name);
-                            char * l_email = (char*) dap_chain_global_db_gr_get(l_login, &l_tmp_size, s_group_email);
-                            dap_chain_time_t * l_ts_last_logined = (dap_chain_time_t*) dap_chain_global_db_gr_get(
-                                    l_login, &l_tmp_size, s_group_ts_last_login);
-                            dap_chain_time_t *l_ts_active_till = (dap_chain_time_t*) dap_chain_global_db_gr_get(l_login,
-                                    &l_tmp_size, s_group_ts_active_till);
-
-                            enc_http_reply_f(a_delegate,
-                                    "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
-                                            "<auth_info>\n"
-                                    );
-                            enc_http_reply_f(a_delegate, "\t<login>%s</login>\n", l_login);
-                            if(l_first_name)
-                                enc_http_reply_f(a_delegate, "\t<first_name>%s</first_name>\n", l_first_name);
-                            if(l_last_name)
-                                enc_http_reply_f(a_delegate, "\t<last_name>%s</last_name>\n", l_last_name);
-                            if(l_email)
-                                enc_http_reply_f(a_delegate, "\t<email>%s</email>\n", l_email);
-                            if(l_ts_last_logined)
-                                enc_http_reply_f(a_delegate, "\t<ts_prev_login>%llu</ts_prev_login>\n", (long long unsigned) *l_ts_last_logined);
-                            if(l_ts_active_till)
-                                enc_http_reply_f(a_delegate, "\t<ts_active_till>%llu</ts_acyive_till>\n", (long long unsigned) *l_ts_active_till);
-
-                            if(a_delegate->cookie)
-                                enc_http_reply_f(a_delegate, "\t<cookie>%s</cookie>\n", a_delegate->cookie);
-                            dap_chain_net_srv_vpn_cdb_auth_after(a_delegate, l_login, l_pkey); // Here if smbd want to add smth to the output
-                            enc_http_reply_f(a_delegate, "</auth_info>");
-                            log_it(L_INFO, "Login: Successfuly logined user %s", l_login);
-                            *l_return_code = Http_Status_OK;
-                            //log_it(L_DEBUG, "response_size='%d'",a_delegate->response_size);
-                            DAP_DELETE(l_first_name);
-                            DAP_DELETE(l_last_name);
-                            DAP_DELETE(l_email);
-                            DAP_DELETE(l_ts_last_logined);
-                            DAP_DELETE(l_ts_active_till);
-
-                            // Update last logined
-                            l_ts_last_logined = DAP_NEW_Z(dap_chain_time_t);
-                            *l_ts_last_logined = dap_chain_time_now();
-                            dap_chain_global_db_gr_set(dap_strdup(l_login), l_ts_last_logined, sizeof(time_t), s_group_ts_last_login);
-                            DAP_DELETE(l_ts_last_logined);
-                        }
-                            break;
-                        case -1:
-                            enc_http_reply_f(a_delegate, OP_CODE_NOT_FOUND_LOGIN_IN_DB);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -2:
-                            enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INCORRECT_PSWD);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -3:
-                            enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INACTIVE);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -4:
-                            enc_http_reply_f(a_delegate, OP_CODE_SUBSCRIBE_EXPIRIED);
-                            *l_return_code = Http_Status_PaymentRequired;
-                            break;
-                        default:
-                            log_it(L_WARNING, "Login: Unknown authorize error for login '%s'", l_login);
-                            *l_return_code = Http_Status_BadRequest;
-                            break;
-                        }
-                    } else {
-                        log_it(L_DEBUG, "Login: wrong auth's request body ");
-                        *l_return_code = Http_Status_BadRequest;
-                    }
-                }
-                // serial mode
-                else
-                {
-                    char l_serial_tmp[64]={0};
-                    if(sscanf(a_delegate->request_str, "%63s %63s %6000s", l_serial_tmp, l_domain, l_pkey) >= 3) {
-                        char *l_serial = make_fullserial(l_serial_tmp);
-                        log_it(L_INFO, "Trying to login with serial '%s'", l_serial);
-                        if(s_input_validation(l_serial) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in serial");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        if(s_input_validation(l_domain) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in l_domain");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        if(s_input_validation(l_pkey) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in base64 pkey string");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        int l_login_result = dap_chain_net_srv_vpn_cdb_auth_check_serial(l_serial, l_pkey);
-                        log_it(L_INFO, "Check serial '%s' with code %d (Ok=0)", l_serial, l_login_result);
-                        switch (l_login_result) {
-                        case 0: {
-                            size_t l_tmp_size;
-                            enc_http_reply_f(a_delegate,
-                                    "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
-                                            "<auth_info>\n"
-                                    );
-                            enc_http_reply_f(a_delegate, "\t<serial>%s</serial>\n", l_serial);
-
-                            dap_chain_time_t * l_ts_last_logined = (dap_chain_time_t*) dap_chain_global_db_gr_get(l_serial, &l_tmp_size, s_group_ts_last_login);
-                            dap_chain_time_t *l_ts_active_till = (dap_chain_time_t*) dap_chain_global_db_gr_get(l_serial, &l_tmp_size, s_group_ts_active_till);
-                            if(l_ts_last_logined)
-                                enc_http_reply_f(a_delegate, "\t<ts_prev_login>%llu</ts_prev_login>\n", (long long unsigned) *l_ts_last_logined);
-                            if(l_ts_active_till)
-                                enc_http_reply_f(a_delegate, "\t<ts_active_till>%llu</ts_acyive_till>\n", (long long unsigned) *l_ts_active_till);
-                            if(a_delegate->cookie)
-                                enc_http_reply_f(a_delegate, "\t<cookie>%s</cookie>\n", a_delegate->cookie);
-                            dap_chain_net_srv_vpn_cdb_auth_after(a_delegate, l_serial, l_pkey); // Here if smbd want to add smth to the output
-                            enc_http_reply_f(a_delegate, "</auth_info>");
-                            log_it(L_INFO, "Login: Successfuly logined user %s", l_login);
-                            *l_return_code = Http_Status_OK;
-                            //log_it(L_DEBUG, "response_size='%d'",a_delegate->response_size);
-
-                            DAP_DELETE(l_ts_last_logined);
-                            DAP_DELETE(l_ts_active_till);
-
-                            // Update last logined
-                            l_ts_last_logined = DAP_NEW_Z(dap_chain_time_t);
-                            *l_ts_last_logined = dap_chain_time_now();
-                            dap_chain_global_db_gr_set(dap_strdup(l_serial), l_ts_last_logined, sizeof(time_t),s_group_ts_last_login);
-                            DAP_DELETE(l_ts_last_logined);
-                        }
-                            break;
-                        case -1:
-                            enc_http_reply_f(a_delegate, OP_CODE_NOT_FOUND_LOGIN_IN_DB);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        /*case -2:
-                            enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INCORRECT_PSWD);
-                            *l_return_code = Http_Status_OK;
-                            break;*/
-                        case -3:
-                            enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INACTIVE);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -4:
-                            enc_http_reply_f(a_delegate, OP_CODE_SUBSCRIBE_EXPIRIED);
-                            *l_return_code = Http_Status_PaymentRequired;
-                            break;
-                        default:
-                            log_it(L_WARNING, "Login: Unknown authorize error for login '%s'", l_login);
-                            *l_return_code = Http_Status_BadRequest;
-                            break;
-                        }
-                        DAP_DELETE(l_serial);
-                    }
-                }
-            }else if (s_is_registration_open && strcmp(a_delegate->in_query,"register")==0){
-                char l_login[128];
-                char l_password[256];
-                char l_first_name[128];
-                char l_last_name[128];
-                char l_email[256];
-
-                log_it(L_INFO, "Request str = %s", a_delegate->request_str);
-                if(sscanf(a_delegate->request_str,"%127s %255s %127s %127s %255s"
-                          ,l_login,l_password,l_email,l_first_name,l_last_name)>=3){
-                    if(s_input_validation(l_login)==0){
-                        log_it(L_WARNING,"Registration: Wrong symbols in the username '%s'",l_login);
-                        *l_return_code = Http_Status_BadRequest;
-                        return;
-                    }
-                    if(s_input_validation(l_password)==0){
-                        log_it(L_WARNING,"Registration: Wrong symbols in the password");
-                        *l_return_code = Http_Status_BadRequest;
-                        return;
-                    }
-                    if(s_input_validation(l_first_name)==0){
-                        log_it(L_WARNING,"Registration: Wrong symbols in the first name '%s'",l_first_name);
-                        *l_return_code = Http_Status_BadRequest;
-                        return;
-                    }
-                    if(s_input_validation(l_last_name)==0){
-                        log_it(L_WARNING,"Registration: Wrong symbols in the last name '%s'",l_last_name);
-                        *l_return_code = Http_Status_BadRequest;
-                        return;
-                    }
-                    if(s_input_validation(l_email)==0){
-                        log_it(L_WARNING,"Registration: Wrong symbols in the email '%s'",l_email);
-                        *l_return_code = Http_Status_BadRequest;
-                        return;
-                    }
-                    if ( l_login[0] && l_password[0] && l_email[0] ){
-
-                        // Hash password with salt
-                        char * l_hash_str = dap_strdup_printf("%s%s",l_password, s_salt_str );
-                        dap_chain_hash_fast_t *l_password_hash = DAP_NEW_Z(dap_chain_hash_fast_t);
-                        dap_hash_fast(l_hash_str,dap_strlen(l_hash_str), l_password_hash );
-                        DAP_DELETE(l_hash_str);
-                        dap_chain_global_db_gr_set(dap_strdup(l_login), l_password_hash,sizeof(*l_password_hash),s_group_password );
-
-                        // Write email in db
-                        dap_chain_global_db_gr_set(dap_strdup(l_login), dap_strdup(l_email),strlen(l_email)+1,s_group_email );
-
-                        enc_http_reply_f(a_delegate,
-                                         "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n"
-                                         "<auth_info>\n"
-                                         );
-
-                        enc_http_reply_f(a_delegate,"\t<login>%s</login>\n",l_login);
-                        // Write first and last names in db if present
-                        if ( l_first_name[0] ){
-                            dap_chain_global_db_gr_set( dap_strdup(l_login), dap_strdup(l_first_name),strlen(l_first_name)+1,
-                                                        s_group_first_name );
-                            enc_http_reply_f(a_delegate,"\t<first_name>%s</first_name>\n",l_first_name);
-                        }
-
-                        if ( l_last_name[0] ){
-                            dap_chain_global_db_gr_set( dap_strdup(l_login), dap_strdup( l_last_name ), strlen( l_last_name)+1,
-                                                        s_group_last_name );
-                            enc_http_reply_f(a_delegate,"\t<last_name>%s</last_name>\n",l_last_name);
-                        }
-
-                        // If cookie present - report it
-                        if ( a_delegate->cookie )
-                            enc_http_reply_f(a_delegate,"\t<cookie>%s</cookie>\n",a_delegate->cookie );
-                        enc_http_reply_f(a_delegate,"</auth_info>");
-
-                        log_it(L_NOTICE,"Registration: new user %s \"%s %s\"<%s> is registred",l_login,l_first_name,l_last_name,l_email);
-                    }
-                }else{
-                    log_it(L_ERROR, "Registration: Wrong auth's request body ");
-                    *l_return_code = Http_Status_BadRequest;
-                }
-            }else{
-                log_it(L_ERROR, "Unknown auth command was selected (query_string='%s')",a_delegate->in_query);
-                *l_return_code = Http_Status_BadRequest;
-            }
-        }
-    }else{
-        log_it(L_ERROR, "Wrong auth request action '%s'",a_delegate->action);
-        *l_return_code = Http_Status_BadRequest;
-    }
-}
-
-/**
- * @brief s_http_enc_proc Auth http interface
- * @param a_delegate HTTP Simple client instance
- * @param a_arg Pointer to bool with okay status (true if everything is ok, by default)
- */
-static void s_http_enc_proc_key(enc_http_delegate_t *a_delegate, void * a_arg)
-{
-    http_status_code_t * l_return_code = (http_status_code_t*) a_arg;
-
-    if((a_delegate->request) && (strcmp(a_delegate->action, "POST") == 0)) {
-        if(a_delegate->in_query == NULL) {
-            log_it(L_WARNING, "Empty auth action");
-            *l_return_code = Http_Status_BadRequest;
-            return;
-        } else {
-            if(strcmp(a_delegate->in_query, "serial") == 0) {
-                char l_serial_raw[64] = { 0 };
-                char l_serial_sign[12000] = { 0 };
-                char l_pkey[6001] = { 0 };
-
-                // only for serial mode
-                if(!s_mode_passwd)
-                {
-                    char l_domain[64];
-                    if(sscanf(a_delegate->request_str, "%63s %12000s %63s %6000s", l_serial_raw, l_serial_sign, l_domain, l_pkey) >= 4) {
-                        char *l_serial = make_fullserial(l_serial_raw);
-                        /*size_t a1 = dap_strlen(l_serial);
-                        size_t a2 = dap_strlen(l_serial_sign);
-                        size_t a3 = dap_strlen(l_pkey);*/
-                        log_it(L_INFO, "Trying to activate with serial '%s'", l_serial);
-                        if(s_input_validation(l_serial) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in serial");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        if(s_input_validation(l_pkey) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in base64 pkey string");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        if(s_input_validation(l_serial_sign) == 0) {
-                            log_it(L_WARNING, "Wrong symbols in base64 serial sign");
-                            enc_http_reply_f(a_delegate, OP_CODE_INCORRECT_SYMOLS);
-                            *l_return_code = Http_Status_BadRequest;
-                            DAP_DELETE(l_serial);
-                            return;
-                        }
-                        int l_activate_result = dap_chain_net_srv_vpn_cdb_auth_activate_serial(l_serial_raw, l_serial, l_serial_sign, l_pkey);
-                        log_it(L_INFO, "Serial '%s' activated with code %d (Ok=0)", l_serial, l_activate_result);
-                        switch (l_activate_result) {
-                        case 0:
-                            enc_http_reply_f(a_delegate, OP_CODE_SERIAL_ACTIVED);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -1:
-                            enc_http_reply_f(a_delegate, OP_CODE_NOT_FOUND_LOGIN_IN_DB);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                        case -2:
-                            enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INCORRECT_SIGN);
-                            *l_return_code = Http_Status_OK;
-                            break;
-                            /*case -3:
-                             enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INACTIVE);
-                             *l_return_code = Http_Status_OK;
-                             break;*/
-                        case -4:
-                            enc_http_reply_f(a_delegate, OP_CODE_SUBSCRIBE_EXPIRIED);
-                            *l_return_code = Http_Status_PaymentRequired;
-                            break;
-                        default:
-                            log_it(L_WARNING, "Login: Unknown authorize error for activate serial '%s'", l_serial);
-                            *l_return_code = Http_Status_BadRequest;
-                            break;
-                        }
-                        DAP_DELETE(l_serial);
-                    }
-                    else {
-                        log_it(L_ERROR, "Registration: Wrong auth_key's request body ");
-                        *l_return_code = Http_Status_BadRequest;
-                    }
-                }
-            } else {
-                log_it(L_ERROR, "Unknown auth command was selected (query_string='%s')", a_delegate->in_query);
-                *l_return_code = Http_Status_BadRequest;
-            }
-        }
-    } else {
-        log_it(L_ERROR, "Wrong auth request action '%s'", a_delegate->action);
-        *l_return_code = Http_Status_BadRequest;
-    }
-}
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c b/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c
deleted file mode 100644
index b03956319f890a8f994e5de51b97f28eb3508e52..0000000000000000000000000000000000000000
--- a/modules/service/vpn/dap_chain_net_srv_vpn_cdb_server_list.c
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Authors:
- * Dmitriy A. Gearasimov <gerasimov.dmitriy@demlabs.net>
- * Alexander Lysikov <alexander.lysikov@demlabs.net>
- * DeM Labs Inc.   https://demlabs.net
- * CellFrame       https://cellframe.net
- * Sources         https://gitlab.demlabs.net/cellframe
- * Copyright  (c) 2017-2019
- * All rights reserved.
-
- 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 <time.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-
-#include "dap_common.h"
-#include "dap_config.h"
-
-#include "dap_chain.h"
-#include "dap_chain_net.h"
-#include "dap_chain_net_srv.h"
-#include "dap_chain_net_srv_vpn.h"
-#include "dap_chain_net_srv_order.h"
-#include "dap_chain_net_srv_geoip.h"
-
-#include "dap_http.h"
-#include "dap_http_simple.h"
-#include "http_status_code.h"
-
-#include "dap_chain_net_srv_vpn_cdb_server_list.h"
-
-#define LOG_TAG "dap_chain_net_srv_vpn_cdb_server_list"
-
-
-static size_t s_cdb_net_count = 0;
-static dap_chain_net_t ** s_cdb_net = NULL;
-static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg);
-
-int dap_chain_net_srv_vpn_cdb_server_list_init()
-{
-    char **l_cdb_networks;
-    uint16_t l_cdb_networks_count = 0;
-    log_it(L_NOTICE,"Initialized Server List Module");
-    l_cdb_networks = dap_config_get_array_str( g_config, "cdb", "servers_list_networks", &l_cdb_networks_count );
-
-    if ( l_cdb_networks_count ){
-        s_cdb_net = DAP_NEW_Z_SIZE(dap_chain_net_t*, sizeof (dap_chain_net_t*)* l_cdb_networks_count );
-        s_cdb_net_count = l_cdb_networks_count;
-        for ( size_t i = 0; i < l_cdb_networks_count ; i++) {
-            s_cdb_net[i] = dap_chain_net_by_name( l_cdb_networks[i] );
-            if ( s_cdb_net[i] )
-                log_it( L_INFO, "Added \"%s\" network for server list fetchs", l_cdb_networks[i]);
-            else
-                log_it( L_WARNING, "Can't find \"%s\" network to add to server list fetchs", l_cdb_networks[i]);
-        }
-    } else
-        log_it( L_WARNING, "No chain networks listed in config");
-
-    return 0;
-}
-
-void dap_chain_net_srv_vpn_cdb_server_list_deinit(void)
-{
-}
-
-
-/**
- * @brief order_info_print
- * @param a_server_location for server name, NULL not used
- * @param a_node_number for server name, <0 not use
- */
-static int order_info_print(dap_string_t *a_reply_str, dap_chain_net_t * a_net, dap_chain_net_srv_order_t * a_order, const char *a_server_name, int a_node_number)
-{
-    dap_chain_node_info_t * l_node_info = dap_chain_node_info_read(a_net, &a_order->node_addr);
-    if(l_node_info) {
-        char l_node_ext_ipv4_str[INET_ADDRSTRLEN] = { 0 };
-        char l_node_ext_ipv6_str[INET6_ADDRSTRLEN] = { 0 };
-        if(l_node_info->hdr.ext_addr_v4.s_addr)
-            inet_ntop(AF_INET, &l_node_info->hdr.ext_addr_v4, l_node_ext_ipv4_str, sizeof(l_node_ext_ipv4_str));
-        if(*((uint128_t *) l_node_info->hdr.ext_addr_v6.s6_addr))
-            inet_ntop(AF_INET6, &l_node_info->hdr.ext_addr_v6, l_node_ext_ipv6_str, sizeof(l_node_ext_ipv6_str));
-
-        uint8_t l_continent_num = 0;
-        char *l_region = NULL;
-        dap_chain_net_srv_order_get_continent_region(a_order, &l_continent_num, &l_region);
-        const char *l_continent_str = dap_chain_net_srv_order_continent_to_str(l_continent_num);
-        // ext_out in hex view
-        char *l_ext_out = a_order->ext_size ? DAP_NEW_Z_SIZE(char, a_order->ext_size * 2 + 1) : NULL;
-        dap_bin2hex(l_ext_out, a_order->ext, a_order->ext_size);
-
-        dap_string_append_printf(a_reply_str, "    {\n");
-        dap_string_append_printf(a_reply_str, "        \"Location\":\"%s\",\n", l_region ? l_region : "None"); //NETHERLANDS
-                //l_continent_str ? l_continent_str : "None", l_region ? l_region : "None");
-
-
-        dap_string_append_printf(a_reply_str, "        \"ChainNet\":\"%s\",\n", a_net->pub.name);
-        //dap_string_append_printf(a_reply_str, "        \"Name\":\"%s.Cell-%lu.%zd\",\n", a_net->pub.name, l_node_info->hdr.cell_id.uint64, 0);
-        if(a_server_name)
-            dap_string_append_printf(a_reply_str, "        \"Name\":\"%s\",\n", a_server_name);
-        else
-            dap_string_append_printf(a_reply_str, "        \"Name\":\"%s.%s.%zd\",\n", l_continent_str ? l_continent_str : "", l_region ? l_region : "", a_node_number + 1);
-            //dap_string_append_printf(a_reply_str, "        \"Name\":\"%s.%s.Cell-%lu.%zd\",\n", l_continent_str ? l_continent_str : "", l_region ? l_region : "", l_node_info->hdr.cell_id.uint64, a_node_number + 1);
-        if(l_node_ext_ipv4_str[0])
-            dap_string_append_printf(a_reply_str, "        \"Address\":\"%s\",\n", l_node_ext_ipv4_str);
-        if(l_node_ext_ipv6_str[0])
-            dap_string_append_printf(a_reply_str, "        \"Address6\":\"%s\",\n", l_node_ext_ipv6_str);
-        dap_string_append_printf(a_reply_str, "        \"Port\":%hu,\n", l_node_info->hdr.ext_port ? l_node_info->hdr.ext_port : 80);
-
-        //dap_string_append_printf(a_reply_str, "        \"Ext\":\"%s-%s\",\n", l_continent_str ? l_continent_str : "", l_region ? l_region : "");
-        if(l_ext_out)
-            dap_string_append_printf(a_reply_str, "        \"Ext\":\"0x%s\",\n", l_ext_out);
-        else
-            dap_string_append_printf(a_reply_str, "        \"Ext\":\"0x0\",\n");
-        dap_string_append_printf(a_reply_str, "        \"Price\":%lu,\n", a_order->price);
-        dap_string_append_printf(a_reply_str, "        \"PriceUnits\":%u,\n", a_order->price_unit.uint32);
-        dap_string_append_printf(a_reply_str, "        \"PriceToken\":\"%s\"\n", a_order->price_ticker);
-        dap_string_append_printf(a_reply_str, "    }");
-        DAP_DELETE(l_region);
-        DAP_DELETE(l_ext_out);
-
-
-    } else{
-        log_it(L_WARNING, "Order in \"%s\" network issued by node without ext_ipv4 field", a_net->pub.name);
-        return -1;
-    }
-    return 0;
-}
-
-
-static void s_http_simple_proc(dap_http_simple_t *a_http_simple, void *a_arg)
-{
-    http_status_code_t * l_ret_code = (http_status_code_t*)a_arg;
-    dap_string_t *l_reply_str = dap_string_new("[\n");
-
-
-    char *l_client_ip = a_http_simple->http->client->s_ip;//"64.225.61.216"
-    geoip_info_t *l_geoip_info = chain_net_geoip_get_ip_info(l_client_ip);
-
-    log_it(L_DEBUG, "Have %zd chain networks for cdb lists", s_cdb_net_count );
-
-    for ( size_t i = 0; i < s_cdb_net_count ; i++ ) {
-        dap_chain_net_t * l_net = s_cdb_net[i];
-        if ( l_net ) {
-            dap_chain_net_srv_order_t * l_orders = NULL;
-            size_t l_orders_num = 0;
-            dap_chain_net_srv_price_unit_uid_t l_unit_uid = {{0}};
-            dap_chain_net_srv_uid_t l_srv_uid = { .uint64 =DAP_CHAIN_NET_SRV_VPN_ID };
-            dap_chain_net_srv_order_find_all_by( l_net, SERV_DIR_SELL,  l_srv_uid, l_unit_uid ,
-                                                 NULL,0,0, &l_orders, &l_orders_num );
-            log_it(L_DEBUG, "Found %zd orders in \"%s\" network", l_orders_num, l_net->pub.name );
-
-
-            // find the shift for each node
-            dap_chain_net_srv_order_t *l_orders_pos[l_orders_num];
-            size_t l_orders_size = 0;
-            for(size_t j = 0; j < l_orders_num; j++) {
-                l_orders_pos[j] = (dap_chain_net_srv_order_t*) ((char*) l_orders + l_orders_size);
-                l_orders_size += dap_chain_net_srv_order_get_size(l_orders_pos[j]);
-            }
-
-
-            // list of node numbers
-            size_t l_continents_count = dap_chain_net_srv_order_continents_count(); //int *l_node_numbering = DAP_NEW_Z_SIZE(int, l_orders_num * sizeof(int));
-            // list of the number of nodes in each continent
-            int l_continents_numbers[l_continents_count]; //int *l_continents_numbers = DAP_NEW_Z_SIZE(int, l_continents_count * sizeof(int));
-            int l_node_numbering[l_continents_count][l_orders_num];
-            // init arrays
-            for(size_t m1 = 0; m1 < l_continents_count; m1++) {
-                l_continents_numbers[m1] = 0;
-                for(size_t m2 = 0; m2 < l_orders_num; m2++)
-                    l_node_numbering[m1][m2] = -1;
-            }
-
-            // node numbering
-            size_t l_orders_used_num = 0;
-            {
-                // filling l_continents_numbers and l_node_numbering
-                for(size_t j = 0; j < l_orders_num; j++) {
-                    dap_chain_net_srv_order_t *l_order = l_orders_pos[j];
-                    uint8_t l_continent_num;
-                    if(!dap_chain_net_srv_order_get_continent_region(l_order, &l_continent_num, NULL))
-                        continue;
-                    l_node_numbering[l_continent_num][j] = l_continents_numbers[l_continent_num]++;
-                    l_orders_used_num++;
-                }
-                // shuffle nodes for each continent
-                for(size_t m1 = 0; m1 < l_continents_count; m1++) {
-                    int l_cont_num = l_continents_numbers[m1];
-                    if(l_cont_num <= 1)
-                        continue;
-                    // number of shuffles
-                    int l_shuffle_num = rand() % (l_cont_num + 1);
-                    for(size_t l_sh = 0; l_sh <= l_shuffle_num; l_sh++) {
-                        size_t l_pos1 = 0;
-                        size_t l_pos2 = 0;
-                        while(l_pos1 == l_pos2) {
-                            l_pos1 = rand() % l_cont_num;
-                            l_pos2 = rand() % l_cont_num;
-                        }
-                        for(size_t m2 = 0; m2 < l_orders_num; m2++) {
-                            if(l_node_numbering[m1][m2] == l_pos1)
-                                l_node_numbering[m1][m2] = l_pos2;
-                            else if(l_node_numbering[m1][m2] == l_pos2)
-                                l_node_numbering[m1][m2] = l_pos1;
-                        }
-                    }
-                }
-            }
-
-            int8_t l_client_continent = l_geoip_info ? dap_chain_net_srv_order_continent_to_num(l_geoip_info->continent) : 0;
-            // random node on client's continent
-            if(l_client_continent > 0 && l_continents_numbers[l_client_continent] > 1) {
-                int l_count = 0;
-                while(l_orders_num > 0) {
-                    size_t k = rand() % l_continents_numbers[l_client_continent];
-                    size_t l_node_pos = -1;
-                    for(size_t j2 = 0; j2 <= l_orders_num; j2++) {
-                        if(k == l_node_numbering[l_client_continent][j2]) {
-                            l_node_pos = j2;
-                            break;
-                        }
-                    }
-                    if(l_node_pos == -1) {
-                        // random node for the whole world
-                        l_node_pos = rand() % l_orders_num;
-                    }
-                    dap_chain_net_srv_order_t *l_order = l_orders_pos[l_node_pos];
-                    const char *country_code = dap_chain_net_srv_order_get_country_code(l_order);
-                    if(country_code) {
-                        // only for other countries
-                        if(dap_strcmp(l_geoip_info->country_code, country_code)) {
-                            if(!order_info_print(l_reply_str, l_net, l_order, "Auto", -1)) {
-                                dap_string_append_printf(l_reply_str, ",\n");
-                                break;
-                            }
-                        }
-                    }
-                    if(l_count > 20)
-                        break;
-                    l_count++;
-                }
-
-            }
-            // random node for the whole world
-			else {
-				int l_count = 0;
-				while(l_orders_num > 0) {
-					// first random node
-					size_t k = rand() % l_orders_num;
-					dap_chain_net_srv_order_t *l_order = l_orders_pos[k];
-					if(!order_info_print(l_reply_str, l_net, l_order, "Auto", -1)){
-						dap_string_append_printf(l_reply_str, ",\n");
-						break;
-					}
-					if (l_count>20)
-						break;
-					l_count++;
-				}
-            }
-            // random nodes for continents
-            int l_count = 0;
-            for(size_t l_c = 0; l_c < l_continents_count; l_c++) {
-                while(l_continents_numbers[l_c] > 0) {
-                    // random node for continent
-                    size_t k = rand() % l_continents_numbers[l_c];
-                    size_t l_node_pos = -1;
-                    for(size_t j2 = 0; j2 <= l_orders_num; j2++) {
-                        if(k == l_node_numbering[l_c][j2]) {
-                            l_node_pos = j2;
-                            break;
-                        }
-                    }
-                    if(l_node_pos == -1)
-                        break;
-                    dap_chain_net_srv_order_t *l_order = l_orders_pos[l_node_pos];
-                    char *l_server_name = dap_strdup_printf("%s", dap_chain_net_srv_order_continent_to_str(l_c));
-                    if(!order_info_print(l_reply_str, l_net, l_order, l_server_name, -1)) {
-                        dap_string_append_printf(l_reply_str, ",\n");
-                        DAP_DELETE(l_server_name);
-                        break;
-                    }
-                    else
-                        DAP_DELETE(l_server_name);
-                    if(l_count > 20)
-                        break;
-                    l_count++;
-                }
-            }
-
-            for(size_t l_c = 0; l_c < l_continents_count; l_c++) {
-                // print all nodes for continent
-                for(size_t l_n = 0; l_n < l_continents_numbers[l_c]; l_n++) {
-                    // since the nodes are shuffled, look for the desired node index
-                    for(size_t l_o = 0; l_o < l_orders_num; l_o++) {
-                        if(l_node_numbering[l_c][l_o] != l_n)
-                            continue;
-                        dap_chain_net_srv_order_t *l_order = l_orders_pos[l_o];
-                        if(!order_info_print(l_reply_str, l_net, l_order, NULL, l_n)) {
-                            dap_string_append_printf(l_reply_str, ",\n");
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-    }
-    DAP_DELETE(l_geoip_info);
-    //delete trailing comma if exists
-    if(l_reply_str->str[l_reply_str->len - 2] == ','){
-        dap_string_truncate(l_reply_str, l_reply_str->len - 2);
-        dap_string_append_printf(l_reply_str, "\n");
-    }
-
-    dap_string_append_printf( l_reply_str, "]\n\n");
-    dap_http_simple_reply( a_http_simple, l_reply_str->str, l_reply_str->len );
-    dap_string_free(l_reply_str, true);
-    //log_it(L_DEBUG,"Reply in buffer: %s", a_http_simple->reply_str );
-    *l_ret_code = Http_Status_OK;
-
-}
-
-/**
- * @brief dap_chain_net_srv_vpn_cdb_server_list_add_proc
- * @param sh
- * @param url
- */
-void dap_chain_net_srv_vpn_cdb_server_list_add_proc(dap_http_t *a_http, const char *a_url)
-{
-    dap_http_simple_proc_add(a_http,a_url,100000,s_http_simple_proc);
-}
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..01a18e9f0d56708000f00ff5ed3d7baefd032d39
--- /dev/null
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.c
@@ -0,0 +1,170 @@
+#include <dap_chain_node_cli.h>
+#include "dap_chain_node_cli_cmd.h"
+#include "dap_chain_net_srv_vpn_cmd.h"
+#include "dap_chain_net_vpn_client.h"
+
+/**
+ * vpn_client command
+ *
+ * VPN client control
+ */
+int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply)
+{
+#ifndef _WIN32
+    enum {
+        CMD_NONE, CMD_INIT, CMD_START, CMD_STOP, CMD_STATUS
+    };
+    int l_arg_index = 1;
+    // find net
+    dap_chain_net_t *l_net = NULL;
+    if(dap_chain_node_cli_cmd_values_parse_net_chain(&l_arg_index, a_argc, a_argv, a_str_reply, NULL, &l_net) < 0)
+        return -2;
+
+    int cmd_num = CMD_NONE;
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "init", NULL)) {
+        cmd_num = CMD_INIT;
+    }
+    if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "start", NULL)) {
+            cmd_num = CMD_START;
+        }
+    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "stop", NULL)) {
+        cmd_num = CMD_STOP;
+    }
+    else if(dap_chain_node_cli_find_option_val(a_argv, l_arg_index, min(a_argc, l_arg_index + 1), "status", NULL)) {
+        cmd_num = CMD_STATUS;
+    }
+    if(cmd_num == CMD_NONE) {
+        if(!a_argv[1])
+            dap_chain_node_cli_set_reply_text(a_str_reply, "invalid parameters");
+        else
+            dap_chain_node_cli_set_reply_text(a_str_reply, "parameter %s not recognized", a_argv[1]);
+        return -1;
+    }
+
+    switch (cmd_num)
+    {
+    case CMD_INIT: {
+            const char * l_str_token = NULL; // token name
+            const char * l_str_value_datoshi = NULL;
+            const char * l_str_wallet = NULL; // wallet name
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-wallet", &l_str_wallet);
+            if(!l_str_wallet)
+                dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-w", &l_str_wallet);
+
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-token", &l_str_token);
+            dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-value", &l_str_value_datoshi);
+
+            if(!l_str_wallet) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Wallet not defined, use -w <wallet_name> or -wallet <wallet_name> parameter");
+                break;
+            }
+            if(!l_str_token) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Token not defined, use -token <token_name> parameter");
+                break;
+            }
+            if(!l_str_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi not defined, use -value <value of datoshi> parameter");
+                break;
+            }
+            uint64_t l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 10);
+            if(!l_a_value_datoshi)
+                l_a_value_datoshi = strtoull(l_str_value_datoshi, NULL, 16);
+            if(!l_a_value_datoshi) {
+                dap_chain_node_cli_set_reply_text(a_str_reply, "Value of datoshi have to be more then 0");
+                break;
+            }
+            int l_res = dap_chain_net_vpn_client_update(l_net, l_str_wallet, l_str_token, l_a_value_datoshi);
+            if(!l_res)
+                dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully");
+            else{
+                if(l_res==-3)
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client init successfully, but probably not enough founds in the wallet");
+                else
+                    dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not init");
+            }
+            return l_res;
+    }
+        break;
+    case CMD_START: {
+        const char * l_str_addr = NULL; // for example, "192.168.100.93"
+        const char * l_str_port = NULL; // for example, "8079"
+        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-addr", &l_str_addr);
+        if(!l_str_addr) {
+            dap_chain_node_cli_set_reply_text(a_str_reply,
+                    "VPN server address not defined, use -addr <vpn server ipv4 address> parameter");
+            break;
+        }
+        dap_chain_node_cli_find_option_val(a_argv, l_arg_index, a_argc, "-port", &l_str_port);
+        int l_srv_port = (l_str_port) ? (int) strtoll(l_str_port, 0, 10) : 0;
+        if(!l_srv_port) {
+            dap_chain_node_cli_set_reply_text(a_str_reply,
+                    "VPN server port not defined, use -port <vpn server port>  parameter");
+            break;
+        }
+        int l_res = dap_chain_net_vpn_client_start(l_net, l_str_addr, NULL, l_srv_port);
+        switch (l_res) {
+        case 0:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client started successfully");
+            break;
+        case 1:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client already started");
+            break;
+        case -2:
+        case -3:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't connect to VPN server");
+            break;
+        default:
+            dap_chain_node_cli_set_reply_text(a_str_reply, "Can't start VPN client");
+            break;
+        }
+        return l_res;
+    }
+        break;
+    case CMD_STOP: {
+        int res = dap_chain_net_vpn_client_stop();
+        if(!res)
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client stopped successfully");
+        else
+            dap_chain_node_cli_set_reply_text(a_str_reply, "VPN client not stopped");
+        return res;
+    }
+        break;
+    case CMD_STATUS:
+        {
+        char *l_wallet_name = NULL, *l_str_token = NULL;
+        uint64_t l_value_datoshi = 0;
+        dap_chain_net_vpn_client_get_wallet_info(l_net, &l_wallet_name, &l_str_token, &l_value_datoshi);
+
+        const char *l_status_txt = "";
+        switch (dap_chain_net_vpn_client_status()) {
+        case VPN_CLIENT_STATUS_NOT_STARTED:
+            l_status_txt = "VPN client not started";
+            break;
+        case VPN_CLIENT_STATUS_STARTED:
+            l_status_txt = "VPN client started";
+            break;
+        case VPN_CLIENT_STATUS_STOPPED:
+            l_status_txt = "VPN client stopped";
+            break;
+        case VPN_CLIENT_STATUS_CONN_LOST:
+            l_status_txt = "VPN client lost connection";
+            break;
+        default:
+            l_status_txt = "VPN client status unknown";
+            break;
+        }
+        dap_chain_node_cli_set_reply_text(a_str_reply, "%s\nused:\nwallet:%s\nreceipt:%u*1e-9 %s", l_status_txt,
+                l_wallet_name, l_value_datoshi, l_str_token);
+        break;
+    }
+    }
+#endif
+    return 0;
+}
+
+
+int dap_chain_net_srv_vpn_cmd_init()
+{
+
+
+}
diff --git a/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h
new file mode 100644
index 0000000000000000000000000000000000000000..4ce2243e8703bc14aa9ead66b5120f38562e8b00
--- /dev/null
+++ b/modules/service/vpn/dap_chain_net_srv_vpn_cmd.h
@@ -0,0 +1,4 @@
+#pragma once
+
+// vpn_client command
+int com_vpn_client(int a_argc, char ** a_argv, void *arg_func, char **a_str_reply);
diff --git a/modules/service/vpn/dap_chain_net_vpn_client.c b/modules/service/vpn/dap_chain_net_vpn_client.c
index 7a673697cd20befce5711f09121ff087912dafa0..9109e9320c28d6a7b677901982c23649be93ca4c 100644
--- a/modules/service/vpn/dap_chain_net_vpn_client.c
+++ b/modules/service/vpn/dap_chain_net_vpn_client.c
@@ -55,6 +55,7 @@
 #include "dap_stream_ch_chain_net_srv.h"
 //#include "dap_stream_ch_chain_net_srv.h"
 #include "dap_chain_net_vpn_client_tun.h"
+#include "dap_chain_net_srv_vpn_cmd.h"
 //#include "dap_chain_net_vpn_client_data.h"
 
 /*
@@ -724,6 +725,12 @@ void dap_chain_net_vpn_client_pkt_out(dap_stream_ch_t* a_ch)
 int dap_chain_net_vpn_client_init(dap_config_t * g_config)
 {
     pthread_mutex_init(&sf_socks_mutex, NULL);
+
+    // vpn client command
+    dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, NULL, "VPN client control",
+    "vpn_client [start -addr <server address> -port <server port>| stop | status] -net <net name>\n");
+
+
     return dap_chain_net_srv_client_vpn_init(g_config);
 }
 
diff --git a/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h b/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
index 8682421039c334431ef333fc981e9b2e10e9e31a..820d9f81936a8ada6244ff6706c28434aeba1003 100644
--- a/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
+++ b/modules/service/vpn/include/dap_chain_net_srv_vpn_cdb_auth.h
@@ -31,11 +31,15 @@ typedef struct dap_serial_key {
     struct {
         char serial[20];
         time_t activated; // if set, then serial is activated
-        time_t expired; // if zero then time no expired
-        int32_t os;// operating system. if zero then any operating system
+        // if zero then time no expired
+        union{
+            time_t expired;
+            int64_t license_length;// in sec
+        };
+        int32_t pkey_type;// dap_enc_key_type_t pkey type
         size_t ext_size;
     }DAP_ALIGN_PACKED header;
-    uint8_t ext[];
+    uint8_t ext[];// pkey here
 }DAP_ALIGN_PACKED dap_serial_key_t;
 
 size_t dap_serial_key_len(dap_serial_key_t *a_serial_key);
@@ -46,7 +50,7 @@ void dap_chain_net_srv_vpn_cdb_auth_deinit();
 
 void dap_chain_net_srv_vpn_cdb_auth_add_proc(dap_http_t * a_http, const char * a_url);
 void dap_chain_net_srv_vpn_cdb_auth_set_callback(dap_enc_http_callback_t a_callback_success);
-int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_user_str, int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply);
+int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_serial(const char *a_serial_str, int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply);
 int dap_chain_net_srv_vpn_cdb_auth_cli_cmd_user(const char *a_user_str, int a_arg_index, int a_argc, char ** a_argv, char **a_str_reply);
 
 int dap_chain_net_srv_vpn_cdb_auth_check_login(const char * a_login, const char * a_password);
diff --git a/modules/service/xchange/dap_chain_net_srv_xchange.c b/modules/service/xchange/dap_chain_net_srv_xchange.c
index 20c03c5db409e62f1be0674124252bfda888695f..b7bd5071492e047932395300fca186099c49d41c 100644
--- a/modules/service/xchange/dap_chain_net_srv_xchange.c
+++ b/modules/service/xchange/dap_chain_net_srv_xchange.c
@@ -87,6 +87,8 @@ int dap_chain_net_srv_xchange_init()
 
 void dap_chain_net_srv_xchange_deinit()
 {
+    if(!s_srv_xchange)
+        return;
     dap_chain_net_srv_xchange_price_t *l_price = NULL, *l_tmp;
     HASH_ITER(hh, s_srv_xchange->pricelist, l_price, l_tmp) {
         HASH_DEL(s_srv_xchange->pricelist, l_price);
diff --git a/modules/type/dag/dap_chain_cs_dag.c b/modules/type/dag/dap_chain_cs_dag.c
index 3d76887d71005e5f28bd56e7187fe25d45d2855c..48afa1e8855c846f51d948e5d11639dd6acde486 100644
--- a/modules/type/dag/dap_chain_cs_dag.c
+++ b/modules/type/dag/dap_chain_cs_dag.c
@@ -77,8 +77,8 @@ typedef struct dap_chain_cs_dag_pvt {
 #define PVT(a) ((dap_chain_cs_dag_pvt_t *) a->_pvt )
 
 // Atomic element organization callbacks
-static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t );                      //    Accept new event in dag
-static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t );                   //    Verify new event in dag
+static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t );                      //    Accept new event in dag
+static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t );                   //    Verify new event in dag
 static size_t s_chain_callback_atom_hdr_get_size(dap_chain_atom_ptr_t );                                 //    Get dag event size
 static size_t s_chain_callback_atom_get_static_hdr_size(void);                               //    Get dag event header size
 
@@ -281,14 +281,29 @@ static int s_dap_chain_add_atom_to_ledger(dap_chain_cs_dag_t * a_dag, dap_ledger
 }
 
 static int s_dap_chain_add_atom_to_events_table(dap_chain_cs_dag_t * a_dag, dap_ledger_t * a_ledger, dap_chain_cs_dag_event_item_t * a_event_item ){
-    HASH_ADD(hh, PVT(a_dag)->events,hash,sizeof (a_event_item->hash), a_event_item);
-    s_dag_events_lasts_process_new_last_event(a_dag, a_event_item);
-
     int res = a_dag->callback_cs_verify(a_dag,a_event_item->event);
 
-    if(res == 0)
+    if(res == 0){
         res = s_dap_chain_add_atom_to_ledger(a_dag, a_ledger, a_event_item);
 
+        HASH_ADD(hh, PVT(a_dag)->events,hash,sizeof (a_event_item->hash), a_event_item);
+        s_dag_events_lasts_process_new_last_event(a_dag, a_event_item);
+    }
+
+    return res;
+}
+
+static bool s_dap_chain_check_if_event_is_present(dap_chain_cs_dag_event_item_t * a_hash_table, const dap_chain_hash_fast_t * hash){
+    bool res = false;
+    dap_chain_cs_dag_event_item_t * l_event_search = NULL;
+
+    if(!a_hash_table)
+        return false;
+
+    HASH_FIND(hh, a_hash_table, hash, sizeof(*hash), l_event_search);
+    if ( l_event_search )
+        res = true;
+
     return res;
 }
 
@@ -298,51 +313,62 @@ static int s_dap_chain_add_atom_to_events_table(dap_chain_cs_dag_t * a_dag, dap_
  * @param a_atom
  * @return 0 if verified and added well, otherwise if not
  */
-static int s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
+static dap_chain_atom_verify_res_t s_chain_callback_atom_add(dap_chain_t * a_chain, dap_chain_atom_ptr_t a_atom)
 {
-    bool l_add_to_threshold = false;
-    int ret = s_chain_callback_atom_verify (a_chain, a_atom);
-    if ( ret < 0 ){
-        log_it(L_WARNING,"Wrong event, can't accept, verification returned %d",ret);
-        return  -1;
-    }else if( ret > 0){
-        l_add_to_threshold = true;
-    }
-
+    dap_chain_atom_verify_res_t ret = ATOM_ACCEPT;
     dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG(a_chain);
     dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *) a_atom;
 
     dap_chain_cs_dag_event_item_t * l_event_item = DAP_NEW_Z(dap_chain_cs_dag_event_item_t);
+    pthread_rwlock_t * l_events_rwlock = &PVT(l_dag)->events_rwlock ;
     l_event_item->event = l_event;
     l_event_item->ts_added = time(NULL);
+
     dap_hash_fast(l_event, dap_chain_cs_dag_event_calc_size(l_event),&l_event_item->hash );
+    dap_chain_hash_fast_t l_event_hash;
+    dap_chain_cs_dag_event_calc_hash(l_event,&l_event_hash);
+
+    char * l_event_hash_str = dap_chain_hash_fast_to_str_new(&l_event_item->hash);
+    log_it(L_DEBUG, "Processing event: %s...", l_event_hash_str);
 
-    // Put in main table or in the treshhold if not all the rest linked event are present
-    dap_chain_cs_dag_event_item_t * l_event_search = NULL;
-    dap_chain_cs_dag_event_item_t * l_events =( l_add_to_threshold )? PVT(l_dag)->events_treshold : PVT(l_dag)->events ;
-    pthread_rwlock_t * l_events_rwlock = &PVT(l_dag)->events_rwlock ;
     pthread_rwlock_wrlock( l_events_rwlock );
-    HASH_FIND(hh, l_events,&l_event_item->hash,sizeof (l_event_search->hash),  l_event_search);
-    if ( l_event_search ) {
-        pthread_rwlock_unlock( l_events_rwlock );
-        char * l_hash_str = dap_chain_hash_fast_to_str_new(&l_event_item->hash);
-        log_it(L_ERROR, "Dag event %s is already present in dag",l_hash_str);
-        DAP_DELETE(l_event_item);
-        DAP_DELETE(l_hash_str);
-        return -3;
+
+    // check if we already have this event
+    if(s_dap_chain_check_if_event_is_present(PVT(l_dag)->events, &l_event_item->hash)){
+        ret = ATOM_PASS;
+        log_it(L_DEBUG, "... already present in events");
+    }else if(s_dap_chain_check_if_event_is_present(PVT(l_dag)->events_treshold, &l_event_item->hash)){
+        ret = ATOM_PASS;
+        log_it(L_DEBUG, "... already present in threshold");
     }
 
-    int res = 0;
-    if(l_add_to_threshold){
+    // verify hashes and consensus
+    if(ret == ATOM_ACCEPT)
+        ret = s_chain_callback_atom_verify (a_chain, a_atom);
+
+    if( ret == ATOM_MOVE_TO_THRESHOLD){
         HASH_ADD(hh, PVT(l_dag)->events_treshold,hash,sizeof (l_event_item->hash),  l_event_item);
-    }else{
-        res = s_dap_chain_add_atom_to_events_table(l_dag, a_chain->ledger, l_event_item);
+        log_it(L_DEBUG, "... added to threshold");
+    }else if( ret == ATOM_ACCEPT){
+        int l_consensus_check = s_dap_chain_add_atom_to_events_table(l_dag, a_chain->ledger, l_event_item);
+        if(!l_consensus_check){
+             log_it(L_DEBUG, "... added");
+        }else{
+             log_it(L_DEBUG, "... error adding");
+             ret = ATOM_REJECT;
+        }
     }
 
     while(dap_chain_cs_dag_proc_treshold(l_dag, a_chain->ledger));
     pthread_rwlock_unlock( l_events_rwlock );
 
-    return res;
+    if(ret == ATOM_PASS){
+        DAP_DELETE(l_event_item);
+    }
+
+   DAP_DELETE(l_event_hash_str);
+
+    return ret;
 }
 
 /**
@@ -542,14 +568,16 @@ dap_chain_cs_dag_event_t* dap_chain_cs_dag_find_event_by_hash(dap_chain_cs_dag_t
  * @param a_atom
  * @return
  */
-static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t  a_atom)
+static dap_chain_atom_verify_res_t s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_ptr_t  a_atom)
 {
     dap_chain_cs_dag_t * l_dag = DAP_CHAIN_CS_DAG(a_chain);
     dap_chain_cs_dag_event_t * l_event = (dap_chain_cs_dag_event_t *) a_atom;
+    dap_chain_atom_verify_res_t res = ATOM_ACCEPT;
+
+    // genesis or seed mode
     if (l_event->header.hash_count == 0){
       if(s_seed_mode && !PVT(l_dag)->events)
-        //starting a new network and this is a genesis event
-        return 0;
+        return ATOM_ACCEPT;
 
       if (l_dag->is_static_genesis_event ){
         dap_chain_hash_fast_t l_event_hash;
@@ -561,36 +589,37 @@ static int s_chain_callback_atom_verify(dap_chain_t * a_chain, dap_chain_atom_pt
           log_it(L_WARNING, "Wrong genesis block %s (staticly predefined %s)",l_event_hash_str, l_genesis_event_hash_str);
           DAP_DELETE(l_event_hash_str);
           DAP_DELETE(l_genesis_event_hash_str);
-          return -22;
+          return ATOM_REJECT;
+        }else{
+          return ATOM_ACCEPT;
         }
-        return 0;
       }
     }
 
-    int ret = l_dag->callback_cs_verify ( l_dag, l_event );
-    if (ret == 0 ){
-        if ( PVT(l_dag)->events ){
-            for (size_t i = 0; i< l_event->header.hash_count; i++) {
-                dap_chain_hash_fast_t * l_hash =  ((dap_chain_hash_fast_t *) l_event->hashes_n_datum_n_signs) + i;
-                dap_chain_cs_dag_event_item_t * l_event_search = NULL;
-                HASH_FIND(hh, PVT(l_dag)->events ,l_hash ,sizeof (*l_hash),  l_event_search);
-                if ( l_event_search == NULL ){
-                    char * l_hash_str = dap_chain_hash_fast_to_str_new(l_hash);
-                    log_it(L_DEBUG, "Hash %s wasn't in hashtable of previously parsed", l_hash_str);
-                    DAP_DELETE(l_hash_str);
-                    return 1;
-                }
+    //chain coherence
+    if (! PVT(l_dag)->events ){
+        res = ATOM_MOVE_TO_THRESHOLD;
+    }else{
+        for (size_t i = 0; i< l_event->header.hash_count; i++) {
+            dap_chain_hash_fast_t * l_hash =  ((dap_chain_hash_fast_t *) l_event->hashes_n_datum_n_signs) + i;
+            dap_chain_cs_dag_event_item_t * l_event_search = NULL;
+            HASH_FIND(hh, PVT(l_dag)->events ,l_hash ,sizeof (*l_hash),  l_event_search);
+            if ( l_event_search == NULL ){
+                char * l_hash_str = dap_chain_hash_fast_to_str_new(l_hash);
+                log_it(L_INFO, "Hash %s wasn't in hashtable of previously parsed", l_hash_str);
+                DAP_DELETE(l_hash_str);
+                res = ATOM_MOVE_TO_THRESHOLD;
+                break;
             }
-          return 0;
-        }else{
-          //event looks fine but we have no hash table yet and can't verify it's hashes
-          //so it goes into threshold
-          return 1;
         }
-
-    }else {
-        return  ret;
     }
+
+    //consensus
+    if(res == ATOM_ACCEPT)
+        if(l_dag->callback_cs_verify ( l_dag, l_event ))
+            res = ATOM_REJECT;
+
+    return res;
 }
 
 /**
@@ -698,7 +727,17 @@ bool dap_chain_cs_dag_proc_treshold(dap_chain_cs_dag_t * a_dag, dap_ledger_t * a
             HASH_DEL(PVT(a_dag)->events_treshold,l_event_item);
 
             if(ret == DAP_THRESHOLD_OK){
-                s_dap_chain_add_atom_to_events_table(a_dag, a_ledger, l_event_item);
+                char * l_event_hash_str = dap_chain_hash_fast_to_str_new(&l_event_item->hash);
+                log_it(L_DEBUG, "Processing event (threshold): %s...", l_event_hash_str);
+
+                int l_add_res = s_dap_chain_add_atom_to_events_table(a_dag, a_ledger, l_event_item);
+                if(! l_add_res){
+                    log_it(L_DEBUG, "... added", l_event_hash_str);
+                }else{
+                    log_it(L_DEBUG, "... error adding", l_event_hash_str);
+                    //todo: delete event
+                }
+                DAP_DELETE(l_event_hash_str);
                 res = true;
             }else if(ret == DAP_THRESHOLD_CONFLICTING)
                 HASH_ADD(hh, PVT(a_dag)->events_treshold_conflicted, hash,sizeof (l_event_item->hash),  l_event_item);
diff --git a/modules_dynamic/CMakeLists.txt b/modules_dynamic/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f4c99055a84659c988d3da1e5da165433129084e
--- /dev/null
+++ b/modules_dynamic/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(cdb)
+
diff --git a/modules_dynamic/cdb/CMakeLists.txt b/modules_dynamic/cdb/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a53e016eb7276048420e90c215c72ee992acd1da
--- /dev/null
+++ b/modules_dynamic/cdb/CMakeLists.txt
@@ -0,0 +1,14 @@
+cmake_minimum_required(VERSION 2.8)
+project (dap_modules_dynamic_cdb)
+
+file(GLOB DAP_MODULES_DYNAMIC_CDB_SRCS *.c)
+
+file(GLOB DAP_MODULES_DYNAMIC_CDB_HEADERS include/*.h)
+
+add_library(${PROJECT_NAME} STATIC ${DAP_MODULES_DYNAMIC_CDB_SRCS} ${DAP_MODULES_DYNAMIC_CDB_HEADERS})
+
+#target_link_libraries(dap_chain_net_srv dap_core dap_crypto dap_chain dap_chain_net dap_chain_wallet)
+target_link_libraries(${PROJECT_NAME} dap_core dap_http_server ${CMAKE_DL_LIBS})
+
+target_include_directories(${PROJECT_NAME} INTERFACE .)
+target_include_directories(${PROJECT_NAME} PUBLIC include)
diff --git a/modules_dynamic/cdb/dap_modules_dynamic_cdb.c b/modules_dynamic/cdb/dap_modules_dynamic_cdb.c
new file mode 100644
index 0000000000000000000000000000000000000000..696838cbeb2a120d60ea81bdf9bacc4daa3d0cf9
--- /dev/null
+++ b/modules_dynamic/cdb/dap_modules_dynamic_cdb.c
@@ -0,0 +1,69 @@
+/*
+ * Authors:
+ * Aleksei I. Voronin <aleksei.voronin@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ 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 "dap_modules_dynamic_cdb.h"
+#include "dap_common.h"
+
+#ifdef DAP_OS_LINUX
+#include <dlfcn.h>
+#endif
+
+#define LOG_TAG "dap_http"
+
+static const char * s_default_path_modules = "var/modules";
+
+int dap_modules_dynamic_load_cdb(dap_http_t * a_server){
+    char l_lib_path[MAX_PATH] = {'\0'};
+#if defined (DAP_OS_LINUX) && !defined (__ANDROID__)
+    const char * l_cdb_so_name = "libcellframe-node-cdb.so";
+    dap_sprintf(l_lib_path, "%s/%s/%s", g_sys_dir_path, s_default_path_modules, l_cdb_so_name);
+
+    void* l_cdb_handle = NULL;
+    l_cdb_handle = dlopen(l_lib_path, RTLD_NOW);
+    if(!l_cdb_handle){
+        log_it(L_ERROR,"Can't load %s module: %s", l_cdb_so_name, dlerror());
+        return -1;
+    }
+
+    int (*dap_chain_net_srv_vpn_cdb_init)(dap_http_t*);
+    const char * l_init_func_name = "dap_chain_net_srv_vpn_cdb_init";
+    *(void **) (&dap_chain_net_srv_vpn_cdb_init) = dlsym(l_cdb_handle, l_init_func_name);
+    char* error;
+    if (( error = dlerror()) != NULL) {
+        log_it(L_ERROR,"%s module: %s error loading %s (%s)", l_cdb_so_name, l_init_func_name, error);
+        return -2;
+     }
+
+    int l_init_res = (*dap_chain_net_srv_vpn_cdb_init)(a_server);
+    if(l_init_res){
+        log_it(L_ERROR,"%s: %s returns %d", l_cdb_so_name, l_init_func_name, error);
+        return -3;
+    }
+
+    return 0;
+#else
+    log_it(L_ERROR,"%s: module is not supported on current platfrom", __PRETTY_FUNCTION__);
+    return -3;
+#endif
+}
diff --git a/modules_dynamic/cdb/include/dap_modules_dynamic_cdb.h b/modules_dynamic/cdb/include/dap_modules_dynamic_cdb.h
new file mode 100644
index 0000000000000000000000000000000000000000..492b98c71127b030ed44527e276fa41eeda9d9e1
--- /dev/null
+++ b/modules_dynamic/cdb/include/dap_modules_dynamic_cdb.h
@@ -0,0 +1,29 @@
+/*
+ * Authors:
+ * Aleksei I. Voronin <aleksei.voronin@demlabs.net>
+ * DeM Labs Inc.   https://demlabs.net
+ * Kelvin Project https://github.com/kelvinblockchain
+ * Copyright  (c) 2017-2019
+ * All rights reserved.
+
+ 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 "dap_http.h"
+
+int dap_modules_dynamic_load_cdb(dap_http_t * a_server);
+