diff --git a/www/i18n/locale-fr-FR.json b/www/i18n/locale-fr-FR.json
index ebc4acbd5ae69abceeff6e2af0e7868aef413478..df661cee1b783d57f7518e59bf2fbc4348ea4c30 100644
--- a/www/i18n/locale-fr-FR.json
+++ b/www/i18n/locale-fr-FR.json
@@ -626,7 +626,7 @@
     "WALLET_REVOKED": "Votre identité a été <b>révoquée</b> : ni votre pseudonyme ni votre clef publique ne pourront être utilisés à l'avenir pour un compte membre.",
     "WALLET_HAS_NO_SELF": "Votre identité doit d'abord avoir été publiée, et ne pas être expirée.",
     "AUTH_REQUIRED": "Authentification requise.",
-    "AUTH_INVALID_PUBKEY": "La clé publique ne correspond pas au compte connecté.",
+    "AUTH_INVALID_PUBKEY": "La clef attendue est <i class=\"ion-key\"></i> {{pubkey|formatPubkey}}...",
     "AUTH_INVALID_SCRYPT": "Identifiant ou mot de passe invalide.",
     "AUTH_INVALID_FILE": "Fichier de trousseau invalide.",
     "AUTH_FILE_ERROR": "Echec de l'ouverture du fichier de trousseau",
