diff --git a/bin/Makefile.am b/bin/Makefile.am
index deb5c45591be8e44eadf9c43d64a008f8ed62452..bbe9d273e158ac60280bc5990c50412094a2a8c6 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -35,15 +35,15 @@ endif
 if RING_RESTCPP
 SUBDIRS=restcpp
 
-sbin_PROGRAMS = dring
+sbin_PROGRAMS = restdring
 
-dring_SOURCES = main.cpp
+restdring_SOURCES = main.cpp
 
-dring_CXXFLAGS= -g \
+restdring_CXXFLAGS= -g \
                 -I$(top_srcdir)/src \
                 -I$(top_srcdir)/src/dring \
                 -DREST_API \
                 -DTOP_BUILDDIR=\"$$(cd "$(top_builddir)"; pwd)\"
 
-dring_LDADD = restcpp/libclient_rest.la $(top_builddir)/src/libring.la
+restdring_LDADD = restcpp/libclient_rest.la $(top_builddir)/src/libring.la
 endif
diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
index 4bc665df95b2075a830185be2f0deb741ba1b9ec..76c36ca7537492b16cb1ed4af7948fe69a0616a0 100644
--- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
+++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml
@@ -220,6 +220,118 @@
            </arg>
        </method>
 
+      <method name="lookupName" tp:name-for-bindings="lookupName">
+           <tp:docstring>
+               Performs name lookup with RingNS protocol for the specified account (if any) or using the default nameserver.
+           </tp:docstring>
+           <arg type="s" name="accountID" direction="in">
+              The account to use. If empty, use the default RingNS server.
+           </arg>
+           <arg type="s" name="nameserverUri" direction="in">
+              The name server URI to use, considered only if accountID is empty.
+           </arg>
+           <arg type="s" name="name" direction="in">
+           </arg>
+           <arg type="b" name="success" direction="out">
+               <tp:docstring>
+                   True if the operation was initialized successfully. registeredNameFound will be trigered on completion.
+               </tp:docstring>
+           </arg>
+      </method>
+      <method name="lookupAddress" tp:name-for-bindings="lookupAddress">
+           <tp:docstring>
+               Performs address lookup with RingNS protocol for the specified account (if any) or using the default nameserver.
+           </tp:docstring>
+           <arg type="s" name="accountID" direction="in">
+              The account to use. If empty, use the default RingNS server.
+           </arg>
+           <arg type="s" name="nameserverUri" direction="in">
+              The name server URI to use, considered only if accountID is empty.
+           </arg>
+           <arg type="s" name="address" direction="in">
+               <tp:docstring>
+                   Address to lookup for. Must not include spaces.
+               </tp:docstring>
+           </arg>
+           <arg type="b" name="success" direction="out">
+               <tp:docstring>
+                   True if the operation was initialized successfully. registeredNameFound will be trigered on completion.
+                   False in case of operation initialization error. registeredNameFound won't be called in that case.
+               </tp:docstring>
+           </arg>
+      </method>
+      <signal name="registeredNameFound" tp:name-for-bindings="registeredNameFound">
+           <tp:docstring>
+               Notify clients when a new registered address-name mapping is known.
+               If status is not success (0), requested field (name or address) is left empty.
+           </tp:docstring>
+           <arg type="s" name="accountID">
+           </arg>
+           <arg type="i" name="status">
+               <tp:docstring>
+                  Status code: 0 for success
+                  <ul>
+                      <li>SUCCESS = 0         everything went fine. Name/address pair was found.</li>
+                      <li>INVALID_NAME = 1    provided name is not valid.</li>
+                      <li>NOT_FOUND = 2       everything went fine. Name/address pair was not found.</li>
+                      <li>ERROR = 3           An error happened</li>
+                  </ul>
+               </tp:docstring>
+           </arg>
+           <arg type="s" name="address">
+           </arg>
+           <arg type="s" name="name">
+           </arg>
+      </signal>
+
+      <method name="registerName" tp:name-for-bindings="registerName">
+           <tp:docstring>
+               Performs name registration with RingNS protocol for the specified account.
+           </tp:docstring>
+           <arg type="s" name="accountID" direction="in">
+           </arg>
+           <arg type="s" name="password" direction="in">
+             <tp:docstring>
+                 Ring account main password.
+             </tp:docstring>
+           </arg>
+           <arg type="s" name="name" direction="in">
+               <tp:docstring>
+                   Name to register. Must be lower-case ASCII, digits or "-" or "_".
+               </tp:docstring>
+           </arg>
+           <arg type="b" name="success" direction="out">
+               <tp:docstring>
+                   True if the operation was initialized successfully. nameRegistrationEnded will be trigered on completion.
+                   False in case of operation initialization error. nameRegistrationEnded won't be called in that case.
+               </tp:docstring>
+           </arg>
+      </method>
+       <signal name="nameRegistrationEnded" tp:name-for-bindings="nameRegistrationEnded">
+           <tp:docstring>
+               Notify clients when the registerName operation ended.
+           </tp:docstring>
+           <arg type="s" name="accountID">
+           </arg>
+           <arg type="i" name="status">
+               <tp:docstring>
+                   Status code: 0 for success
+                  <ul>
+                      <li>SUCCESS = 0         everything went fine. Name is now registered.</li>
+                      <li>WRONG_PASSWORD = 1  registration failed: wrong password.</li>
+                      <li>INVALID_NAME = 2    registration failed: invalid name.</li>
+                      <li>ALREADY_TAKEN = 3   registration failed: name is already taken.</li>
+                      <li>NETWORK_ERROR = 4   registration failed: network or server error.</li>
+                  </ul>
+               </tp:docstring>
+           </arg>
+           <arg type="s" name="name">
+               <tp:docstring>
+                   The name that was attempted to register.
+               </tp:docstring>
+           </arg>
+       </signal>
+
        <method name="testAccountICEInitialization" tp:name-for-bindings="testAccountICEInitialization">
            <tp:docstring>
                Test initializing an ICE transport with the current account configuration.
diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp
index 6ef794a62ac0117c63cc007097a172f63e0a1c81..093998ac506c0f654ccdbec17ef2b356c437065f 100644
--- a/bin/dbus/dbusclient.cpp
+++ b/bin/dbus/dbusclient.cpp
@@ -178,6 +178,8 @@ DBusClient::initLibrary(int flags)
         exportable_callback<ConfigurationSignal::IncomingTrustRequest>(bind(&DBusConfigurationManager::incomingTrustRequest, confM, _1, _2, _3, _4 )),
         exportable_callback<ConfigurationSignal::ExportOnRingEnded>(bind(&DBusConfigurationManager::exportOnRingEnded, confM, _1, _2, _3 )),
         exportable_callback<ConfigurationSignal::KnownDevicesChanged>(bind(&DBusConfigurationManager::knownDevicesChanged, confM, _1, _2 )),
+        exportable_callback<ConfigurationSignal::NameRegistrationEnded>(bind(&DBusConfigurationManager::nameRegistrationEnded, confM, _1, _2, _3 )),
+        exportable_callback<ConfigurationSignal::RegisteredNameFound>(bind(&DBusConfigurationManager::registeredNameFound, confM, _1, _2, _3, _4 )),
         exportable_callback<ConfigurationSignal::CertificatePinned>(bind(&DBusConfigurationManager::certificatePinned, confM, _1 )),
         exportable_callback<ConfigurationSignal::CertificatePathPinned>(bind(&DBusConfigurationManager::certificatePathPinned, confM, _1, _2 )),
         exportable_callback<ConfigurationSignal::CertificateExpired>(bind(&DBusConfigurationManager::certificateExpired, confM, _1 )),
diff --git a/bin/dbus/dbusconfigurationmanager.cpp b/bin/dbus/dbusconfigurationmanager.cpp
index 62dc9f8ddee4b38528bb734c49ec4bf92ad86d4d..fdf6f59bfe89f9d54ec318334818e7bb609d7152 100644
--- a/bin/dbus/dbusconfigurationmanager.cpp
+++ b/bin/dbus/dbusconfigurationmanager.cpp
@@ -86,6 +86,24 @@ DBusConfigurationManager::getKnownRingDevices(const std::string& accountID) -> d
     return DRing::getKnownRingDevices(accountID);
 }
 
+auto
+DBusConfigurationManager::lookupName(const std::string& account, const std::string& nameserver, const std::string& name) -> decltype(DRing::lookupName(account, nameserver, name))
+{
+    return DRing::lookupName(account, nameserver, name);
+}
+
+auto
+DBusConfigurationManager::lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address) -> decltype(DRing::lookupAddress(account, nameserver, address))
+{
+    return DRing::lookupAddress(account, nameserver, address);
+}
+
+auto
+DBusConfigurationManager::registerName(const std::string& account, const std::string& password, const std::string& name) -> decltype(DRing::registerName(account, password, name))
+{
+    return DRing::registerName(account, password, name);
+}
+
 void
 DBusConfigurationManager::removeAccount(const std::string& accountID)
 {
diff --git a/bin/dbus/dbusconfigurationmanager.h b/bin/dbus/dbusconfigurationmanager.h
index c25be1eb1ee1c10a86a4cea6f23f1dc1bf9ec5b3..562c9ab4f91d03ca07bd5c9c17e70d6d5432fc5c 100644
--- a/bin/dbus/dbusconfigurationmanager.h
+++ b/bin/dbus/dbusconfigurationmanager.h
@@ -65,6 +65,9 @@ class DBusConfigurationManager :
         std::string addAccount(const std::map<std::string, std::string>& details);
         bool exportOnRing(const std::string& accountID, const std::string& password);
         std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID);
+        bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
+        bool lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address);
+        bool registerName(const std::string& account, const std::string& password, const std::string& name);
         void removeAccount(const std::string& accoundID);
         std::vector<std::string> getAccountList();
         void sendRegister(const std::string& accoundID, const bool& enable);
diff --git a/configure.ac b/configure.ac
index d4ede1d0cb9c8a6e1f16dfe6948602af89908797..fbc6a6aaa12c7c2a351898a3c5b819d860a23da1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -385,20 +385,26 @@ AS_IF([test "x$with_dbus" = "xyes"], [
        AM_CONDITIONAL(RING_DBUS, true)],
        AM_CONDITIONAL(RING_DBUS, false));
 
+dnl Ring name service is default-enabled
+AC_ARG_ENABLE([ringns], AS_HELP_STRING([--disable-ringns], [Enable Ring Name Service]))
+AM_CONDITIONAL([RINGNS], test "x$enable_ringns" != "xno", [Define if you use the Ring Name Service])
+AC_DEFINE_UNQUOTED([HAVE_RINGNS], `if test "x$enable_ringns" != "xno"; then echo 1; else echo 0; fi`, [Define if you use the Ring Name Service])
+
 # Rest C++ with restbed
 AC_ARG_WITH([restcpp],
     AS_HELP_STRING([--with-restcpp], [enable rest support with C++]))
 
