diff --git a/app/config.json b/app/config.json
index 216de473fe58cdd5ad2c663167750d7511bcdb64..af183ae1715c9c8fe04ceaf42b3da88266c0101a 100644
--- a/app/config.json
+++ b/app/config.json
@@ -9,7 +9,7 @@
     "timeout": 30000,
     "timeWarningExpireMembership": 5184000,
     "timeWarningExpire": 7776000,
-    "minPeerCountAtStartup": 10,
+    "minConsensusPeerCount": 10,
     "keepAuthIdle": 600,
     "useLocalStorage": true,
     "useRelative": false,
@@ -138,7 +138,7 @@
     "timeout": 30000,
     "timeWarningExpireMembership": 5184000,
     "timeWarningExpire": 7776000,
-    "minPeerCountAtStartup": 2,
+    "minConsensusPeerCount": -1,
     "useLocalStorage": true,
     "useRelative": false,
     "expertMode": true,
diff --git a/www/js/config-test.js b/www/js/config-test.js
index 8576b66a435d531c7ccf20cf7b4c40c8293363f0..d8aa2c45957c43b449133e60353d8bbe1db4afb4 100644
--- a/www/js/config-test.js
+++ b/www/js/config-test.js
@@ -18,7 +18,7 @@ angular.module("cesium.config", [])
 	"timeout": 30000,
 	"timeWarningExpireMembership": 5184000,
 	"timeWarningExpire": 7776000,
-	"minPeerCountAtStartup": 2,
+	"minConsensusPeerCount": 2,
 	"useLocalStorage": true,
 	"useRelative": false,
 	"expertMode": true,
@@ -80,9 +80,9 @@ angular.module("cesium.config", [])
 			"defaultCountry": "France"
 		}
 	},
-	"version": "1.7.7",
-	"build": "2023-08-17T13:11:36.358Z",
+	"version": "1.7.8",
+	"build": "2023-08-17T17:00:38.095Z",
 	"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
 })
 
-;
\ No newline at end of file
+;
diff --git a/www/js/config.js b/www/js/config.js
index 97b7d307c1204a8cb4f7429bb723dbe6675ad08a..c4b79c10ba1f8ca486387b1045e7831f444e809a 100644
--- a/www/js/config.js
+++ b/www/js/config.js
@@ -18,7 +18,7 @@ angular.module("cesium.config", [])
 	"timeout": 30000,
 	"timeWarningExpireMembership": 5184000,
 	"timeWarningExpire": 7776000,
-	"minPeerCountAtStartup": 10,
+	"minConsensusPeerCount": 10,
 	"keepAuthIdle": 600,
 	"useLocalStorage": true,
 	"useRelative": false,
@@ -152,4 +152,4 @@ angular.module("cesium.config", [])
 	"newIssueUrl": "https://git.duniter.org/clients/cesium-grp/cesium/issues/new"
 })
 
-;
\ No newline at end of file
+;
diff --git a/www/js/controllers/app-controllers.js b/www/js/controllers/app-controllers.js
index 2a58c669214f50b617d492cb8d4322974cf8ab58..87508125545f1ba216396cac2ebb1ccc88a1f638 100644
--- a/www/js/controllers/app-controllers.js
+++ b/www/js/controllers/app-controllers.js
@@ -403,8 +403,11 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
     console.info('[app] Trying to parse as uri: ', uri);
     var fromHomeState = $state.current && $state.current.name === 'app.home';
 
-    // Parse the URI
-    return BMA.uri.parse(uri)
+    return (!csPlatform.isStarted() ? csPlatform.ready() : $q.when())
+      // Parse the URI
+      .then(function() {
+        return BMA.uri.parse(uri);
+      })
       .then(function(res) {
         if (!res) throw {message: 'ERROR.UNKNOWN_URI_FORMAT'}; // Continue
 
@@ -469,7 +472,7 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
           reject(err);
           return;
         }
-        console.error("[home] Error while handle uri {" + uri + "': ", err);
+        console.error("[home] Error while handle uri '{0}'".format(uri), JSON.stringify(err));
         return UIUtils.onError(uri)(err);
       });
   };