diff --git a/www/js/app.js b/www/js/app.js
index 283c61b7ca7ba7c839d8c96909b560f092e78cb8..7497c864ffc0d435937906477ab1d20984cc514f 100644
--- a/www/js/app.js
+++ b/www/js/app.js
@@ -103,6 +103,8 @@ angular.module('cesium', ['ionic', 'ionic-material', 'ngMessages', 'pascalprecht
         options = next.data.minData ? {minData: true} : undefined;
         if (!csWallet.isDataLoaded(options)) {
           event.preventDefault();
+          // Show loading message, when full load
+          if (!options || !options.minData) UIUtils.loading.show();
           return csWallet.loadData(options)
             .then(function() {
               preventStateChange = false;
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index 6c42b1e1d22636c5c968a2aea3fb83a03fdd5d56..91987bd83d28edc85c47c3d0599c8d1e6b9b8064 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -209,15 +209,9 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     if (options.auth && !csWallet.isAuth()) {
       return csWallet.auth(options)
         .then(function (walletData) {
-          if (walletData) {
-            // Force full load, even if min data asked
-            // Because user can wait when just filled login (by modal)
-            if (options && options.minData) options.minData = false;
-            return $scope.loadWalletData(options);
-          }
-          else { // failed to auth
-            throw 'CANCELLED';
-          }
+          if (walletData) return walletData;
+          // failed to auth
+          throw 'CANCELLED';
         });
     }
 
@@ -225,15 +219,9 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     else if (!csWallet.isLogin()) {
       return csWallet.login(options)
         .then(function (walletData) {
-          if (walletData) {
-            // Force full load, even if min data asked
-            // Because user can wait when just filled login (by modal)
-            if (options && options.minData) options.minData = false;
-            return $scope.loadWalletData(options);
-          }
-          else { // failed to login
-            throw 'CANCELLED';
-          }
+          if (walletData) return walletData;
+          // failed to login
+          throw 'CANCELLED';
         });
     }
 
@@ -341,8 +329,11 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     $scope.login = false;
     $rootScope.walletData = {};
   });
-  csWallet.api.data.on.auth($scope, function() {
+  csWallet.api.data.on.auth($scope, function(data, deferred) {
+    deferred = deferred || $q.defer();
     $scope.auth = true;
+    deferred.resolve();
+    return deferred.promise;
   });
   csWallet.api.data.on.unauth($scope, function() {
     $scope.auth = false;
diff --git a/www/js/controllers/login-controllers.js b/www/js/controllers/login-controllers.js
index 5184cbd4235bc3873262cd062d825f81fc56df29..8487c8333067cef4ea501c2314eb4206ba807feb 100644
--- a/www/js/controllers/login-controllers.js
+++ b/www/js/controllers/login-controllers.js
@@ -406,6 +406,8 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
     }
   };
 
+
+
   // Default action
   $scope.init();
 
diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js
index 5c893f869617d0234aa1359e31c7d85845e486c0..54db54fa99e1576dfbd89ae98e832442dcb58ecb 100644
--- a/www/js/services/device-services.js
+++ b/www/js/services/device-services.js
@@ -94,7 +94,7 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
             }
           },
           function(err) {
-            console.log('XXX -> ' + err);
+            console.error('[device] Error while using barcode scanner -> ' + err);
             deferred.reject(err);
           },
           n);
diff --git a/www/js/services/settings-services.js b/www/js/services/settings-services.js
index 0828792d112ca1c78a397835949c5bae94331f31..811afb4667bd8d6b448f75de26b52a21126beb0f 100644
--- a/www/js/services/settings-services.js
+++ b/www/js/services/settings-services.js
@@ -163,7 +163,7 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     return promise
       .then(function() {
         if (data.useLocalStorage) {
-          console.debug('[setting] Saved');
+          console.debug('[setting] Saved locally');
         }
 
         // Emit event on store
diff --git a/www/js/services/wallet-services.js b/www/js/services/wallet-services.js
index c325d0bb2752ba47612e001e323b9b5e277d6a7c..6f637d59fba8d2512cab4eddf231d5be038be652 100644
--- a/www/js/services/wallet-services.js
+++ b/www/js/services/wallet-services.js
@@ -166,18 +166,21 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
 
           // Send auth event (if need)
           if (needAuth || isAuth()) {
-            api.data.raise.auth();
-
             // Check if need to start/stop auth idle
             checkAuthIdle(true);
-          }
 
-          // Load data
-          if (!data.loaded) {
-            var loadOptions = options && angular.isDefined(options.minData) ? {minData: true} : undefined;
-            return loadData(loadOptions);
+            return api.data.raisePromise.auth(keepAuth ? data : authData);
           }
         }).then(function() {
+          // Load data if need
+          // If user just login, force data full load (even if min data asked)
+          // because the user can wait (after the login modal)
+          var loadOptions = !needLogin && options && options.minData ? {minData: true} : undefined;
+          if (!isDataLoaded(loadOptions)) {
+            return loadData(loadOptions);
+          }
+        })
+        .then(function() {
           if (options && options.silent) {
             UIUtils.loading.hide();
           }
@@ -327,7 +330,7 @@ angular.module('cesium.wallet.services', ['ngApi', 'ngFileSaver', 'cesium.bma.se
           jobs.push(localStorage.put(constants.OLD_STORAGE_KEY, null));
 
           return $q.all(jobs).then(function() {
-            console.debug('[wallet] saved');
+            console.debug('[wallet] Saved locally');
           });
         }
         else {
diff --git a/www/plugins/es/i18n/locale-en-GB.json b/www/plugins/es/i18n/locale-en-GB.json
index 2ae21e5f82814562b8a2bebf8a9f44477b2a2caa..6ab370203e4c7b33cd770104ac7aa620946b7e5c 100644
--- a/www/plugins/es/i18n/locale-en-GB.json
+++ b/www/plugins/es/i18n/locale-en-GB.json
@@ -8,6 +8,7 @@
     "BTN_PUBLISH": "Publish",
     "BTN_PICTURE_DELETE": "Delete",
     "BTN_PICTURE_FAVORISE": "Default",
+    "BTN_PICTURE_ROTATE": "Rotate",
     "BTN_ADD_PICTURE": "Add picture",
     "NOTIFICATIONS": {
       "TITLE": "Notifications",
@@ -49,6 +50,11 @@
       "TITLE": "Ask certifications",
       "HELP": "Select recipients"
     },
+    "SEARCH": {
+      "DIVIDER_PROFILE": "Accounts",
+      "DIVIDER_PAGE": "Pages",
+      "DIVIDER_GROUP": "Groups"
+    },
     "CONFIRM": {
       "SUGGEST_CERTIFICATIONS": "Etes-vous sûr de vouloir <b>envoyer ces suggestions de certification</b> ?",
       "ASK_CERTIFICATION": "Etes-vous sûr de vouloir <b>envoyer une demande de certification</b> ?",
diff --git a/www/plugins/es/i18n/locale-en.json b/www/plugins/es/i18n/locale-en.json
index 2ae21e5f82814562b8a2bebf8a9f44477b2a2caa..6ab370203e4c7b33cd770104ac7aa620946b7e5c 100644
--- a/www/plugins/es/i18n/locale-en.json
+++ b/www/plugins/es/i18n/locale-en.json
@@ -8,6 +8,7 @@
     "BTN_PUBLISH": "Publish",
     "BTN_PICTURE_DELETE": "Delete",
     "BTN_PICTURE_FAVORISE": "Default",
+    "BTN_PICTURE_ROTATE": "Rotate",
     "BTN_ADD_PICTURE": "Add picture",
     "NOTIFICATIONS": {
       "TITLE": "Notifications",
@@ -49,6 +50,11 @@
       "TITLE": "Ask certifications",
       "HELP": "Select recipients"
     },
+    "SEARCH": {
+      "DIVIDER_PROFILE": "Accounts",
+      "DIVIDER_PAGE": "Pages",
+      "DIVIDER_GROUP": "Groups"
+    },
     "CONFIRM": {
       "SUGGEST_CERTIFICATIONS": "Etes-vous sûr de vouloir <b>envoyer ces suggestions de certification</b> ?",
       "ASK_CERTIFICATION": "Etes-vous sûr de vouloir <b>envoyer une demande de certification</b> ?",
diff --git a/www/plugins/es/i18n/locale-fr-FR.json b/www/plugins/es/i18n/locale-fr-FR.json
index f3760e57ccbc9cf44301857dd67fa1f4d725c957..561c254073b76eaaa3a4f53e8177a803d9511cbc 100644
--- a/www/plugins/es/i18n/locale-fr-FR.json
+++ b/www/plugins/es/i18n/locale-fr-FR.json
@@ -237,7 +237,8 @@
     },
     "VIEW": {
       "POPOVER_SHARE_TITLE": "{{title}}",
-      "MENU_TITLE": "Options"
+      "MENU_TITLE": "Options",
+      "REMOVE_CONFIRMATION" : "Êtes-vous sûr de vouloir supprimer ce groupe ?<br/><br/>Cette opération est irréversible."
     },
     "EDIT": {
       "TITLE": "Groupe",
@@ -248,7 +249,11 @@
       "RECORD_DESCRIPTION_HELP": "Description"
     },
     "ERROR": {
-      "SEARCH_GROUPS_FAILED": "Echec de la recherche de groupes"
+      "SEARCH_GROUPS_FAILED": "Echec de la recherche de groupes",
+      "REMOVE_RECORD_FAILED": "Erreur de la suppression du groupe"
+    },
+    "INFO": {
+      "RECORD_REMOVED" : "Group supprimé"
     }
   },
   "REGISTRY": {
diff --git a/www/plugins/es/js/controllers/group-controllers.js b/www/plugins/es/js/controllers/group-controllers.js
index 489a59b1b814b12b55c332810a0f501eed1a92d1..7b3f31c0cbf051d3e40eb567f70419ee27e6b89d 100644
--- a/www/plugins/es/js/controllers/group-controllers.js
+++ b/www/plugins/es/js/controllers/group-controllers.js
@@ -178,7 +178,8 @@ function ESGroupListController($scope, UIUtils, $state, csWallet, esGroup, Modal
 }
 
 
-function ESGroupViewController($scope, $state, $ionicPopover, UIUtils, csConfig, esGroup, csWallet) {
+function ESGroupViewController($scope, $state, $ionicPopover, $ionicHistory, $translate,
+                               UIUtils, csConfig, esGroup, csWallet) {
   'ngInject';
 
   $scope.formData = {};
@@ -233,6 +234,31 @@ function ESGroupViewController($scope, $state, $ionicPopover, UIUtils, csConfig,
     $state.go('app.edit_group', {id: $scope.id});
   };
 
+  $scope.delete = function() {
+    $scope.hideActionsPopover();
+
+    // translate
+    var translations;
+    $translate(['GROUP.VIEW.REMOVE_CONFIRMATION', 'GROUP.INFO.RECORD_REMOVED'])
+      .then(function(res) {
+        translations = res;
+        return UIUtils.alert.confirm(res['GROUP.VIEW.REMOVE_CONFIRMATION']);
+      })
+      .then(function(confirm) {
+        if (confirm) {
+          esGroup.record.remove($scope.id)
+            .then(function () {
+              $ionicHistory.nextViewOptions({
+                historyRoot: true
+              });
+              $state.go('app.groups');
+              UIUtils.toast.show(translations['GROUP.INFO.RECORD_REMOVED']);
+            })
+            .catch(UIUtils.onError('GROUP.ERROR.REMOVE_RECORD_FAILED'));
+        }
+      });
+  };
+
   /* -- modals & popover -- */
 
   $scope.showActionsPopover = function(event) {
diff --git a/www/plugins/es/js/controllers/profile-controllers.js b/www/plugins/es/js/controllers/profile-controllers.js
index 218ddd2f014d85a19d1b724bf6dcde42b934fcf9..1fb02982bb1b469d0892fab18d54ce12ae1310f0 100644
--- a/www/plugins/es/js/controllers/profile-controllers.js
+++ b/www/plugins/es/js/controllers/profile-controllers.js
@@ -197,7 +197,7 @@ function ESViewEditProfileController($scope, $rootScope, $q, $timeout, $state, $
         }
 
         $scope.walletData.profile = angular.copy(formData);
-        $scope.walletData.profile.description = esHttp.util.trustAsHtml(formData.description);
+        $scope.walletData.profile.description = esHttp.util.parseAsHtml(formData.description);
       }
     };
 
diff --git a/www/plugins/es/js/services/comment-services.js b/www/plugins/es/js/services/comment-services.js
index a0268b2bba3f41bf3aad376616348da880bfc27f..7be2621ab1d732dc753be15bdbf829898c71866b 100644
--- a/www/plugins/es/js/services/comment-services.js
+++ b/www/plugins/es/js/services/comment-services.js
@@ -83,7 +83,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
               var comment = data.mapById[hit._id];
               comment.copyFromJson(hit._source);
               // Parse URL and hashtags
-              comment.html = esHttp.util.trustAsHtml(comment.message);
+              comment.html = esHttp.util.parseAsHtml(comment.message);
               delete incompleteCommentIdByParentIds[comment.id];
             });
 
@@ -132,7 +132,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
             data.result = res.hits.hits.reduce(function (result, hit) {
               var comment = new Comment(hit._id, hit._source);
               // Parse URL and hashtags
-              comment.html = esHttp.util.trustAsHtml(comment.message);
+              comment.html = esHttp.util.parseAsHtml(comment.message);
               // fill map by id
               data.mapById[comment.id] = comment;
               return result.concat(comment);
@@ -218,7 +218,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
                   if (comment) {
                     comment.copyFromJson(change._source);
                     // Parse URL and hashtags
-                    comment.html = esHttp.util.trustAsHtml(comment.message);
+                    comment.html = esHttp.util.parseAsHtml(comment.message);
                     exports.raw.refreshTreeLinks(data);
                   }
                   // create (if not in pending comment)
@@ -227,7 +227,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
                     comment.addOnRemoveListener(onRemoveListener);
                     comment.isnew = true;
                     // Parse URL and hashtags
-                    comment.html = esHttp.util.trustAsHtml(comment.message);
+                    comment.html = esHttp.util.parseAsHtml(comment.message);
                     // fill map by id
                     data.mapById[change._id] = comment;
                     exports.raw.refreshTreeLinks(data)
@@ -298,7 +298,7 @@ angular.module('cesium.es.comment.services', ['ngResource', 'cesium.services',
         }
 
         // Parse URL and hashtags
-        entity.html = esHttp.util.trustAsHtml(entity.message);
+        entity.html = esHttp.util.parseAsHtml(entity.message);
 
         // Send add request
         if (!id) {
diff --git a/www/plugins/es/js/services/group-services.js b/www/plugins/es/js/services/group-services.js
index 849dfb9699110bd5141aa2a4d6e22f5ceb64437a..f0a216c18c6eaaa8a020f358bfa447836a74d0b8 100644
--- a/www/plugins/es/js/services/group-services.js
+++ b/www/plugins/es/js/services/group-services.js
@@ -84,7 +84,7 @@ angular.module('cesium.es.group.services', ['cesium.platform', 'cesium.es.http.s
 
     // description
     if (html) {
-      record.description = esHttp.util.trustAsHtml(record.description);
+      record.description = esHttp.util.parseAsHtml(record.description);
     }
 
     // avatar
@@ -260,8 +260,8 @@ angular.module('cesium.es.group.services', ['cesium.platform', 'cesium.es.http.s
       last: getLastGroups,
       search: searchGroups,
       load: loadData,
-      add: esHttp.record.post('/group/record'),
-      update: esHttp.record.post('/group/record/:id/_update'),
+      add: esHttp.record.post('/group/record', {tagFields: ['title', 'description']}),
+      update: esHttp.record.post('/group/record/:id/_update', {tagFields: ['title', 'description']}),
       remove: esHttp.record.remove('group', 'record'),
       fields: {
         commons: fields.commons
diff --git a/www/plugins/es/js/services/http-services.js b/www/plugins/es/js/services/http-services.js
index 51edeb75554d00e5b903dd39e3905b1bd0928766..9f1fa42c706d468a2fcc4d3d2729902e2d496e87 100644
--- a/www/plugins/es/js/services/http-services.js
+++ b/www/plugins/es/js/services/http-services.js
@@ -193,8 +193,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       return text.replace(/</g, '&lt;').replace(/>/g, '&gt;');
     }
 
-    function trustAsHtml(text, options) {
-
+    function parseAsHtml(text, options) {
 
       var content = text ? escape(text.trim()) : undefined;
       if (content) {
@@ -208,6 +207,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
         // Replace URL in description
         var urls = parseUrlsFromText(content);
         _.forEach(urls, function(url){
+          // Redirect URL to the function 'openLink', to open a new window if need (e.g. desktop app)
           var link = '<a ng-click=\"openLink($event, \'{0}\')\">{1}</a>'.format(url, url);
           content = content.replace(url, link);
         });
@@ -225,8 +225,6 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
           var link = '<a ui-sref=\"{0}({uid: \'{1}\'})\">@{2}</a>'.format(options.uidState, uid, uid);
           content = content.replace('@'+uid, link);
         });
-
-        //$sce.trustAsHtml(content);
       }
       return content;
     }
@@ -234,10 +232,11 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
     function fillRecordTags(record, fieldNames) {
       fieldNames = fieldNames || ['title', 'description'];
 
-      _.forEach(fieldNames, function(fieldName) {
+      record.tags = fieldNames.reduce(function(res, fieldName) {
         var value = record[fieldName];
-        record.tags = parseTagsFromText(value);
-      });
+        var tags = value && parseTagsFromText(value);
+        return tags ? res.concat(tags) : res;
+      }, []);
     }
 
     function postRecord(path, options) {
@@ -261,7 +260,9 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
             obj.issuer = walletData.pubkey;
 
             // Fill tags
-            fillRecordTags(obj);
+            if (options.tagFields) {
+              fillRecordTags(obj, options.tagFields);
+            }
 
             var str = JSON.stringify(obj);
 
@@ -421,7 +422,7 @@ angular.module('cesium.es.http.services', ['ngResource', 'ngApi', 'cesium.servic
       },
       util: {
         parseTags: parseTagsFromText,
-        trustAsHtml: trustAsHtml
+        parseAsHtml: parseAsHtml
       },
       constants: constants
     };
diff --git a/www/plugins/es/js/services/message-services.js b/www/plugins/es/js/services/message-services.js
index fd2f24bc48613099c329c1f3d305f6b297a48fed..ea0b88247104758e9d6af4a31b79f0a76188662c 100644
--- a/www/plugins/es/js/services/message-services.js
+++ b/www/plugins/es/js/services/message-services.js
@@ -324,7 +324,7 @@ angular.module('cesium.es.message.services', ['ngResource', 'cesium.platform',
                   fillSummary(message);
                 }
                 else if (content){
-                  message.html = esHttp.util.trustAsHtml(content);
+                  message.html = esHttp.util.parseAsHtml(content);
                 }
               })
               .catch(function(err){
diff --git a/www/plugins/es/js/services/profile-services.js b/www/plugins/es/js/services/profile-services.js
index cc70f076148f3fad99cb83358d6276709762b2a7..744f6787c9ac804b0214c9f9e68b0956a0bfeb25 100644
--- a/www/plugins/es/js/services/profile-services.js
+++ b/www/plugins/es/js/services/profile-services.js
@@ -71,7 +71,7 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
 
           // description
           if (!options.raw) {
-            profile.description = esHttp.util.trustAsHtml(profile.source.description);
+            profile.description = esHttp.util.parseAsHtml(profile.source.description);
           }
 
           // Social url must be unique in socials links - Workaround for issue #306:
@@ -125,6 +125,44 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
     }
   }
 
+  function onWalletLogin(data, deferred) {
+    deferred = deferred || $q.defer();
+    if (!data || !data.pubkey || !data.keypair) {
+      deferred.resolve();
+      return deferred.promise;
+    }
+
+    // Waiting to load crypto libs
+    if (!CryptoUtils.isLoaded()) {
+      console.debug('[ES] [wallet] Waiting crypto lib loading...');
+      return $timeout(function() {
+        return onWalletLogin(data, deferred);
+      }, 50);
+    }
+
+    console.debug('[ES] [wallet] Loading user avatar+name...');
+    var now = new Date().getTime();
+
+    esProfile.getAvatarAndName(data.pubkey)
+      .then(function(profile) {
+        if (profile) {
+          data.name = profile.name;
+          data.avatarStyle = profile.avatarStyle;
+          data.avatar = profile.avatar;
+          console.debug('[ES] [profile] Loaded user avatar+name in '+ (new Date().getTime()-now) +'ms');
+        }
+        else {
+          console.debug('[ES] [profil] No user avatar+name found');
+        }
+        deferred.resolve(data);
+      })
+      .catch(function(err){
+        deferred.reject(err);
+      });
+
+    return deferred.promise;
+  }
+
   function onWotSearch(text, datas, pubkeyAtributeName, deferred) {
     deferred = deferred || $q.defer();
     if (!text && (!datas || !datas.length)) {
@@ -144,9 +182,10 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
       _source: ["title", "avatar._content_type"]
     };
 
-    var mixedSearch = esSettings.wot.isMixedSearchEnable();
+    var mixedSearch = text && esSettings.wot.isMixedSearchEnable();
     if (mixedSearch) {
       request._source = request._source.concat(["description", "thumbnail._content_type", "city", "creationTime", "membersCount"]);
+      console.debug("[ES] [profile] Mixed search: enable");
     }
 
     if (datas.length > 0) {
@@ -373,8 +412,8 @@ angular.module('cesium.es.profile.services', ['cesium.services', 'cesium.es.http
   return {
     getAvatarAndName: getAvatarAndName,
     get: getProfile,
-    add: esHttp.record.post('/user/profile'),
-    update: esHttp.record.post('/user/profile/:id/_update'),
+    add: esHttp.record.post('/user/profile', {tagFields: ['title', 'description']}),
+    update: esHttp.record.post('/user/profile/:id/_update', {tagFields: ['title', 'description']}),
     avatar: esHttp.get('/user/profile/:id?_source=avatar'),
     fillAvatars: fillAvatars
   };
diff --git a/www/plugins/es/js/services/registry-services.js b/www/plugins/es/js/services/registry-services.js
index 8acd1f5baf0c2a5058d4960d8574484eabb9b541..0e00954cb0b18671b19f24ff34dc503ee481cce8 100644
--- a/www/plugins/es/js/services/registry-services.js
+++ b/www/plugins/es/js/services/registry-services.js
@@ -148,7 +148,7 @@ angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services',
 
         // parse description as Html
         if (!options.raw) {
-          record.description = esHttp.util.trustAsHtml(record.description);
+          record.description = esHttp.util.parseAsHtml(record.description);
         }
 
         // Load issuer (avatar, name, uid, etc.)
@@ -170,8 +170,8 @@ angular.module('cesium.es.registry.services', ['ngResource', 'cesium.services',
     exports.record = {
         search: search,
         load: loadData,
-        add: esHttp.record.post('/page/record'),
-        update: esHttp.record.post('/page/record/:id/_update'),
+        add: esHttp.record.post('/page/record', {tagFields: ['title', 'description']}),
+        update: esHttp.record.post('/page/record/:id/_update', {tagFields: ['title', 'description']}),
         remove: esHttp.record.remove('registry', 'record'),
         fields: {
           commons: fields.commons
diff --git a/www/plugins/es/js/services/settings-services.js b/www/plugins/es/js/services/settings-services.js
index 703720db657897f482071e2b3398793be6b38c2e..98462476afbca2bec9206206b99f7bb8eeb5e0e9 100644
--- a/www/plugins/es/js/services/settings-services.js
+++ b/www/plugins/es/js/services/settings-services.js
@@ -107,29 +107,25 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
   }
 
   // Load settings
-  function loadSettings(pubkey, keypair) {
+  function loadSettings(pubkey, boxKeypair) {
     var now = new Date().getTime();
-    return $q.all([
-        CryptoUtils.box.keypair.fromSignKeypair(keypair),
-        that.get({id: pubkey})
-          .catch(function(err){
-            if (err && err.ucode && err.ucode == 404) {
-              return null; // not found
-            }
-            else {
-              throw err;
-            }
-          })])
+    return that.get({id: pubkey})
+        .catch(function(err){
+          if (err && err.ucode && err.ucode == 404) {
+            return null; // not found
+          }
+          else {
+            throw err;
+          }
+        })
       .then(function(res) {
-        var boxKeypair = res[0];
-        res = res[1];
         if (!res || !res._source) {
           return;
         }
         var record = res._source;
         // Do not apply if same version
         if (record.time === csSettings.data.time) {
-          console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms (no update need)');
+          console.debug('[ES] [settings] Loaded in '+ (new Date().getTime()-now) +'ms, but already up to date');
           return;
         }
         var nonce = CryptoUtils.util.decode_base58(record.nonce);
@@ -138,13 +134,12 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
           .then(function(json) {
             var settings = JSON.parse(json || '{}');
             settings.time = record.time;
-            console.debug('[ES] [settings] Loaded user settings in '+ (new Date().getTime()-now) +'ms');
-            console.debug(settings);
+            console.debug('[ES] [settings] Loaded and decrypted in '+ (new Date().getTime()-now) +'ms');
             return settings;
           })
           // if error: skip stored content
           .catch(function(err){
-            console.error('[ES] [settings] Could not read stored settings: ' + (err && err.message || 'decryption error'));
+            console.error('[ES] [settings] Could not load remote settings: ' + (err && err.message || 'decryption error'));
             // make sure to remove time, to be able to save it again
             delete csSettings.data.time;
             return null;
@@ -159,21 +154,13 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
     return deferred.promise;
   }
 
-  function onWalletLogin(data, deferred) {
+  function onWalletAuth(data, deferred) {
     deferred = deferred || $q.defer();
-    if (!data || !data.pubkey || !data.keypair || !data.keypair.signSk) {
+    if (!data || !data.pubkey || !data.keypair || !data.keypair.signSk || !data.keypair.boxSk) {
       deferred.resolve();
       return deferred.promise;
     }
 
-    // Waiting to load crypto libs
-    if (!CryptoUtils.isLoaded()) {
-      console.debug('[ES] [settings] Waiting crypto lib loading...');
-      return $timeout(function() {
-        return onWalletLogin(data, deferred);
-      }, 50);
-    }
-
     console.debug('[ES] [settings] Loading user settings...');
 
     // Load settings
@@ -185,7 +172,7 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
         // Remember for comparison
         previousRemoteData = settings;
 
-        console.debug('[ES] [settings] Successfully load settings from ES');
+        console.debug('[ES] [settings] Applied');
         return storeSettingsLocally();
       })
     .then(function() {
@@ -211,7 +198,7 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
     if (csWallet.isAuth()) {
       if (!wasEnable && isEnable) {
 
-        onWalletLogin(csWallet.data);
+        onWalletAuth(csWallet.data);
       }
       else {
         storeSettingsRemotely(data);
@@ -233,26 +220,16 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
   }
 
   function storeSettingsRemotely(data) {
-    if (!csWallet.isLogin()) return $q.when();
-
     var filteredData = copyUsingSpec(data, SETTINGS_SAVE_SPEC);
     if (previousRemoteData && angular.equals(filteredData, previousRemoteData)) {
       return $q.when();
     }
 
-    // Waiting to load crypto libs
-    if (!CryptoUtils.isLoaded()) {
-      console.debug('[ES] [settings] Waiting crypto lib loading...');
-      return $timeout(function() {
-        return storeSettingsRemotely();
-      }, 50);
-    }
-
-    var time = esHttp.date.now();
+    var time = esHttp.date.now(); // always update time
     console.debug('[ES] [settings] Saving user settings... at time ' + time);
 
     return $q.all([
-        CryptoUtils.box.keypair.fromSignKeypair(csWallet.data.keypair),
+        csWallet.getKeypair(), // same result as esWallet.box.getKeypair(), because box keypair computed on auth
         CryptoUtils.util.random_nonce()
       ])
       .then(function(res) {
@@ -303,7 +280,7 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
     // Extend csWallet.login()
     listeners = [
       csSettings.api.data.on.reset($rootScope, onSettingsReset, this),
-      csWallet.api.data.on.login($rootScope, onWalletLogin, this)
+      csWallet.api.data.on.auth($rootScope, onWalletAuth, this)
     ];
   }
 
@@ -335,8 +312,8 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
             console.debug("[ES] [settings] Enable");
             addListeners();
 
-            if (csWallet.isLogin()) {
-              return onWalletLogin(csWallet.data)
+            if (csWallet.isAuth()) {
+              return onWalletAuth(csWallet.data)
                 .then(function() {
                   // Emit event
                   api.state.raise.changed(enable);
diff --git a/www/plugins/es/js/services/wallet-services.js b/www/plugins/es/js/services/wallet-services.js
index c881c8360b151e02332bcf9779cba6171935da8b..9d92c52ae592e60c714fd9561044c703b751decc 100644
--- a/www/plugins/es/js/services/wallet-services.js
+++ b/www/plugins/es/js/services/wallet-services.js
@@ -18,6 +18,20 @@ angular.module('cesium.es.wallet.services', ['ngResource', 'cesium.platform', 'c
       }
     }
 
+    function onWalletAuth(data, deferred) {
+      deferred = deferred || $q.defer();
+
+      // Generate box keypair
+      esCrypto.box.getKeypair(data.keypair)
+        .then(function(res) {
+          csWallet.data.keypair.boxSk = res.boxSk;
+          csWallet.data.keypair.boxPk = res.boxPk;
+          console.debug("[ES] [wallet] Box keypair successfully computed");
+          deferred.resolve();
+        });
+      return deferred.promise;
+    }
+
     function onWalletUnauth(data) {
       data = data || csWallet.data;
       if (data.keypair) {
@@ -95,11 +109,11 @@ angular.module('cesium.es.wallet.services', ['ngResource', 'cesium.platform', 'c
     }
 
     function getBoxKeypair() {
-      if (!csWallet.isLogin()) {
-        throw new Error('Unable to get box keypair: user not connected !');
+      if (!csWallet.isAuth()) {
+        throw new Error('Unable to get box keypair: user not authenticated !');
       }
 
-      return csWallet.getKeypair()
+      return csWallet.getKeypair({silent: true})
         .then(function(keypair) {
           if (keypair && keypair.boxPk && keypair.boxSk) {
             return $q.when(csWallet.data.keypair);
@@ -109,7 +123,7 @@ angular.module('cesium.es.wallet.services', ['ngResource', 'cesium.platform', 'c
         .then(function(res) {
           csWallet.data.keypair.boxSk = res.boxSk;
           csWallet.data.keypair.boxPk = res.boxPk;
-          console.debug("[ES] [wallet] Secret box keypair successfully computed");
+          console.debug("[ES] [wallet] Box keypair successfully computed");
           return csWallet.data.keypair;
         });
     }
@@ -121,7 +135,8 @@ angular.module('cesium.es.wallet.services', ['ngResource', 'cesium.platform', 'c
         csWallet.api.data.on.load($rootScope, onWalletLoad, this),
         csWallet.api.data.on.init($rootScope, onWalletReset, this),
         csWallet.api.data.on.reset($rootScope, onWalletReset, this),
-        csWallet.api.data.on.unauth($rootScope, onWalletUnauth, this)
+        csWallet.api.data.on.unauth($rootScope, onWalletUnauth, this),
+        csWallet.api.data.on.auth($rootScope, onWalletAuth, this)
       ];
     }
 
diff --git a/www/plugins/graph/js/controllers/synchro-controllers.js b/www/plugins/graph/js/controllers/synchro-controllers.js
index f41782fd61a67a0d46bdddd7061ffccfeed93e37..b770cef3f15449c7bd9fabd9bbcf0dd6c023f4c2 100644
--- a/www/plugins/graph/js/controllers/synchro-controllers.js
+++ b/www/plugins/graph/js/controllers/synchro-controllers.js
@@ -160,7 +160,6 @@ function GpSynchroController($scope, $controller, $q, $translate, gpColor, gpDat
       };
 
       result = result[1];
-      console.log("TODO", result);
       if (!result || !result.times) return; // no data
       $scope.times = result.times;
 
diff --git a/www/plugins/map/js/services/wot-services.js b/www/plugins/map/js/services/wot-services.js
index 02aab8ad5292ccff59b512c95dd3034bc611d327..81edba0e90a3e54ae9aef6701cbf31d723211637 100644
--- a/www/plugins/map/js/services/wot-services.js
+++ b/www/plugins/map/js/services/wot-services.js
@@ -174,7 +174,7 @@ angular.module('cesium.map.wot.services', ['cesium.services'])
       }
 
       // Description
-      item.description = hit._source.description && esHttp.util.trustAsHtml(hit._source.description);
+      item.description = hit._source.description && esHttp.util.parseAsHtml(hit._source.description);
 
       return item.geoPoint ? res.concat(item) : res;
     }, []);
diff --git a/www/templates/login/form_scrypt.html b/www/templates/login/form_scrypt.html
index a752945ef179073b79bb8577fd04052365e70136..752b848286af61b01d4d26de4333e271a269f43d 100644
--- a/www/templates/login/form_scrypt.html
+++ b/www/templates/login/form_scrypt.html
@@ -57,13 +57,17 @@
     </div>
     <a class="button button-icon positive button-small-padding icon ion-ios-help-outline animate-show-hide "
        ng-click="showHelpModal('login-pubkey')"
+       ng-if="!expectedPubkey"
        ng-show="showPubkey">
     </a>
+    <span class="button button-icon balanced button-small-padding icon ion-checkmark animate-show-hide"
+       ng-if="expectedPubkey"
+       ng-show="showPubkey && !computing && !pubkeyError">
+    </span>
   </div>
-  <div class="form-errors"
-       ng-show="pubkeyError">
-    <div class="form-error" >
-      <span translate="ERROR.AUTH_INVALID_PUBKEY"></span>
+  <div class="form-errors" ng-if="expectedPubkey">
+    <div class="form-error" ng-show="pubkeyError">
+      <span trust-as-html="::'ERROR.AUTH_INVALID_PUBKEY'|translate:{pubkey: expectedPubkey}"></span>
     </div>
   </div>