-AS_IF([test "x$with_restcpp" = "xyes"], [
-    PKG_CHECK_MODULES(RESTBED, librestbed,, AC_MSG_WARN([Missing restbed files]))
+AS_IF([test "x$enable_ringns" != "xno" || test "x$with_restcpp" = "xyes"],
+  AC_CHECK_LIB(restbed, exit,, AC_MSG_ERROR([Missing restbed files])));
 
+AS_IF([test "x$with_restcpp" = "xyes"], [
     AS_AC_EXPAND(SBINDIR, $sbindir)
     AC_SUBST(SBINDIR)
-
     AC_CONFIG_FILES([bin/restcpp/Makefile])
-
-    AM_CONDITIONAL(RING_RESTCPP, true)],
-    AM_CONDITIONAL(RING_RESTCPP, false));
+    AM_CONDITIONAL(RING_RESTCPP, true)
+  ],
+  AM_CONDITIONAL(RING_RESTCPP, false)
+);
 
 dnl Check for libav
 PKG_CHECK_MODULES(LIBAVCODEC, libavcodec >= 53.5.0,, AC_MSG_ERROR([Missing libavcodec development files]))
diff --git a/src/Makefile.am b/src/Makefile.am
index c9e4bf7773158eff29bef9010d0083d34c538c42..1c36d1799b7df73f92fd68e6cd54c7b4c40cdb2b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,7 +55,8 @@ libring_la_LDFLAGS = \
 		@GNUTLS_LIBS@ \
 		@OPENDHT_LIBS@ \
 		@ZLIB_LIBS@ \
-		$(PCRE_LIBS)
+		$(PCRE_LIBS) \
+		@LIBS@
 
 if HAVE_WIN32
 libring_la_LDFLAGS += -no-undefined -avoid-version
diff --git a/src/client/configurationmanager.cpp b/src/client/configurationmanager.cpp
index 157ce84d599319862b3e6c3c7306a7c5795351ee..97132fec0a183d50f9990cda984883b97af736f6 100644
--- a/src/client/configurationmanager.cpp
+++ b/src/client/configurationmanager.cpp
@@ -61,6 +61,7 @@ namespace DRing {
 constexpr unsigned CODECS_NOT_LOADED = 0x1000; /** Codecs not found */
 
 using ring::SIPAccount;
+using ring::RingAccount;
 using ring::tls::TlsValidator;
 using ring::tls::CertificateStore;
 using ring::DeviceType;
@@ -828,12 +829,52 @@ connectivityChanged()
         RING_ERR("UPnP context error: %s", e.what());
     }
 
-    auto account_list = ring::Manager::instance().getAccountList();
-    for (auto account_id : account_list) {
-        if (auto account = ring::Manager::instance().getAccount(account_id)) {
-            account->connectivityChanged();
-        }
+    for (const auto &account : ring::Manager::instance().getAllAccounts()) {
+        account->connectivityChanged();
+    }
+}
+
+bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name)
+{
+#if HAVE_RINGNS
+    if (account.empty()) {
+        ring::NameDirectory::instance(nameserver).lookupName(name, [name](const std::string& result, ring::NameDirectory::Response response) {
+            ring::emitSignal<DRing::ConfigurationSignal::RegisteredNameFound>("", (int)response, result, name);
+        });
+        return true;
+    } else if (auto acc = ring::Manager::instance().getAccount<RingAccount>(account)) {
+        acc->lookupName(name);
+        return true;
+    }
+#endif
+    return false;
+}
+
+bool lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address)
+{
+#if HAVE_RINGNS
+    if (account.empty()) {
+        ring::NameDirectory::instance(nameserver).lookupAddress(address, [address](const std::string& result, ring::NameDirectory::Response response) {
+            ring::emitSignal<DRing::ConfigurationSignal::RegisteredNameFound>("", (int)response, address, result);
+        });
+        return true;
+    } else if (auto acc = ring::Manager::instance().getAccount<RingAccount>(account)) {
+        acc->lookupAddress(address);
+        return true;
     }
+#endif
+    return false;
+}
+
+bool registerName(const std::string& account, const std::string& password, const std::string& name)
+{
+#if HAVE_RINGNS
+    if (auto acc = ring::Manager::instance().getAccount<RingAccount>(account)) {
+        acc->registerName(password, name);
+        return true;
+    }
+#endif
+    return false;
 }
 
 } // namespace DRing
diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp
index 09c10202b0fd98375296572c2cfa6ade1eb7887a..e16243095807b3dba01d179b0747aaaa320b9cfc 100644
--- a/src/client/ring_signal.cpp
+++ b/src/client/ring_signal.cpp
@@ -65,6 +65,8 @@ getSignalHandlers()
         exported_callback<DRing::ConfigurationSignal::IncomingTrustRequest>(),
         exported_callback<DRing::ConfigurationSignal::ExportOnRingEnded>(),
         exported_callback<DRing::ConfigurationSignal::KnownDevicesChanged>(),
+        exported_callback<DRing::ConfigurationSignal::NameRegistrationEnded>(),
+        exported_callback<DRing::ConfigurationSignal::RegisteredNameFound>(),
         exported_callback<DRing::ConfigurationSignal::MediaParametersChanged>(),
         exported_callback<DRing::ConfigurationSignal::Error>(),
 #ifdef __ANDROID__