diff --git a/www/js/controllers/network-controllers.js b/www/js/controllers/network-controllers.js
index df86a0141c37f46e45c3d4ce990551e4269f090e..3ec5405591c3c875c95962481eff5c7cbd4eeec1 100644
--- a/www/js/controllers/network-controllers.js
+++ b/www/js/controllers/network-controllers.js
@@ -48,7 +48,7 @@ angular.module('cesium.network.controllers', ['cesium.services'])
 ;
 
 function NetworkLookupController($scope,  $state, $location, $ionicPopover, $window, $translate,
-                                 BMA, UIUtils, csConfig, csSettings, csCurrency, csNetwork, csWot) {
+                                 BMA, Device, UIUtils, csConfig, csSettings, csCurrency, csNetwork, csWot) {
   'ngInject';
 
   $scope.networkStarted = false;
@@ -149,7 +149,8 @@ function NetworkLookupController($scope,  $state, $location, $ionicPopover, $win
         asc : $scope.search.asc
       },
       expertMode: $scope.expertMode,
-      timeout: angular.isDefined($scope.timeout) ? $scope.timeout : Device.network.timeout()
+      timeout: angular.isDefined($scope.timeout) ? $scope.timeout : Device.network.timeout(),
+      withSandboxes: $scope.expertMode && Device.isDesktop() // SKip sandboxes if mobile
     };
     return options;
   };
diff --git a/www/js/platform.js b/www/js/platform.js
index b31f7a23bb65b71574d7e7a3a8cd243885a9091e..89d3d76794cc2f601dc569daa7915e77bbce30b1 100644
--- a/www/js/platform.js
+++ b/www/js/platform.js
@@ -194,19 +194,19 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
       if (!alive) return false;
       var now = Date.now();
 
-      console.info("[platform] Checking peer [{0}] is well synchronized...".format(BMA.server));
+      console.info('[platform] Checking peer [{0}] is well synchronized...'.format(BMA.server));
       api.start.raise.message('NETWORK.INFO.ANALYZING_NETWORK');
 
       var askUserConfirmation = csSettings.data.expertMode;
-      var minConsensusPeerCount = csSettings.data.minPeerCountAtStartup || -1;
+      var minConsensusPeerCount = csSettings.data.minConsensusPeerCount || -1;
 