diff --git a/src/dring/account_const.h b/src/dring/account_const.h
index 5ac7dfd56dd781a6913eee88667b491cb38fa8fd..9db2f649e654aadc3c01b0ed33aeb2fdcb84fd00 100644
--- a/src/dring/account_const.h
+++ b/src/dring/account_const.h
@@ -76,6 +76,7 @@ enum class testAccountICEInitializationStatus : int {
 namespace VolatileProperties {
 
 constexpr static const char ACTIVE                    [] = "Account.active";
+constexpr static const char REGISTERED_NAME           [] = "Account.registredName";
 
 // Volatile parameters
 namespace Registration {
@@ -222,12 +223,12 @@ constexpr static const char ALLOW_FROM_TRUSTED [] = "DHT.AllowFromTrusted";
 
 } //namespace DRing::Account::DHT
 
-namespace ETH {
+namespace RingNS {
 
-constexpr static const char KEY_FILE           [] = "ETH.keyFile";
-constexpr static const char ACCOUNT            [] = "ETH.account";
+constexpr static const char URI         [] = "RingNS.uri";
+constexpr static const char ACCOUNT     [] = "RingNS.account";
 
-} //namespace DRing::Account::ETH
+} //namespace DRing::Account::RingNS
 
 namespace CodecInfo {
 
diff --git a/src/dring/call_const.h b/src/dring/call_const.h
index 98486238a671e063acddd275099b161fa9f92846..9bd26b15e2d6c40eb231390a983b843c396ceeaa 100644
--- a/src/dring/call_const.h
+++ b/src/dring/call_const.h
@@ -44,6 +44,7 @@ namespace Details {
 
 constexpr static char CALL_TYPE                [] = "CALL_TYPE"           ;
 constexpr static char PEER_NUMBER              [] = "PEER_NUMBER"         ;
+constexpr static char REGISTERED_NAME          [] = "REGISTERED_NAME"     ;
 constexpr static char DISPLAY_NAME             [] = "DISPLAY_NAME"        ;
 constexpr static char CALL_STATE               [] = "CALL_STATE"          ;
 constexpr static char CONF_ID                  [] = "CONF_ID"             ;
diff --git a/src/dring/configurationmanager_interface.h b/src/dring/configurationmanager_interface.h
index dae105ed9b28a2be8bf6f2474085b4226d61e230..dff11f96833b3e08a2fda5f0d5c22ecd91011757 100644
--- a/src/dring/configurationmanager_interface.h
+++ b/src/dring/configurationmanager_interface.h
@@ -48,6 +48,10 @@ std::string addAccount(const std::map<std::string, std::string>& details);
 bool exportOnRing(const std::string& accountID, const std::string& password);
 std::map<std::string, std::string> getKnownRingDevices(const std::string& accountID);
 
+bool lookupName(const std::string& account, const std::string& nameserver, const std::string& name);
+bool lookupAddress(const std::string& account, const std::string& nameserver, const std::string& address);
+bool registerName(const std::string& account, const std::string& password, const std::string& name);
+
 void removeAccount(const std::string& accountID);
 void setAccountEnabled(const std::string& accountID, bool enable);
 std::vector<std::string> getAccountList();
@@ -216,10 +220,18 @@ struct ConfigurationSignal {
                 constexpr static const char* name = "ExportOnRingEnded";
                 using cb_type = void(const std::string& /*account_id*/, int state, const std::string& pin);
         };
+        struct NameRegistrationEnded {
+                constexpr static const char* name = "NameRegistrationEnded";
+                using cb_type = void(const std::string& /*account_id*/, int state, const std::string& name);
+        };
         struct KnownDevicesChanged {
                 constexpr static const char* name = "KnownDevicesChanged";
                 using cb_type = void(const std::string& /*account_id*/, const std::map<std::string, std::string>& devices);
         };
+        struct RegisteredNameFound {
+                constexpr static const char* name = "RegisteredNameFound";
+                using cb_type = void(const std::string& /*account_id*/, int state, const std::string& /*address*/, const std::string& /*name*/);
+        };
         struct CertificatePinned {
                 constexpr static const char* name = "CertificatePinned";
                 using cb_type = void(const std::string& /*certId*/);
diff --git a/src/ringdht/Makefile.am b/src/ringdht/Makefile.am
index 4654df9a7379c1c95ef190c6c99d9d97d2e9f0ed..ecbc4963858a1b4160395eaf92cdd62e66d469cb 100644
--- a/src/ringdht/Makefile.am
+++ b/src/ringdht/Makefile.am
@@ -11,9 +11,7 @@ libringacc_la_CXXFLAGS = @CXXFLAGS@ @JSONCPP_CFLAGS@
 
 libringacc_la_LIBADD = $(DHT_LIBS) \
     $(BOOST_SYSTEM_LIB) \
-    $(BOOST_FILESYSTEM_LIB) \
     $(BOOST_RANDOM_LIB) \
-    $(BOOST_THREAD_LIB) \
     ./eth/libdevcore/libdevcore.la \
     ./eth/libdevcrypto/libdevcrypto.la
 
@@ -24,3 +22,9 @@ libringacc_la_SOURCES = \
         sip_transport_ice.h \
         sips_transport_ice.cpp \
         sips_transport_ice.h
+
+if RINGNS
+libringacc_la_SOURCES += \
+        namedirectory.cpp \
+        namedirectory.h
+endif
diff --git a/src/ringdht/namedirectory.cpp b/src/ringdht/namedirectory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0f6994948db80f90fc3a5e9f784396cf6324ceb8
--- /dev/null
+++ b/src/ringdht/namedirectory.cpp
@@ -0,0 +1,261 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program 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.
+ *
+ *  This 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 General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "namedirectory.h"
+
+#include "logger.h"
+#include "string_utils.h"
+#include "thread_pool.h"
+
+#include <json/json.h>
+#include <restbed>
+
+/* for visual studio */
+#include <ciso646>
+#include <sstream>
+#include <regex>
+
+namespace ring {
+
+constexpr const char* const QUERY_NAME {"/name/"};
+constexpr const char* const QUERY_ADDR {"/addr/"};
+const std::regex NAME_VALIDATOR {"^[a-z0-9-_]{3,32}$"};
+const std::regex URI_VALIDATOR {"^(:?[a-zA-Z]+://)?([a-zA-Z0-9\\-._~%!$&'()*+,;=:\\[\\]]+)"};
+
+constexpr size_t MAX_RESPONSE_SIZE {1024 * 1024};
+
+std::string hostFromUri(const std::string& uri)
+{
+    std::smatch pieces_match;
+    if (std::regex_search(uri, pieces_match, URI_VALIDATOR))
+        if (pieces_match.size() == 3)
+            return pieces_match[2].str();
+    return uri;
+}
+
+NameDirectory::NameDirectory(const std::string& s) : serverUri_(s), serverHost_(hostFromUri(s))
+{}
+
+NameDirectory& NameDirectory::instance(const std::string& server)
+{
+    const std::string& s = server.empty() ? DEFAULT_SERVER_URI : server;
+    static std::map<std::string, NameDirectory> instances {};
+    auto it = instances.emplace(s, NameDirectory{s});
+    return it.first->second;
+}
+
+void NameDirectory::lookupAddress(const std::string& addr, LookupCallback cb)
+{
+    auto cacheRes = nameCache_.find(addr);
+    if (cacheRes != nameCache_.end()) {
+        cb(cacheRes->second, Response::found);
+        return;
+    }
+
+    restbed::Uri uri(serverUri_ + QUERY_ADDR + addr);
+    auto req = std::make_shared<restbed::Request>(uri);
+    req->set_header("Accept", "*/*");
+    req->set_header("Host", serverHost_);
+
+    RING_DBG("Address lookup for %s: %s", addr.c_str(), uri.to_string().c_str());
+
+    auto ret = restbed::Http::async(req, [this,cb,addr](const std::shared_ptr<restbed::Request>,
+                                             const std::shared_ptr<restbed::Response> reply) {
+        if (reply->get_status_code() == 200) {
+            size_t length = 0;
+            length = reply->get_header("Content-Length", length);
+            if (length > MAX_RESPONSE_SIZE) {
+                cb("", Response::error);
+                return;
+            }
+            restbed::Http::fetch(length, reply);
+            std::string body;
+            reply->get_body(body);
+
+            Json::Value json;
+            Json::Reader reader;
+            if (!reader.parse(body, json)) {
+                RING_ERR("Address lookup for %s: can't parse server response: %s", addr.c_str(), body.c_str());
+                cb("", Response::error);
+                return;
+            }
+            auto name = json["name"].asString();
+            if (not name.empty()) {
+                RING_DBG("Found name for %s: %s", addr.c_str(), name.c_str());
+                addrCache_.emplace(name, addr);
+                nameCache_.emplace(addr, name);
+                cb(name, Response::found);
+            } else {
+                cb("", Response::notFound);
+            }
+        } else {
+            cb("", Response::error);
+        }
+    }).share();
+
+    // avoid blocking on future destruction
+    ThreadPool::instance().run([ret](){ ret.get(); });
+}
+
+static const std::string HEX_PREFIX {"0x"};
+
+void NameDirectory::lookupName(const std::string& name, LookupCallback cb)
+{
+    if (not validateName(name)) {
+        cb(name, Response::invalidName);
+        return;
+    }
+
+    auto cacheRes = addrCache_.find(name);
+    if (cacheRes != addrCache_.end()) {
+        cb(cacheRes->second, Response::found);
+        return;
+    }
+
+    restbed::Uri uri(serverUri_ + QUERY_NAME + name);
+    auto request = std::make_shared<restbed::Request>(std::move(uri));
+    request->set_header("Accept", "*/*");
+    request->set_header("Host", serverHost_);
+
+    RING_DBG("Name lookup for %s: %s", name.c_str(), uri.to_string().c_str());
+
+    auto ret = restbed::Http::async(request, [this,cb,name](const std::shared_ptr<restbed::Request>,
+                                                 const std::shared_ptr<restbed::Response> reply) {
+        auto code = reply->get_status_code();
+        if (code != 200)
+            RING_DBG("Name lookup for %s: got reply code %d", name.c_str(), code);
+        if (code >= 200 && code < 300) {
+            size_t length = 0;
+            length = reply->get_header("Content-Length", length);
+            if (length > MAX_RESPONSE_SIZE) {
+                cb("", Response::error);
+                return;
+            }
+            restbed::Http::fetch(length, reply);
+            std::string body;
+            reply->get_body(body);
+
+            Json::Value json;
+            Json::Reader reader;
+            if (!reader.parse(body, json)) {
+                RING_ERR("Name lookup for %s: can't parse server response: %s", name.c_str(), body.c_str());
+                cb("", Response::error);
+                return;
+            }
+            auto addr = json["addr"].asString();
+            if (!addr.compare(0, HEX_PREFIX.size(), HEX_PREFIX))
+                addr = addr.substr(HEX_PREFIX.size());
+            if (not addr.empty()) {
+                RING_DBG("Found address for %s: %s", name.c_str(), addr.c_str());
+                addrCache_.emplace(name, addr);
+                nameCache_.emplace(addr, name);
+                cb(addr, Response::found);
+            } else {
+                cb("", Response::notFound);
+            }
+        } else if (code >= 400 && code < 500) {
+            cb("", Response::notFound);
+        } else {
+            cb("", Response::error);
+        }
+    }).share();
+
+    // avoid blocking on future destruction
+    ThreadPool::instance().run([ret](){ ret.get(); });
+}
+
+bool NameDirectory::validateName(const std::string& name) const
+{
+    return std::regex_match(name, NAME_VALIDATOR);
+}
+
+void NameDirectory::registerName(const std::string& addr, const std::string& name, const std::string& owner, RegistrationCallback cb)
+{
+    if (not validateName(name)) {
+        cb(RegistrationResponse::invalidName);
+        return;
+    }
+
+    auto cacheRes = addrCache_.find(name);
+    if (cacheRes != addrCache_.end()) {
+        if (cacheRes->second == addr)
+            cb(RegistrationResponse::success);
+        else
+            cb(RegistrationResponse::alreadyTaken);
+        return;
+    }
+
+    auto request = std::make_shared<restbed::Request>(restbed::Uri(serverUri_ + QUERY_NAME + name));
+    request->set_header("Accept", "*/*");
+    request->set_header("Host", serverHost_);
+    request->set_header("Content-Type", "application/json");
+    request->set_method("POST");
+    std::string body;
+    {
+        std::stringstream ss;
+        ss << "{\"addr\":\"" << addr << "\",\"owner\":\"" << owner << "\"}";
+        body = ss.str();
+    }
+    request->set_body(body);
+    request->set_header("Content-Length", ring::to_string(body.size()));
+
+    auto params = std::make_shared<restbed::Settings>();
+    params->set_connection_timeout(std::chrono::seconds(60));
+
+    RING_WARN("registerName: sending request %s %s", addr.c_str(), name.c_str());
+    auto ret = restbed::Http::async(request,
+                         [this,cb,addr,name](const std::shared_ptr<restbed::Request>,
+                                             const std::shared_ptr<restbed::Response> reply)
+    {
+        auto code = reply->get_status_code();
+        RING_DBG("Got reply for registration of %s -> %s: code %d", name.c_str(), addr.c_str(), code);
+        if (code >= 200 && code < 300) {
+            size_t length = 0;
+            length = reply->get_header("Content-Length", length);
+            if (length > MAX_RESPONSE_SIZE) {
+                cb(RegistrationResponse::error);
+                return;
+            }
+            restbed::Http::fetch(length, reply);
+            std::string body;
+            reply->get_body(body);
+
+            Json::Value json;
+            Json::Reader reader;
+            if (!reader.parse(body, json)) {
+                cb(RegistrationResponse::error);
+                return;
+            }
+            auto success = json["success"].asBool();
+            RING_DBG("Got reply for registration of %s -> %s: %s", name.c_str(), addr.c_str(), success ? "success" : "failure");
+            if (success) {
+                addrCache_.emplace(name, addr);
+                nameCache_.emplace(addr, name);
+            }
+            cb(success ? RegistrationResponse::success : RegistrationResponse::error);
+        } else if (code >= 400 && code < 500) {
+            cb(RegistrationResponse::alreadyTaken);
+        } else {
+            cb(RegistrationResponse::error);
+        }
+    }, params).share();
+
+    // avoid blocking on future destruction
+    ThreadPool::instance().run([ret](){ ret.get(); });
+}
+
+}
diff --git a/src/ringdht/namedirectory.h b/src/ringdht/namedirectory.h
new file mode 100644
index 0000000000000000000000000000000000000000..e8acc6f2a4855878f3feb205967fa73c5f0fdf31
--- /dev/null
+++ b/src/ringdht/namedirectory.h
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (C) 2016 Savoir-faire Linux Inc.
+ *  Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
+ *
+ *  This program 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.
+ *
+ *  This 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 General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <functional>
+#include <map>
+#include <string>
+
+namespace ring {
+
+class NameDirectory
+{
+public:
+    NameDirectory() {}
+    NameDirectory(const std::string& s);
+
+    static NameDirectory& instance(const std::string& server);
+    static NameDirectory& instance() { return instance(DEFAULT_SERVER_URI); }
+
+    enum class Response : int { found = 0, invalidName, notFound, error };
+    enum class RegistrationResponse : int { success = 0, invalidName, alreadyTaken, error };
+
+    using LookupCallback = std::function<void(const std::string& result, Response response)>;
+    void lookupAddress(const std::string& addr, LookupCallback cb);
+    void lookupName(const std::string& name, LookupCallback cb);
+
+    using RegistrationCallback = std::function<void(RegistrationResponse response)>;
+    void registerName(const std::string& addr, const std::string& name, const std::string& owner, RegistrationCallback cb);
+
+    const std::string& getServer() const {
+        return serverUri_;
+    }
+
+private:
+    constexpr static const char* const DEFAULT_SERVER_URI = "http://5.196.89.112:3000";
+
+    const std::string serverUri_ {DEFAULT_SERVER_URI};
+    const std::string serverHost_ {};
+    std::map<std::string, std::string> nameCache_;
+    std::map<std::string, std::string> addrCache_;
+
+    bool validateName(const std::string& name) const;
+
+};
+
+}
diff --git a/src/ringdht/ringaccount.cpp b/src/ringdht/ringaccount.cpp
index edabcc145b9d5852c738fb88aacf7ffdd4541917..171fd80ac219b86795d5f2d69abade1362ee09e3 100644
--- a/src/ringdht/ringaccount.cpp
+++ b/src/ringdht/ringaccount.cpp
@@ -153,6 +153,9 @@ RingAccount::createIceTransport(const Args&... args)
 
 RingAccount::RingAccount(const std::string& accountID, bool /* presenceEnabled */)
     : SIPAccountBase(accountID), via_addr_(),
+#if HAVE_RINGNS
+    nameDir_(NameDirectory::instance()),
+#endif
     cachePath_(fileutils::get_cache_dir()+DIR_SEPARATOR_STR+getAccountID()),
     dataPath_(cachePath_ + DIR_SEPARATOR_STR "values"),
     idPath_(fileutils::get_data_dir()+DIR_SEPARATOR_STR+getAccountID())
@@ -481,10 +484,13 @@ void RingAccount::serialize(YAML::Emitter &out)
     out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_CONTACT << YAML::Value << allowPeersFromContact_;
     out << YAML::Key << Conf::DHT_ALLOW_PEERS_FROM_TRUSTED << YAML::Value << allowPeersFromTrusted_;
 
+#if HAVE_RINGNS
+    out << YAML::Key << DRing::Account::ConfProperties::RingNS::URI << YAML::Value <<  nameDir_.get().getServer();
+#endif
+
     out << YAML::Key << DRing::Account::ConfProperties::ARCHIVE_PATH << YAML::Value << archivePath_;
     out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT << YAML::Value << receipt_;
     out << YAML::Key << Conf::RING_ACCOUNT_RECEIPT_SIG << YAML::Value << YAML::Binary(receiptSignature_.data(), receiptSignature_.size());
-    out << YAML::Key << DRing::Account::ConfProperties::ETH::ACCOUNT << YAML::Value << ethAccount_;
 
     // tls submap
     out << YAML::Key << Conf::TLS_KEY << YAML::Value << YAML::BeginMap;
@@ -503,12 +509,6 @@ void RingAccount::unserialize(const YAML::Node &node)
     parseValue(node, Conf::DHT_ALLOW_PEERS_FROM_CONTACT, allowPeersFromContact_);
     parseValue(node, Conf::DHT_ALLOW_PEERS_FROM_TRUSTED, allowPeersFromTrusted_);
 
-    try {
-        parseValue(node, DRing::Account::ConfProperties::ETH::ACCOUNT, ethAccount_);
-    } catch (const std::exception& e) {
-        RING_WARN("can't read eth account: %s", e.what());
-    }
-
     try {
         parseValue(node, DRing::Account::ConfProperties::ARCHIVE_PATH, archivePath_);
     } catch (const std::exception& e) {
@@ -527,6 +527,12 @@ void RingAccount::unserialize(const YAML::Node &node)
         dhtPort_ = getRandomEvenPort(DHT_PORT_RANGE);
     dhtPortUsed_ = dhtPort_;
 
+#if HAVE_RINGNS
+    std::string ringns_server;
+    parseValue(node, DRing::Account::ConfProperties::RingNS::URI, ringns_server);
+    nameDir_ = NameDirectory::instance(ringns_server);
+#endif
+
     parseValue(node, Conf::DHT_PUBLIC_IN_CALLS, dhtPublicInCalls_);
 
     loadAccount();
@@ -650,12 +656,7 @@ RingAccount::hasSignedReceipt()
     ringDeviceId_ = identity_.first->getPublicKey().getId().toString();
     username_ = RING_URI_PREFIX + id;
     announce_ = std::make_shared<dht::Value>(std::move(announce_val));
-
-    auto eth_addr = root["eth"].asString();
-    if (eth_addr != ethAccount_) {
-        RING_WARN("hasSignedReceipt() eth_addr not matching");
-        ethAccount_ = eth_addr;
-    }
+    ethAccount_ = root["eth"].asString();
 
     RING_WARN("hasSignedReceipt() -> true");
     return true;
@@ -1124,6 +1125,12 @@ RingAccount::setAccountDetails(const std::map<std::string, std::string> &details
     std::transform(archive_pin.begin(), archive_pin.end(), archive_pin.begin(), ::toupper);
     parseString(details, DRing::Account::ConfProperties::ARCHIVE_PATH,     archivePath_);
 
+#if HAVE_RINGNS
+    std::string ringns_server;
+    parseString(details, DRing::Account::ConfProperties::RingNS::URI,     ringns_server);
+    nameDir_ = NameDirectory::instance(ringns_server);
+#endif
+
     loadAccount(archive_password, archive_pin);
 }
 
@@ -1154,7 +1161,10 @@ RingAccount::getAccountDetails() const
     a.emplace(Conf::CONFIG_TLS_NEGOTIATION_TIMEOUT_SEC,    "-1");
 
     //a.emplace(DRing::Account::ConfProperties::ETH::KEY_FILE,               ethPath_);
-    a.emplace(DRing::Account::ConfProperties::ETH::ACCOUNT,                ethAccount_);
+    a.emplace(DRing::Account::ConfProperties::RingNS::ACCOUNT,               ethAccount_);
+#if HAVE_RINGNS
+    a.emplace(DRing::Account::ConfProperties::RingNS::URI,                  nameDir_.get().getServer());
+#endif
 
     return a;
 }
@@ -1164,9 +1174,50 @@ RingAccount::getVolatileAccountDetails() const
 {
     auto a = SIPAccountBase::getVolatileAccountDetails();
     a.emplace(DRing::Account::VolatileProperties::InstantMessaging::OFF_CALL, TRUE_STR);
+#if HAVE_RINGNS
+    if (not registeredName_.empty())
+        a.emplace(DRing::Account::VolatileProperties::REGISTERED_NAME, registeredName_);
+#endif
     return a;
 }
 
+#if HAVE_RINGNS
+void
+RingAccount::lookupName(const std::string& name)
+{
+    auto acc = getAccountID();
+    nameDir_.get().lookupName(name, [acc,name](const std::string& result, NameDirectory::Response response) {
+        emitSignal<DRing::ConfigurationSignal::RegisteredNameFound>(acc, 0, result, name);
+    });
+}
+
+void
+RingAccount::lookupAddress(const std::string& addr)
+{
+    auto acc = getAccountID();
+    nameDir_.get().lookupAddress(addr, [acc,addr](const std::string& result, NameDirectory::Response response) {
+        emitSignal<DRing::ConfigurationSignal::RegisteredNameFound>(acc, 0, addr, result);
+    });
+}
+
+void
+RingAccount::registerName(const std::string& password, const std::string& name)
+{
+    auto acc = getAccountID();
+    std::weak_ptr<RingAccount> w = std::static_pointer_cast<RingAccount>(shared_from_this());
+    nameDir_.get().registerName(ringAccountId_, name, ethAccount_, [acc,name,w](NameDirectory::RegistrationResponse response){
+        int res = (response == NameDirectory::RegistrationResponse::success)      ? 0 : (
+                  (response == NameDirectory::RegistrationResponse::invalidName)  ? 2 : (
+                  (response == NameDirectory::RegistrationResponse::alreadyTaken) ? 3 : 4));
+        if (response == NameDirectory::RegistrationResponse::success) {
+            if (auto this_ = w.lock())
+                this_->registeredName_ = name;
+        }
+        emitSignal<DRing::ConfigurationSignal::NameRegistrationEnded>(acc, res, name);
+    });
+}
+#endif
+
 void
 RingAccount::handleEvents()
 {
@@ -1460,6 +1511,22 @@ RingAccount::doRegister_()
             dht_.join();
         }
 
+        auto shared = std::static_pointer_cast<RingAccount>(shared_from_this());
+        std::weak_ptr<RingAccount> w {shared};
+
+#if HAVE_RINGNS
+        // Look for registered name on the blockchain
+        nameDir_.get().lookupAddress(ringAccountId_, [w](const std::string& result, const NameDirectory::Response& response) {
+            if (response == NameDirectory::Response::found)
+                if (auto this_ = w.lock()) {
+                    if (this_->registeredName_ != result) {
+                        this_->registeredName_ = result;
+                        emitSignal<DRing::ConfigurationSignal::VolatileDetailsChanged>(this_->accountID_, this_->getVolatileAccountDetails());
+                    }
+                }
+        });
+#endif
+
         dht_.setOnStatusChanged([this](dht::NodeStatus s4, dht::NodeStatus s6) {
                 RING_WARN("Dht status : IPv4 %s; IPv6 %s", dhtStatusStr(s4), dhtStatusStr(s6));
                 RegistrationState state;
@@ -1514,8 +1581,6 @@ RingAccount::doRegister_()
         if (not bootstrap.empty())
             dht_.bootstrap(bootstrap);
 
-        auto shared = std::static_pointer_cast<RingAccount>(shared_from_this());
-
         // Put device annoucement
         if (announce_) {
             auto h = dht::InfoHash(ringAccountId_);
@@ -1735,8 +1800,18 @@ RingAccount::replyToIncomingIceMsg(std::shared_ptr<SIPCall> call,
     dht::Value val { dht::IceCandidates(peer_ice_msg.id, ice->getLocalAttributesAndCandidates()) };
     val.id = vid;
 
-    // Asynchronous DHT put of our local ICE data
     std::weak_ptr<SIPCall> wcall = call;
+#if HAVE_RINGNS
+    auto from_acc_id = peer_cert ? (peer_cert->issuer ? peer_cert->issuer->getId().toString() : peer_cert->getId().toString()) : peer_ice_msg.from.toString();
+    nameDir_.get().lookupAddress(from_acc_id, [wcall](const std::string& result, const NameDirectory::Response& response){
+        if (response == NameDirectory::Response::found)
+            if (auto call = wcall.lock())
+                call->setPeerRegistredName(result);
+    });
+#endif
+
+    // Asynchronous DHT put of our local ICE data
+    auto shared_this = std::static_pointer_cast<RingAccount>(shared_from_this());
     dht_.putEncrypted(
         callKey_,
         peer_ice_msg.from,
@@ -1779,6 +1854,12 @@ RingAccount::replyToIncomingIceMsg(std::shared_ptr<SIPCall> call,
 void
 RingAccount::doUnregister(std::function<void(bool)> released_cb)
 {
+    if (registrationState_ == RegistrationState::INITIALIZING
+     || registrationState_ == RegistrationState::ERROR_NEED_MIGRATION) {
+        if (released_cb) released_cb(false);
+        return;
+    }
+
     RING_WARN("doUnregister");
     {
         std::lock_guard<std::mutex> lock(callsMutex_);
diff --git a/src/ringdht/ringaccount.h b/src/ringdht/ringaccount.h
index b481fd4e916528587de8e56767f0572ccc6a21b1..cdf50d07a8afc7b498e2d93876fd25a2ea5aad22 100644
--- a/src/ringdht/ringaccount.h
+++ b/src/ringdht/ringaccount.h
@@ -42,6 +42,10 @@
 #include <list>
 #include <future>
 
+#if HAVE_RINGNS
+#include "namedirectory.h"
+#endif
+
 /**
  * @file ringaccount.h
  * @brief Ring Account is build on top of SIPAccountBase and uses DHT to handle call connectivity.
@@ -284,9 +288,15 @@ class RingAccount : public SIPAccountBase {
 
         void connectivityChanged() override;
 
-    public: // overloaded methods
+        // overloaded methods
         void flush() override;
 
+#if HAVE_RINGNS
+        void lookupName(const std::string& name);
+        void lookupAddress(const std::string& address);
+        void registerName(const std::string& password, const std::string& name);
+#endif
+
     private:
         NON_COPYABLE(RingAccount);
 
@@ -341,6 +351,11 @@ class RingAccount : public SIPAccountBase {
             MSGPACK_DEFINE_MAP(dev);
         };
 
+#if HAVE_RINGNS
+        std::reference_wrapper<NameDirectory> nameDir_;
+        std::string registeredName_;
+#endif
+
         /**
          * Compute archive encryption key and DHT storage location from password and PIN.
          */