-      return csNetwork.getSynchronizedBmaPeers(BMA)
+      return csNetwork.getSynchronizedBmaPeers(BMA, {autoRefresh: false})
         .then(function(peers) {
 
           var consensusBlockNumber = peers.length ? peers[0].currentNumber : undefined;
           var consensusPeerCount = peers.length;
 
-          // Not enough synchronized peers found (e.g. an isolated peer). Should never occur.
+          // Not enough peers on main consensus (e.g. an isolated peer). Should never occur.
           if (!consensusPeerCount || (minConsensusPeerCount > 0 && consensusPeerCount < minConsensusPeerCount)) {
             console.warn("[platform] Not enough BMA peers on the main consensus block: {0} found. Will peek another peer...".format(consensusPeerCount));
             // Retry using another fallback peer
@@ -214,26 +214,25 @@ angular.module('cesium.platform', ['ngIdle', 'cesium.config', 'cesium.services']
               .then(checkBmaNodeSynchronized); // Loop
           }
 
-          // Filter compatible peers
-          peers = peers && peers.reduce(function(res, peer) {
+          // Filter on compatible peers
+          peers = peers.reduce(function(res, peer) {
             if (!peer.compatible) return res;
             // Serialize to JSON, then append
             return res.concat(peer.toJSON());
           }, []);
 
-          console.info("[platform] Found {0}/{1} BMA peers, synchronized and compatible, in {2}ms".format(peers.length, consensusPeerCount, Date.now() - now));
+          console.info("[platform] Keep {0}/{1} BMA peers, synchronized and compatible, in {2}ms".format(peers.length, consensusPeerCount, Date.now() - now));
 
           // Try to find the current peer in synchronized peers
           var synchronized = false;
-          peers = peers && _.filter(peers, function(peer) {
+          peers = _.filter(peers, function(peer) {
             if (BMA.url !== peer.url) return true;
             synchronized = true;
             return false;
           });
 
-          // Saving other peers to settings
-          console.debug("[platform] Saving {0} BMA peers in settings, for a later use".format(peers.length));
-          csSettings.data.network.peers = peers;
+          // Saving others peers to settings
+          csSettings.savePeers(peers);
 
           // OK (current BMA node is sync and compatible): continue
           if (synchronized) {
diff --git a/www/js/services/bma-services.js b/www/js/services/bma-services.js
index 566fee59b763eb38e7e3145d85ac1d11116eeb94..a09fc57b42e0ff9976c2a2ecb6613f63d6e69d87 100644
--- a/www/js/services/bma-services.js
+++ b/www/js/services/bma-services.js
@@ -1118,15 +1118,21 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
           var matches = exports.regexp.PUBKEY_WITH_CHECKSUM.exec(result.pubkey);
           pubkey = matches[1];
           var checksum = matches[2];
-          var expectedChecksum = csCrypto.util.pkChecksum(pubkey);
-          if (checksum !== expectedChecksum) {
-            console.warn("[BMA.parse] Detecting a pubkey {{0}} with checksum {{1}}, but expecting checksum is {{2}}".format(
-              pubkey, checksum, expectedChecksum
-            ));
-            throw {message: 'ERROR.PUBKEY_INVALID_CHECKSUM'};
-          }
-          result.pubkey = pubkey;
-          result.pubkeyChecksum = checksum;
+          return csCrypto.ready()
+            .then(function() {
+              return csCrypto.util.pkChecksum(pubkey);
+            })
+            .then(function(expectedChecksum){
+              if (checksum !== expectedChecksum) {
+                console.warn("[BMA.parse] Detecting a pubkey {{0}} with checksum {{1}}, but expecting checksum is {{2}}".format(
+                  pubkey, checksum, expectedChecksum
+                ));
+                throw {message: 'ERROR.PUBKEY_INVALID_CHECKSUM'};
+              }
+              result.pubkey = pubkey;
+              result.pubkeyChecksum = checksum;
+              return result;
+            });
         }
         return result;
       });
@@ -1221,7 +1227,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
       url: csHttp.getUrl(host, port, path, useSsl),
       node: {
         summary: csHttp.getWithCache(host, port, path + '/node/summary', useSsl, csCache.constants.MEDIUM, false/*autoRefresh*/, timeout),
-        sandboxes: csHttp.get(host, port, path + '/node/sandboxes', useSsl, Math.max(1000, timeout)), // sandboxes request can be long
+        sandboxes: csHttp.get(host, port, path + '/node/sandboxes', useSsl, timeout ? Math.max(timeout * 2, 3000) : undefined), // /!\ sandboxes request can be long
       },
       network: {
         peering: {
diff --git a/www/js/services/device-services.js b/www/js/services/device-services.js
index ab1536ba3cc16dc880053cc66376ca2db96b0652..25d1b13ed055d586cff190ce975120769f9dee5a 100644
--- a/www/js/services/device-services.js
+++ b/www/js/services/device-services.js
@@ -150,45 +150,61 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
       }
     };
     exports.network = {
+      constants: {
+        NONE: 'none',
+        CELL: 'cellular',
+        CELL_2G: '2g',
+        CELL_3G: '3g',
+        CELL_4G: '4g',
+        CELL_5G: '5g',
+        ETHERNET: 'ethernet',
+        WIFI: 'wifi',
+        UNKOWN: 'unknown',
+      },
       connectionType: function () {
+        var type;
         try {
           // If mobile: use the Cordova network plugin
           if (exports.network.enable) {
-            return navigator.connection.type || 'unknown';
+            type = navigator.connection.type || Connection.CELL_2G;
+            console.debug('[device] Network plugin connection type: {0}'.format(type));
+            return type;
           }
 
           // Continue using browser API
           if (navigator.onLine === false) {
-            return 'none';
+            return exports.network.constants.NONE;
           }
 
           var connection = navigator.connection;
-          var connectionType = connection && connection.effectiveType || 'unknown';
+          type = connection && connection.effectiveType || exports.network.constants.UNKNOWN;
 
           // Si la vitesse de liaison descendante est de 0 mais que le type est '4g', cela signifie probablement que nous sommes hors ligne
           if (connection && connection.downlink === 0) {
             //console.debug('[device] Navigator connection type: none (downlink=0)');
-            return 'none';
+            return exports.network.constants.NONE;
           }
           else {
-            //console.debug('[device] Navigator connection type: ' + connectionType);
-            switch (connectionType) {
+            //console.debug('[device] Navigator connection type: ' + type);
+            switch (type) {
               case 'slow-2g':
               case '2g':
-                return 'cell_2g';
+                return exports.network.constants.CELL_2G;
               case '3g':
-                return 'cell_3g';
+                return exports.network.constants.CELL_3G;
               case '4g':
-                if (exports.isDesktop()) return 'ethernet';
-                return 'cell_4g';
+                if (exports.isDesktop()) return exports.network.constants.ETHERNET;
+                return exports.network.constants.CELL_4G;
               case '5g':
-                if (exports.isDesktop()) return 'ethernet';
-                return 'cell_5g';
+                if (exports.isDesktop()) return exports.network.constants.ETHERNET;
+                return exports.network.constants.CELL_5G;
               case 'unknown':
-                if (exports.isDesktop()) return 'ethernet';
-                return 'unknown';
+                if (exports.isDesktop()) return exports.network.constants.ETHERNET;
+                return exports.network.constants.UNKNOWN;
+              case 'none':
+                return exports.network.constants.NONE;
               default:
-                return connectionType;
+                return type;
             }
           }
 
@@ -228,30 +244,25 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
           var connectionType = exports.network.connectionType();
 
           switch (connectionType) {
-            case 'ethernet':
-              timeout = 1000; // 1 s
-              break;
-            case 'wifi':
-              timeout = 2000;
-              break;
-            case 'cell_5g':
-              timeout = 3000;
-              break;
-            case 'cell': // (e.g. iOS)
-            case 'cell_4g':
+            case exports.network.constants.ETHERNET:
+            case exports.network.constants.WIFI:
+            case exports.network.constants.CELL: // (e.g. iOS)
+            case exports.network.constants.CELL_4G:
+            case exports.network.constants.CELL_5G:
               timeout = 4000; // 4s
               break;
-            case 'cell_3g':
+            case exports.network.constants.CELL_3G:
               timeout = 5000; // 5s
               break;
-            case 'cell_2g':
+            case exports.network.constants.CELL_2G:
               timeout = 10000; // 10s
               break;
-            case 'none':
+            case exports.network.constants.NONE:
               timeout = 0;
               break;
-            case 'unknown':
+            case exports.network.constants.UNKNOWN:
             default:
+              console.warn('[device] Fallback to default timeout ({0}ms) - connectionType: {1}'.format(defaultTimeout, connectionType));
               timeout = defaultTimeout;
               break;
           }
@@ -452,7 +463,7 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
             var paths = (modelPath || '').split('.');
             var property = paths.length && paths[paths.length - 1];
             paths.reduce(function (res, path) {
-              if (path == property) {
+              if (path === property) {
                 res[property] = value;
                 return;
               }
@@ -518,7 +529,6 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
     exports.isDesktop = function () {
       if (!angular.isDefined(cache.isDesktop)) {
         try {
-
           cache.isDesktop = !exports.enable && (
             exports.isUbuntu() ||
             exports.isWindows() ||
@@ -581,22 +591,45 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
               window.open = cordova.InAppBrowser.open;
             }
 
-            // Add network listeners
+            // Add network listeners, using cordova network plugin
             if (exports.network.enable) {
+              // Override constants, because it depends on OS version
+              exports.network.constants.NONE = Connection.NONE;
+              exports.network.constants.CELL = Connection.CELL;
+              exports.network.constants.CELL_2G = Connection.CELL_2G;
+              exports.network.constants.CELL_3G = Connection.CELL_3G;
+              exports.network.constants.CELL_4G = Connection.CELL_4G;
+              exports.network.constants.WIFI = Connection.WIFI;
+              exports.network.constants.ETHERNET = Connection.ETHERNET;
+              exports.network.constants.UNKNOWN = Connection.UNKNOWN;
+
+
+              var previousConnectionType;
               document.addEventListener('offline', function () {
                 console.info('[device] Network is offline');
                 api.network.raise.offline();
+                previousConnectionType = 'none';
               }, false);
               document.addEventListener('online', function () {
                 console.info('[device] Network is online');
-                api.network.raise.online();
+                if (!previousConnectionType || previousConnectionType === 'none') {
+                  api.network.raise.online();
+                  previousConnectionType = exports.network.connectionType();
+                }
+                else {
+                  var connectionType = exports.network.connectionType();
+                  if (connectionType !== previousConnectionType) {
+                    console.info('[device] Network connection type changed: ' + connectionType);
+                    api.network.raise.changed(connectionType);
+                  }
+                }
               }, false);
             }
 
           } else {
             console.debug('[device] Ionic platform ready - no device detected.');
 
-            // Add network listeners
+            // Add network listeners, using browser events
             window.addEventListener('offline', function () {
               console.info('[device] Network is offline');
               api.network.raise.offline();
diff --git a/www/js/services/network-services.js b/www/js/services/network-services.js
index 501dba84cbe508f18d4efd8dec173a227afaa9dd..b440e660ab126db98dbfd05ccc1d034500eb5e3d 100644
--- a/www/js/services/network-services.js
+++ b/www/js/services/network-services.js
@@ -43,8 +43,11 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       searchingPeersOnNetwork: false,
       difficulties: null,
       ws2pHeads: null,
-      timeout: csConfig.timeout,
-      startTime: null
+      timeout: csConfig.timeout, // Max timeout
+      startTime: null,
+      autoRefresh: true,
+      flushIntervalMs: 1000,
+      withSandboxes: null
     },
 
     // Return the block uid
@@ -91,14 +94,16 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       data.searchingPeersOnNetwork = false;
       data.difficulties = null;
       data.ws2pHeads = null;
-      data.timeout = data.timeout || getDefaultTimeout();
+      data.timeout = data.timeout || getDeviceTimeout(); // Keep it is already set
       data.startTime = null;
+      data.autoRefresh = true;
+      data.flushIntervalMs = null;
     },
 
    /**
     * Compute a timeout, depending on connection type (wifi, ethernet, cell, etc.)
     */
-   getDefaultTimeout = function () {
+   getDeviceTimeout = function () {
      // Using timeout from settings
      if (csSettings.data.expertMode && csSettings.data.timeout > 0) {
        console.debug('[network] Using user defined timeout: {0}ms'.format(csSettings.data.timeout));
@@ -148,7 +153,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
             return $timeout(function() {
               if (remainingTime() > 0) return loadW2spHeads();
-            }, 3000);
+            }, BMA.constants.LIMIT_REQUEST_DELAY);
           }
           console.error(err); // can occur on duniter v1.6
           data.ws2pHeads = {};
@@ -166,10 +171,10 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
         })
         .catch(function(err) {
           // When too many request, retry in 3s
-          if (err && err.ucode == BMA.errorCodes.HTTP_LIMITATION) {
+          if (err && err.ucode === BMA.errorCodes.HTTP_LIMITATION) {
             return $timeout(function() {
               if (remainingTime() > 0) return loadDifficulties();
-            }, 3000);
+            }, BMA.constants.LIMIT_REQUEST_DELAY);
           }
           console.error(err);
           data.difficulties = {};
@@ -201,8 +206,9 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           sortPeers(true/*update main buid*/);
 
           console.debug('[network] [#{0}] {1} peer(s) found.'.format(pid, data.peers.length));
+
         }
-      }, 1000);
+      }, data.flushIntervalMs || 1000);
 
       var initJobs = [
         // Load uids
@@ -545,7 +551,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
         return $q.when(peer);
       }
 
-      var timeout = Math.max(data.timeout / 2, remainingTime()); // >= 500ms
+      var timeout = Math.max(1000, remainingTime()); // >= 500ms
       peer.api = peer.api || BMA.lightInstance(peer.getHost(), peer.getPort(), peer.getPath(), peer.isSsl(), timeout);
 
       // Get current block
@@ -625,7 +631,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           }
 
           // Get sandboxes
-          if (data.expertMode) {
+          if (data.expertMode && data.withSandboxes !== false) {
             jobs.push(peer.api.node.sandboxes()
               .catch(function(err) {
                 return {}; // continue
@@ -665,18 +671,21 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
     flushNewPeersAndSort = function(newPeers, updateMainBuid) {
       newPeers = newPeers || data.newPeers;
       if (!newPeers.length) return;
-      var ids = _.map(data.peers, function(peer){
-        return peer.id;
-      });
+      var ids = _.pluck(data.peers, 'id');
       var hasUpdates = false;
       var newPeersAdded = 0;
       _.forEach(newPeers.splice(0), function(peer) {
-        if (!ids[peer.id]) {
+        if (!ids.includes(peer.id)) {
           data.peers.push(peer);
-          ids[peer.id] = peer;
+          ids.push(peer.id);
           hasUpdates = true;
           newPeersAdded++;
         }
+        else {
+          // Skip if exists
+          //data.peers[ids.indexOf(peer.id)] = peer;
+          //hasUpdates = true;
+        }
       });
       if (hasUpdates) {
         console.debug('[network] Flushing {0} new peers...'.format(newPeersAdded));
@@ -873,8 +882,11 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
           data.filter = options.filter ? angular.merge(data.filter, options.filter) : data.filter;
           data.sort = options.sort ? angular.merge(data.sort, options.sort) : data.sort;
           data.expertMode = angular.isDefined(options.expertMode) ? options.expertMode : data.expertMode;
-          data.timeout = angular.isDefined(options.timeout) ? options.timeout : getDefaultTimeout();
+          data.timeout = angular.isDefined(options.timeout) ? options.timeout : getDeviceTimeout(); // Fore recompute
           data.startTime = Date.now();
+          data.autoRefresh = angular.isDefined(options.autoRefresh) ? data.autoRefresh : true;
+          data.flushIntervalMs = angular.isDefined(options.flushIntervalMs) ? options.flushIntervalMs : 1000;
+          data.withSandboxes = angular.isDefined(options.withSandboxes) ? options.withSandboxes : true;
 
           // Init a min block number
           var mainBlockNumber = data.mainBlock && buidBlockNumber(data.mainBlock.buid);
@@ -892,9 +904,10 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
         })
         .then(function() {
           var now = Date.now();
-          console.info('[network] [#{0}] Starting from [{1}{2}] {ssl: {3}}'.format(pid, bma.server, bma.path, bma.useSsl));
+          console.info('[network] [#{0}] Starting from [{1}{2}] {ssl: {3}, expertMode: {4}, sandboxes: {5}}'.format(pid, bma.server, bma.path, bma.useSsl, data.expertMode, data.withSandboxes));
 
-          addListeners();
+          // Start listener to auto refresh peers
+          if (data.autoRefresh) addListeners();
 
           return loadPeers()
             .then(function(peers){
@@ -910,7 +923,7 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
 
     close = function(pid) {
       if (data.bma) {
-        console.info(pid > 0 ? '[network] [#{0}] Stopping...'.format(pid) : '[network] Stopping...');
+        console.info(pid > 0 ? '[network] [#{0}] Stopping'.format(pid) : '[network] Stopping');
         removeListeners();
         resetData();
       }
@@ -959,34 +972,85 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
       options.filter.ssl = isHttpsMode ? true : undefined /*= all */;
       options.filter.online = true;
       options.filter.expertMode = false; // Difficulties not need
-      options.timeout = angular.isDefined(options.timeout) ? options.timeout : getDefaultTimeout();
+      options.timeout = angular.isDefined(options.timeout) ? options.timeout : getDeviceTimeout();
+      options.minConsensusPeerCount = options.minConsensusPeerCount > 0 ? options.minConsensusPeerCount : -1;
+      options.autoRefresh = options.autoRefresh === true; // false by default (dont need to detecte changes, using websockets)
+      options.flushIntervalMs = angular.isDefined(options.flushIntervalMs) ? options.flushIntervalMs : 1000;
+      options.withSandboxes = angular.isDefined(options.withSandboxes) ? options.withSandboxes : false;
+
+      // Compute minConsensusPeerCount, from statistics
+      if (options.minConsensusPeerCount <= 0) {
+        // Init using default config value
+        options.minConsensusPeerCount = csSettings.data.minConsensusPeerCount > 0 ? csSettings.data.minConsensusPeerCount : -1;
+
+        // Override with stats value (if greater)
+        var avgPeerCount = csSettings.stats.computeAvgPeerCount(options);
+        var avgPeerCount80pct = avgPeerCount > 0 && Math.floor(avgPeerCount * 0.8) || -1;
+        if (avgPeerCount80pct > options.minConsensusPeerCount) {
+          console.debug('[network] Using computed minConsensusPeerCount={0} (=80% of average peer count {1})'.format(avgPeerCount80pct, avgPeerCount));
+          options.minConsensusPeerCount = avgPeerCount80pct;
+        }
+      }
 
       var wasStarted = isStarted();
       var pid = wasStarted ? data.pid : data.pid + 1;
+      console.info('[network] [#{0}] Getting synchronized BMA peers... {timeout: {1}, minConsensusPeerCount: {2}, flushIntervalMs: {3}}'.format(pid,
+        options.timeout, options.minConsensusPeerCount, options.flushIntervalMs));
 
-      var now = Date.now();
-      console.info('[network] [#{0}] Getting synchronized BMA peers... {timeout: {1}}'.format(pid, options.timeout));
+      var filterPeers = function(peers) {
+        var peerUrls = [];
+        return peers.reduce(function(res, peer) {
+          // Exclude if not BMA or not on the main consensus block
+          if (!peer || !peer.isBma() || !peer.hasMainConsensusBlock) return res;
 
-      return startIfNeed(bma, options)
-        .then(function(data){
-          var peerUrls = [];
-          var peers = data && data.peers.reduce(function(res, peer) {
-            // Exclude if not BMA or not on the main consensus block
-            if (!peer || !peer.isBma() || !peer.hasMainConsensusBlock) return res;
+          // Fill some properties compatible
+          peer.compatible = isCompatible(peer);
+          peer.url = peer.getUrl();
+
+          // Clean unused properties (e.g. the API, created by BMA.lightInstance())
+          delete peer.api;
 
-            // Fill some properties compatible
-            peer.compatible = isCompatible(peer);
-            peer.url = peer.getUrl();
+          // Remove duplicate
+          if (peerUrls.includes(peer.url)) return res;
+          peerUrls.push(peer.url);
 
-            // Clean unused properties (e.g. the API, created by BMA.lightInstance())
-            delete peer.api;
+          return res.concat(peer);
+        }, []);
+      };
 
-            // Remove duplicate
-            if (peerUrls.includes(peer.url)) return res;
-            peerUrls.push(peer.url);
 
-            return res.concat(peer);
-          }, []);
+      var deferred = $q.defer();
+      var checkEnoughListener = null;
+
+      // Stop when enough peers, on the main consensus block
+      if (options.minConsensusPeerCount > 0) {
+        checkEnoughListener = api.data.on.changed($rootScope, function(data) {
+          console.debug('[network] [#{0}] {1} peers'.format(pid, data.peers.length));
+          var peers = filterPeers(data.peers);
+          var consensusPeerCount = peers.length;
+          // Stop here, if reach minConsensusPeerCount
+          if (consensusPeerCount >= options.minConsensusPeerCount) {
+            if (checkEnoughListener) {
+              checkEnoughListener();
+              checkEnoughListener = null;
+
+              console.debug('[network] [#{0}] Found enough peers on main consensus - in {1}ms (timeout {2}ms) '.format(pid, Date.now() - data.startTime, options.timeout));
+              deferred.resolve(peers);
+            }
+          }
+        });
+      }
+
+      // Start network scan
+      startIfNeed(bma, options)
+        .then(function(data) {
+          var peers = filterPeers(data.peers);
+          deferred.resolve(peers);
+        })
+        .catch(deferred.reject);
+
+      return deferred.promise
+        .then(function(peers){
 
           // Log
           if (peers && peers.length > 0) {
@@ -995,18 +1059,24 @@ angular.module('cesium.network.services', ['ngApi', 'cesium.currency.services',
               peers.length,
               data.peers.length,
               mainConsensusBlock,
-              Date.now() - now));
+              Date.now() - data.startTime));
           }
           else {
-            console.warn('[network] [#{0}] No synchronized BMA peers found - in {1}ms'.format(pid, Date.now() - now));
+            console.warn('[network] [#{0}] No synchronized BMA peers found - in {1}ms'.format(pid, Date.now() - data.startTime));
           }
 
+          // Stop network
           if (!wasStarted) close(pid);
+          if (checkEnoughListener) checkEnoughListener();
+
           return peers;
         })
         .catch(function(err) {
           console.error('[network] [#{0}] Error while getting synchronized BMA peers'.format(pid), err);
+
           if (!wasStarted) close(pid);
+          if (checkEnoughListener) checkEnoughListener();
+
           throw err;
         });
     },
diff --git a/www/js/services/settings-services.js b/www/js/services/settings-services.js
index 81ceb5e5a25c7c6f9fe0f71d0455895cfa6663dc..ff47d939a413b90c8c20428de44ed8bcce39b9e6 100644
--- a/www/js/services/settings-services.js
+++ b/www/js/services/settings-services.js
@@ -1,7 +1,7 @@
 
 angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
 
-.factory('csSettings', function($rootScope, $q, $window, Api, localStorage, $translate, csConfig) {
+.factory('csSettings', function($rootScope, $q, $window, $timeout, Api, localStorage, $translate, csConfig) {
   'ngInject';
 
   // Define app locales
@@ -76,7 +76,7 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     timeWarningExpire: 2592000 * 3 /*=3 mois*/,
     minVersion: '1.8.0',
     minVersionAtStartup: '1.8.7', // use for node auto-selection
-    minPeerCountAtStartup: 10, // use for node auto-selection (avoid to start if no few peers found)
+    minConsensusPeerCount: 10, // use for node auto-selection (avoid to start if no few peers found)
     sourceUrl: 'https://git.duniter.org/clients/cesium-grp/cesium',
     sourceLicenseUrl: 'https://git.duniter.org/clients/cesium-grp/cesium/-/raw/master/LICENSE',
     newIssueUrl: "https://git.duniter.org/clients/cesium-grp/cesium/issues/new",
@@ -107,7 +107,10 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     blockValidityWindow: 6,
     network: {
       // Synchronized BMA peers found
-      peers: []
+      peers: [],
+      stats: [],
+      statsWindowSecond: (csConfig.network && csConfig.network.statsWindowSecond || 10 * 24 * 60 * 60), // 10 days
+      statsPeriodSecond: 5 * 60 // 5 min
     },
     helptip: {
       enable: true,
@@ -302,6 +305,85 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
         });
   }
 
+  /**
+  * Save synchronized BMA peers, after a network scan
+  * @param newData
+  */
+  function savePeers(peers, options) {
+    if (!peers) return; // skip empty
+
+    console.debug("[settings] Saving {0} BMA peers...".format(peers.length));
+
+    data.network = data.network || {};
+    data.network.peers = peers;
+
+    // Update peer stats
+    var statsWindowSecond = options && options.statsWindowSecond || data.network.statsWindowSecond;
+    if (statsWindowSecond > 0) {
+
+      // Clean stats outside the stats window
+      var now = Date.now();
+      var minTime = now - statsWindowSecond * 1000;
+      data.network.stats = _.filter(data.network.stats || [], function(stat) {
+        return stat.time > minTime;
+      });
+
+      var currentStat = {
+        time: now,
+        peerCount: peers.length + 1 /*current BMA peer*/
+      };
+
+      // If previous stats is recent (< 5min)
+      // this is used to avoid too many stats, but keep the must fresh value
+      var previousStats = data.network.stats.length ? data.network.stats[data.network.stats.length-1] : undefined;
+      var previousAgeSecond = previousStats && (now - previousStats.time) / 1000;
+      var statsPeriodSecond = data.network.statsPeriodSecond || 5 * 60; // 5 min;
+      if (previousAgeSecond && previousAgeSecond < statsPeriodSecond) {
+        if (currentStat.peerCount === previousStats.peerCount) {
+          console.debug("[settings] Skip network stats (recent stats exists)");
+        }
+        // Replace it peer count change
+        else {
+          angular.merge(previousStats, currentStat);
+        }
+      }
+      else {
+        // Insert
+        data.network.stats.push(currentStat);
+      }
+    }
+
+    // Storing, with a delay 2s
+    $timeout(store, 2000);
+  }
+
+  function computeAvgPeerCount(options) {
+    if (!data.network || !data.network.stats || !data.network.stats.length) return undefined; // Cannot compute
+
+    var statsWindowSecond = options && options.statsWindowSecond || data.network.statsWindowSecond || -1;
+    if (statsWindowSecond > data.network.statsWindowSecond) {
+      console.warn('[settings] Peer stats windows ({0}s) should not be greater than the collected windows ({1}s)'.format(
+        statsWindowSecond,
+        data.statsWindowSecond
+      ));
+      statsWindowSecond = data.network.statsWindowSecond;
+    }
+
+    var now = Date.now();
+    var minTime = now - statsWindowSecond * 1000;
+
+    // Select stats inside the expected window
+    var stats = _.filter(data.network.stats || [], function(stat) {
+      return stat.time > minTime && stat.peerCount > 1;
+    });
+
+    if (!stats.length) return undefined; // Not enough stats to compute something
+
+    // Compute the AVG(peerCount)
+    var sum = _.reduce(stats, function(sum, stat) { return sum + stat.peerCount; }, 0);
+    return Math.floor(sum / stats.length);
+  }
+
   function getLicenseUrl() {
     var locale = data.locale && data.locale.id || csConfig.defaultLanguage || 'en';
     return (csConfig.license) ?
@@ -408,6 +490,10 @@ angular.module('cesium.settings.services', ['ngApi', 'cesium.config'])
     restore: restore,
     getLicenseUrl: getLicenseUrl,
     getFeedUrl: getFeedUrl,
+    savePeers: savePeers,
+    stats: {
+      computeAvgPeerCount: computeAvgPeerCount
+    },
     defaultSettings: defaultSettings,
     // api extension
     api: api,