diff --git a/app/controllers/webmin.controller.js b/app/controllers/webmin.controller.js
index 4f9d6b59a61532aa56dd3d27a6ed55190c74a92c..3d8ec3705efc9acd16fe1be137e34253ab5da522 100644
--- a/app/controllers/webmin.controller.js
+++ b/app/controllers/webmin.controller.js
@@ -1,5 +1,6 @@
 "use strict";
 
+const path = require('path');
 const util = require('util');
 const es = require('event-stream');
 const stream      = require('stream');
@@ -48,14 +49,18 @@ function WebAdmin (dbConf, overConf) {
 
   let pluggedConfP = plugForConf();
 
-  let pluggedDALP = co(function *() {
-    yield pluggedConfP;
+  let pluggedDALP = replugDAL();
 
-    // Routing documents
-    server.routing();
+  function replugDAL() {
+    return co(function *() {
+      yield pluggedConfP;
 
-    return plugForDAL();
-  });
+      // Routing documents
+      server.routing();
+
+      return plugForDAL();
+    });
+  }
 
   this.summary = () => co(function *() {
     yield pluggedDALP;
@@ -67,6 +72,9 @@ function WebAdmin (dbConf, overConf) {
       "host": host,
       "current": current,
       "pubkey": server.keyPair.publicKey,
+      "conf": {
+        "cpu": server.conf.cpu
+      },
       "parameters": parameters
     };
   });
@@ -75,7 +83,7 @@ function WebAdmin (dbConf, overConf) {
     const conf = http2raw.conf(req);
     const pair = yield keyring.scryptKeyPair(conf.idty_entropy, conf.idty_password);
     return {
-      "pubkey": base58.encode(pair.publicKey)
+      "pubkey": pair.publicKey
     };
   });
 
@@ -244,9 +252,16 @@ function WebAdmin (dbConf, overConf) {
         sec: base58.encode(secretKey)
       }
     }));
-    pluggedConfP = co(function *() {
-      yield server.loadConf();
-    });
+    pluggedConfP = yield server.loadConf();
+    yield pluggedConfP;
+    return {};
+  });
+
+  this.applyCPUConf = (req) => co(function *() {
+    yield pluggedConfP;
+    server.conf.cpu = http2raw.cpu(req);
+    yield server.dal.saveConf(server.conf);
+    pluggedConfP = yield server.loadConf();
     yield pluggedConfP;
     return {};
   });
@@ -417,6 +432,43 @@ function WebAdmin (dbConf, overConf) {
     return {};
   });
 
+  this.exportData = () => co(function *() {
+    yield pluggedDALP;
+    return server.exportAllDataAsZIP();
+  });
+
+  this.importData = (req) => co(function *() {
+    yield that.stopHTTP();
+    yield that.stopAllServices();
+    yield server.unplugFileSystem();
+    yield pluggedDALP;
+    if (!req.files.importData) {
+      throw "Wrong upload file name";
+    }
+    const importZipPath = path.join(server.home, 'import.zip');
+    yield new Promise((resolve, reject) => {
+      req.files.importData.mv(importZipPath, (err) => {
+        err ? reject(err) : resolve();
+      });
+    });
+    yield server.importAllDataFromZIP(importZipPath);
+    pluggedConfP = plugForConf();
+    pluggedDALP = replugDAL();
+    return {};
+  });
+
+  this.testPeer = (req) => co(function *() {
+    return server.testForSync(req.body.host, parseInt(req.body.port));
+  });
+
+  this.loadData = (dunFile) => co(function *() {
+    yield pluggedDALP;
+    // We have to wait for a non-breaking window to process reset
+    yield server.unplugFileSystem();
+    yield server.cleanDBData();
+    return {};
+  });
+
   function plugForConf() {
     return co(function *() {
       yield server.plugFileSystem();
diff --git a/app/lib/blockchainContext.js b/app/lib/blockchainContext.js
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/app/lib/computation/blockchainContext.js b/app/lib/computation/blockchainContext.js
index 578b62289286d7d20cc442e7ca8f8ba3477a7892..84ed55db2939c1c289860f8bdac8c4ca39475a30 100644
--- a/app/lib/computation/blockchainContext.js
+++ b/app/lib/computation/blockchainContext.js
@@ -84,6 +84,8 @@ function BlockchainContext() {
     yield dal.blockDAL.setSideBlock(block, previousBlock);
     yield undoCertifications(block);
     yield undoLinks(block);
+    yield dal.unflagExpiredIdentitiesOf(block.number);
+    yield dal.unflagExpiredCertificationsOf(block.number);
     if (previousBlock) {
       yield dal.undoObsoleteLinks(previousBlock.medianTime - conf.sigValidity);
     }
@@ -132,6 +134,12 @@ function BlockchainContext() {
     yield that.computeObsoleteLinks(block);
     // Compute obsolete memberships (active, joiner)
     yield that.computeObsoleteMemberships(block);
+    // Compute obsolete identities
+    yield that.computeExpiredIdentities(block);
+    // Compute obsolete certifications
+    yield that.computeExpiredCertifications(block);
+    // Compute obsolete memberships
+    yield that.computeExpiredMemberships(block);
     // Update consumed sources & create new ones
     yield that.updateSources(block);
     // Delete eventually present transactions
@@ -408,6 +416,27 @@ function BlockchainContext() {
     }
   });
 
+  this.computeExpiredIdentities = (block) => co(function *() {
+    let lastForExpiry = yield dal.getIdentityExpiringBlock(block, conf.idtyWindow);
+    if (lastForExpiry) {
+      yield dal.flagExpiredIdentities(lastForExpiry.number, block.number);
+    }
+  });
+
+  this.computeExpiredCertifications = (block) => co(function *() {
+    let lastForExpiry = yield dal.getCertificationExpiringBlock(block, conf.certWindow);
+    if (lastForExpiry) {
+      yield dal.flagExpiredCertifications(lastForExpiry.number, block.number);
+    }
+  });
+
+  this.computeExpiredMemberships = (block) => co(function *() {
+    let lastForExpiry = yield dal.getMembershipExpiringBlock(block, conf.certWindow);
+    if (lastForExpiry) {
+      yield dal.flagExpiredMemberships(lastForExpiry.number, block.number);
+    }
+  });
+
   this.updateSources = (block) => co(function*() {
     if (block.dividend) {
       const idties = yield dal.getMembers();
diff --git a/app/lib/constants.js b/app/lib/constants.js
index 8716f701357d555e3e79eeb437b628eafe2cd521..b7b71e632f900bf64f63a4918f8fd9266c823e7d 100644
--- a/app/lib/constants.js
+++ b/app/lib/constants.js
@@ -53,6 +53,11 @@ module.exports = {
     SIGNATURE_DOES_NOT_MATCH:             { httpCode: 400, uerr: { ucode: 1003, message: "Signature does not match" }},
     ALREADY_UP_TO_DATE:                   { httpCode: 400, uerr: { ucode: 1004, message: "Already up-to-date" }},
     WRONG_DOCUMENT:                       { httpCode: 400, uerr: { ucode: 1005, message: "Document has unkown fields or wrong line ending format" }},
+    HTTP_LIMITATION:                      { httpCode: 503, uerr: { ucode: 1006, message: "This URI has reached its maximum usage quota. Please retry later." }},
+    SANDBOX_FOR_IDENTITY_IS_FULL:         { httpCode: 503, uerr: { ucode: 1007, message: "The identities' sandbox is full. Please retry with another document or retry later." }},
+    SANDBOX_FOR_CERT_IS_FULL:             { httpCode: 503, uerr: { ucode: 1008, message: "The certifications' sandbox is full. Please retry with another document or retry later." }},
+    SANDBOX_FOR_MEMERSHIP_IS_FULL:        { httpCode: 503, uerr: { ucode: 1009, message: "The memberships' sandbox is full. Please retry with another document or retry later." }},
+    SANDBOX_FOR_TRANSACTION_IS_FULL:      { httpCode: 503, uerr: { ucode: 1010, message: "The transactions' sandbox is full. Please retry with another document or retry later." }},
 
     HTTP_PARAM_PUBKEY_REQUIRED:           { httpCode: 400, uerr: { ucode: 1101, message: "Parameter `pubkey` is required" }},
     HTTP_PARAM_IDENTITY_REQUIRED:         { httpCode: 400, uerr: { ucode: 1102, message: "Parameter `identity` is required" }},
@@ -64,6 +69,7 @@ module.exports = {
     HTTP_PARAM_CERT_REQUIRED:             { httpCode: 400, uerr: { ucode: 1108, message: "Parameter `cert` is required" }},
     HTTP_PARAM_REVOCATION_REQUIRED:       { httpCode: 400, uerr: { ucode: 1109, message: "Parameter `revocation` is required" }},
     HTTP_PARAM_CONF_REQUIRED:             { httpCode: 400, uerr: { ucode: 1110, message: "Parameter `conf` is required" }},
+    HTTP_PARAM_CPU_REQUIRED:              { httpCode: 400, uerr: { ucode: 1111, message: "Parameter `cpu` is required" }},
 
     // Business errors
     NO_MATCHING_IDENTITY:                 { httpCode: 404, uerr: { ucode: 2001, message: "No matching identity" }},
diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js
index 454701c15fabed662270685af27afe86045c4a79..aa70740c6ce8595731a1ebe1378bd958e4e0f32b 100644
--- a/app/lib/dal/fileDAL.js
+++ b/app/lib/dal/fileDAL.js
@@ -25,6 +25,10 @@ function FileDAL(params) {
   const rootPath = params.home;
   const myFS = params.fs;
   const sqlite = params.dbf();
+  let dbOpened = false;
+  sqlite.once('open', () => {
+    dbOpened = true;
+  });
   const wotbInstance = params.wotb;
   const that = this;
 
@@ -408,187 +412,114 @@ function FileDAL(params) {
     return that.idtyDAL.saveIdentity(idty);
   });
 
-  this.getMembershipExcludingBlock = (current, msValidtyTime) => co(function *() {
-    let currentExcluding;
-    if (current.number > 0) {
-      try {
-        currentExcluding = yield that.indicatorsDAL.getCurrentMembershipExcludingBlock();
-      } catch (e) {
-        currentExcluding = null;
-      }
-    }
-    if (!currentExcluding) {
-      const root = yield that.getRootBlock();
-      const delaySinceStart = current.medianTime - root.medianTime;
-      if (delaySinceStart > msValidtyTime) {
-        return that.indicatorsDAL.writeCurrentExcluding(root).then(() => root);
-      }
-    } else {
-      const start = currentExcluding.number;
-      let newExcluding;
-      let top = current.number, bottom = start;
-      // Binary tree search
-      do {
-        let middle = top - bottom;
-        if (middle % 2 != 0) {
-          middle = middle + 1;
-        }
-        middle /= 2;
-        middle += bottom;
-        if (middle == top) {
-          middle--;
-          bottom--; // Helps not being stuck looking at 'top'
+  this.getMembershipExcludingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring(
+    current,
+    msValidtyTime,
+    that.indicatorsDAL.getCurrentMembershipExcludingBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentExcluding.bind(that.indicatorsDAL)
+  );
+
+  this.getMembershipRevocatingBlock = (current, msValidtyTime) => getCurrentExcludingOrExpiring(
+    current,
+    msValidtyTime,
+    that.indicatorsDAL.getCurrentMembershipRevocatingBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentRevocating.bind(that.indicatorsDAL)
+  );
+
+  this.getCertificationExcludingBlock = (current, certValidtyTime) => getCurrentExcludingOrExpiring(
+    current,
+    certValidtyTime,
+    that.indicatorsDAL.getCurrentCertificationExcludingBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentExcludingForCert.bind(that.indicatorsDAL)
+  );
+
+  this.getIdentityExpiringBlock = (current, idtyValidtyTime) => getCurrentExcludingOrExpiring(
+    current,
+    idtyValidtyTime,
+    that.indicatorsDAL.getCurrentIdentityExpiringBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentExpiringForIdty.bind(that.indicatorsDAL)
+  );
+
+  this.getCertificationExpiringBlock = (current, certWindow) => getCurrentExcludingOrExpiring(
+    current,
+    certWindow,
+    that.indicatorsDAL.getCurrentCertificationExpiringBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentExpiringForCert.bind(that.indicatorsDAL)
+  );
+
+  this.getMembershipExpiringBlock = (current, msWindow) => getCurrentExcludingOrExpiring(
+    current,
+    msWindow,
+    that.indicatorsDAL.getCurrentMembershipExpiringBlock.bind(that.indicatorsDAL),
+    that.indicatorsDAL.writeCurrentExpiringForMembership.bind(that.indicatorsDAL)
+  );
+
+  function getCurrentExcludingOrExpiring(current, delayMax, currentGetter, currentSetter) {
+    return co(function *() {
+      let currentExcluding;
+      if (current.number > 0) {
+        try {
+          currentExcluding = yield currentGetter();
+        } catch (e) {
+          currentExcluding = null;
         }
-        const middleBlock = yield that.getBlock(middle);
-        const middleNextB = yield that.getBlock(middle + 1);
-        const delaySinceMiddle = current.medianTime - middleBlock.medianTime;
-        const delaySinceNextB = current.medianTime - middleNextB.medianTime;
-        const isValidPeriod = delaySinceMiddle <= msValidtyTime;
-        const isValidPeriodB = delaySinceNextB <= msValidtyTime;
-        const isExcludin = !isValidPeriod && isValidPeriodB;
-        //console.log('MS: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP');
-        if (isExcludin) {
-          // Found
-          yield that.indicatorsDAL.writeCurrentExcluding(middleBlock);
-          newExcluding = middleBlock;
-        }
-        else if (isValidPeriod) {
-          // Look down in the blockchain
-          top = middle;
-        }
-        else {
-          // Look up in the blockchain
-          bottom = middle;
-        }
-      } while (!newExcluding);
-      return newExcluding;
-    }
-  });
-
-  // TODO: this is complete duplicate of getMembershipExcludingBlock()n but with two different calls:
-  // * getCurrentMembershipRevocatingBlock()
-  // * writeCurrentRevocating
-  this.getMembershipRevocatingBlock = (current, msValidtyTime) => co(function *() {
-    let currentExcluding;
-    if (current.number > 0) {
-      try {
-        currentExcluding = yield that.indicatorsDAL.getCurrentMembershipRevocatingBlock();
-      } catch (e) {
-        currentExcluding = null;
-      }
-    }
-    if (!currentExcluding) {
-      const root = yield that.getRootBlock();
-      const delaySinceStart = current.medianTime - root.medianTime;
-      if (delaySinceStart > msValidtyTime) {
-        return that.indicatorsDAL.writeCurrentRevocating(root).then(() => root);
       }
-    } else {
-      const start = currentExcluding.number;
-      let newRevocating;
-      let top = current.number, bottom = start;
-      // Binary tree search
-      do {
-        let middle = top - bottom;
-        if (middle % 2 != 0) {
-          middle = middle + 1;
-        }
-        middle /= 2;
-        middle += bottom;
-        if (middle == top) {
-          middle--;
-          bottom--; // Helps not being stuck looking at 'top'
-        }
-        const middleBlock = yield that.getBlock(middle);
-        const middleNextB = yield that.getBlock(middle + 1);
-        const delaySinceMiddle = current.medianTime - middleBlock.medianTime;
-        const delaySinceNextB = current.medianTime - middleNextB.medianTime;
-        const isValidPeriod = delaySinceMiddle <= msValidtyTime;
-        const isValidPeriodB = delaySinceNextB <= msValidtyTime;
-        const isExcludin = !isValidPeriod && isValidPeriodB;
-        //console.log('MS: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP');
-        if (isExcludin) {
-          // Found
-          yield that.indicatorsDAL.writeCurrentRevocating(middleBlock);
-          newRevocating = middleBlock;
+      if (!currentExcluding) {
+        const root = yield that.getRootBlock();
+        const delaySinceStart = current.medianTime - root.medianTime;
+        if (delaySinceStart > delayMax) {
+          return currentSetter(root).then(() => root);
         }
-        else if (isValidPeriod) {
-          // Look down in the blockchain
-          top = middle;
-        }
-        else {
-          // Look up in the blockchain
-          bottom = middle;
-        }
-      } while (!newRevocating);
-      return newRevocating;
-    }
-  });
-
-  this.getCertificationExcludingBlock = (current, certValidtyTime) => co(function *() {
-    let currentExcluding;
-    if (current.number > 0) {
-      try {
-        currentExcluding = yield that.indicatorsDAL.getCurrentCertificationExcludingBlock();
-      } catch (e) {
-        currentExcluding = null;
-      }
-    }
-    if (!currentExcluding) {
-      const root = yield that.getRootBlock();
-      const delaySinceStart = current.medianTime - root.medianTime;
-      if (delaySinceStart > certValidtyTime) {
-        return that.indicatorsDAL.writeCurrentExcludingForCert(root).then(() => root);
-      }
-    } else {
-      // Check current position
-      const currentNextBlock = yield that.getBlock(currentExcluding.number + 1);
-      if (isExcluding(current, currentExcluding, currentNextBlock, certValidtyTime)) {
-        return currentExcluding;
       } else {
-        // Have to look for new one
-        const start = currentExcluding.number;
-        let newExcluding;
-        let top = current.number;
-        let bottom = start;
-        // Binary tree search
-        do {
-          let middle = top - bottom;
-          if (middle % 2 != 0) {
-            middle = middle + 1;
-          }
-          middle /= 2;
-          middle += bottom;
-          if (middle == top) {
-            middle--;
-            bottom--; // Helps not being stuck looking at 'top'
-          }
-          const middleBlock = yield that.getBlock(middle);
-          const middleNextB = yield that.getBlock(middle + 1);
-          const delaySinceMiddle = current.medianTime - middleBlock.medianTime;
-          const delaySinceNextB = current.medianTime - middleNextB.medianTime;
-          const isValidPeriod = delaySinceMiddle <= certValidtyTime;
-          const isValidPeriodB = delaySinceNextB <= certValidtyTime;
-          const isExcludin = !isValidPeriod && isValidPeriodB;
-          //console.log('CRT: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP');
-          if (isExcludin) {
-            // Found
-            yield that.indicatorsDAL.writeCurrentExcludingForCert(middleBlock);
-            newExcluding = middleBlock;
-          }
-          else if (isValidPeriod) {
-            // Look down in the blockchain
-            top = middle;
-          }
-          else {
-            // Look up in the blockchain
-            bottom = middle;
-          }
-        } while (!newExcluding);
-        return newExcluding;
+        // Check current position
+        const currentNextBlock = yield that.getBlock(currentExcluding.number + 1);
+        if (isExcluding(current, currentExcluding, currentNextBlock, delayMax)) {
+          return currentExcluding;
+        } else {
+          // Have to look for new one
+          const start = currentExcluding.number;
+          let newExcluding;
+          let top = current.number;
+          let bottom = start;
+          // Binary tree search
+          do {
+            let middle = top - bottom;
+            if (middle % 2 != 0) {
+              middle = middle + 1;
+            }
+            middle /= 2;
+            middle += bottom;
+            if (middle == top) {
+              middle--;
+              bottom--; // Helps not being stuck looking at 'top'
+            }
+            const middleBlock = yield that.getBlock(middle);
+            const middleNextB = yield that.getBlock(middle + 1);
+            const delaySinceMiddle = current.medianTime - middleBlock.medianTime;
+            const delaySinceNextB = current.medianTime - middleNextB.medianTime;
+            const isValidPeriod = delaySinceMiddle <= delayMax;
+            const isValidPeriodB = delaySinceNextB <= delayMax;
+            const isExcludin = !isValidPeriod && isValidPeriodB;
+            //console.log('CRT: Search between %s and %s: %s => %s,%s', bottom, top, middle, isValidPeriod ? 'DOWN' : 'UP', isValidPeriodB ? 'DOWN' : 'UP');
+            if (isExcludin) {
+              // Found
+              yield currentSetter(middleBlock);
+              newExcluding = middleBlock;
+            }
+            else if (isValidPeriod) {
+              // Look down in the blockchain
+              top = middle;
+            }
+            else {
+              // Look up in the blockchain
+              bottom = middle;
+            }
+          } while (!newExcluding);
+          return newExcluding;
+        }
       }
-    }
-  });
+    });
+  }
 
   const isExcluding = (current, excluding, nextBlock, certValidtyTime) => {
     const delaySinceMiddle = current.medianTime - excluding.medianTime;
@@ -598,6 +529,9 @@ function FileDAL(params) {
     return !isValidPeriod && isValidPeriodB;
   };
 
+  this.flagExpiredIdentities = (maxNumber, onNumber) => this.idtyDAL.flagExpiredIdentities(maxNumber, onNumber);
+  this.flagExpiredCertifications = (maxNumber, onNumber) => this.certDAL.flagExpiredCertifications(maxNumber, onNumber);
+  this.flagExpiredMemberships = (maxNumber, onNumber) => this.msDAL.flagExpiredMemberships(maxNumber, onNumber);
   this.kickWithOutdatedMemberships = (maxNumber) => this.idtyDAL.kickMembersForMembershipBelow(maxNumber);
   this.revokeWithOutdatedMemberships = (maxNumber) => this.idtyDAL.revokeMembersForMembershipBelow(maxNumber);
 
@@ -716,6 +650,12 @@ function FileDAL(params) {
 
   this.unConsumeSource = (identifier, noffset) => that.sourcesDAL.unConsumeSource(identifier, noffset);
 
+  this.unflagExpiredIdentitiesOf = (number) => that.idtyDAL.unflagExpiredIdentitiesOf(number);
+  
+  this.unflagExpiredCertificationsOf = (number) => that.certDAL.unflagExpiredCertificationsOf(number);
+  
+  this.unflagExpiredMembershipsOf = (number) => that.msDAL.unflagExpiredMembershipsOf(number);
+
   this.saveSource = (src) => that.sourcesDAL.addSource(src.type, src.number, src.identifier, src.noffset,
       src.amount, src.base, src.block_hash, src.time, src.conditions);
 
@@ -881,7 +821,11 @@ function FileDAL(params) {
   this.close = () => co(function *() {
     yield _.values(that.newDals).map((dal) => dal.cleanCache && dal.cleanCache());
     return new Promise((resolve, reject) => {
-      if (!sqlite.open) {
+      let isOpened = !dbOpened;
+      if (process.platform === 'win32') {
+        isOpened = sqlite.open; // For an unknown reason, we need this line.
+      }
+      if (!isOpened) {
         return resolve();
       }
       logger.debug('Trying to close SQLite...');
@@ -889,7 +833,12 @@ function FileDAL(params) {
         logger.info('Database closed.');
         resolve();
       });
-      sqlite.on('error', (err) => reject(err));
+      sqlite.on('error', (err) => {
+        if (err && err.message === 'SQLITE_MISUSE: Database is closed') {
+          return resolve();
+        }
+        reject(err);
+      });
       sqlite.close();
     });
   });
diff --git a/app/lib/dal/fileDALs/IndicatorsDAL.js b/app/lib/dal/fileDALs/IndicatorsDAL.js
index c923d7eaa53b158e7914ecf39f17c6b13369c391..a90087bde2062d2d51447f5e155a34d6f61033cb 100644
--- a/app/lib/dal/fileDALs/IndicatorsDAL.js
+++ b/app/lib/dal/fileDALs/IndicatorsDAL.js
@@ -29,9 +29,21 @@ function IndicatorsDAL(rootPath, qioFS, parentCore, localDAL, AbstractStorage) {
 
   this.writeCurrentExcludingForCert = (excluding) => that.coreFS.writeJSON('indicators/excludingCRT.json', excluding);
 
+  this.writeCurrentExpiringForCert = (excluding) => that.coreFS.writeJSON('indicators/expiringCRT.json', excluding);
+
+  this.writeCurrentExpiringForIdty = (excluding) => that.coreFS.writeJSON('indicators/expiringIDTY.json', excluding);
+
+  this.writeCurrentExpiringForMembership = (excluding) => that.coreFS.writeJSON('indicators/expiringMS.json', excluding);
+
   this.getCurrentMembershipExcludingBlock = () => that.coreFS.readJSON('indicators/excludingMS.json');
 
   this.getCurrentMembershipRevocatingBlock = () => that.coreFS.readJSON('indicators/revocatingMS.json');
 
+  this.getCurrentCertificationExpiringBlock = () => that.coreFS.readJSON('indicators/expiringCRT.json');
+
   this.getCurrentCertificationExcludingBlock = () => that.coreFS.readJSON('indicators/excludingCRT.json');
+
+  this.getCurrentIdentityExpiringBlock = () => that.coreFS.readJSON('indicators/expiringIDTY.json');
+
+  this.getCurrentMembershipExpiringBlock = () => that.coreFS.readJSON('indicators/expiringMS.json');
 }
diff --git a/app/lib/dal/sqliteDAL/AbstractSQLite.js b/app/lib/dal/sqliteDAL/AbstractSQLite.js
index 58d5738d6f64b768659e845fbb780f5df801cde4..6e3fbafa7fbe492e22abe5d6e14e4aed5ecb407e 100644
--- a/app/lib/dal/sqliteDAL/AbstractSQLite.js
+++ b/app/lib/dal/sqliteDAL/AbstractSQLite.js
@@ -144,6 +144,7 @@ function AbstractSQLite(db) {
 
   this.exec = (sql) => co(function *() {
     try {
+      // logger.trace(sql);
       return Q.nbind(db.exec, db)(sql);
     } catch (e) {
       logger.error('ERROR >> %s', sql);
@@ -291,8 +292,12 @@ function AbstractSQLite(db) {
     for (const f of that.booleans) {
       row[f] = Boolean(row[f]);
     }
+    // Transient
+    for (const f of (that.transientFields || [])) {
+      row[f] = row[f];
+    }
     return row;
-  };
+  }
 
   function toRow(entity) {
     let row = _.clone(entity);
diff --git a/app/lib/dal/sqliteDAL/CertDAL.js b/app/lib/dal/sqliteDAL/CertDAL.js
index b0cd51cdb9ae53c013a54b846041ddd61333d59c..9779d04d901a65973e5307a9cb1d1c033a2dcdce 100644
--- a/app/lib/dal/sqliteDAL/CertDAL.js
+++ b/app/lib/dal/sqliteDAL/CertDAL.js
@@ -5,6 +5,7 @@
 const Q = require('q');
 const co = require('co');
 const AbstractSQLite = require('./AbstractSQLite');
+const SandBox = require('./SandBox');
 
 module.exports = CertDAL;
 
@@ -28,7 +29,8 @@ function CertDAL(db) {
     'target',
     'to',
     'from',
-    'block'
+    'block',
+    'expired'
   ];
   this.arrays = [];
   this.booleans = ['linked', 'written'];
@@ -102,4 +104,44 @@ function CertDAL(db) {
       return that.exec(queries.join('\n'));
     }
   });
+
+  this.flagExpiredCertifications = (maxNumber, onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = ' + onNumber + ' ' +
+      'WHERE expired IS NULL ' +
+      'AND block_number <= ' + maxNumber);
+  });
+
+  this.unflagExpiredCertificationsOf = (onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = NULL ' +
+      'WHERE expired = ' + onNumber);
+  });
+
+  /**************************
+   * SANDBOX STUFF
+   */
+
+  this.getSandboxCertifications = () => that.query('SELECT ' +
+    '* ' +
+    'FROM ' + that.table + ' ' +
+    'WHERE expired IS NULL ' +
+    'AND written_block IS NULL ' +
+    'ORDER BY block_number ASC ' +
+    'LIMIT ' + (that.sandbox.maxSize), []);
+
+  this.sandbox = new SandBox(30, this.getSandboxCertifications.bind(this), (compared, reference) => {
+    if (compared.block_number > reference.block_number) {
+      return -1;
+    }
+    else if (compared.block_number < reference.block_number) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  });
+
+  this.getSandboxRoom = () => this.sandbox.getSandboxRoom();
+  this.setSandboxSize = (maxSize) => this.sandbox.maxSize = maxSize;
 }
\ No newline at end of file
diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.js b/app/lib/dal/sqliteDAL/IdentityDAL.js
index 6bc56dc6d0231d1669f695a384abfa68f6a77981..647f731fba54d47294251a7f9f586b6ee8332f67 100644
--- a/app/lib/dal/sqliteDAL/IdentityDAL.js
+++ b/app/lib/dal/sqliteDAL/IdentityDAL.js
@@ -6,6 +6,7 @@ const Q = require('q');
 const co = require('co');
 const logger = require('../../logger')('idtyDAL');
 const AbstractSQLite = require('./AbstractSQLite');
+const SandBox = require('./SandBox');
 
 module.exports = IdentityDAL;
 
@@ -33,11 +34,13 @@ function IdentityDAL(db, wotb) {
     'sig',
     'hash',
     'written',
-    'wotb_id'
+    'wotb_id',
+    'expired'
   ];
   this.arrays = [];
   this.booleans = ['revoked', 'member', 'kick', 'leaving', 'wasMember', 'written'];
   this.pkFields = ['pubkey', 'uid', 'hash'];
+  this.transientFields = ['certsCount', 'ref_block'];
   this.translated = {};
 
   this.init = () => co(function *() {
@@ -233,6 +236,19 @@ function IdentityDAL(db, wotb) {
     uid: "%" + search + "%"
   });
 
+  this.flagExpiredIdentities = (maxNumber, onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = ' + onNumber + ' ' +
+      'WHERE expired IS NULL ' +
+      'AND CAST(SUBSTR(buid, 0, INSTR(buid, "-")) as number) <= ' + maxNumber);
+  });
+
+  this.unflagExpiredIdentitiesOf = (onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = NULL ' +
+      'WHERE expired = ' + onNumber);
+  });
+
   this.kickMembersForMembershipBelow = (maxNumber) => co(function *() {
     const toKick = yield that.sqlFind({
       currentINN: { $lte: maxNumber },
@@ -258,4 +274,40 @@ function IdentityDAL(db, wotb) {
       yield that.saveEntity(idty);
     }
   });
+
+  /**************************
+   * SANDBOX STUFF
+   */
+
+  this.getSandboxIdentities = () => that.query('SELECT ' +
+    'I.*, ' +
+    'I.hash, ' +
+    '(SELECT COUNT(*) FROM cert C where C.target = I.hash) AS certsCount, ' +
+    'CAST(SUBSTR(buid, 0, INSTR(buid, "-")) as number) AS ref_block ' +
+    'FROM ' + that.table + ' as I ' +
+    'WHERE NOT I.member ' +
+    'AND I.expired IS NULL ' +
+    'ORDER BY certsCount DESC, ref_block ASC ' +
+    'LIMIT ' + (that.sandbox.maxSize), []);
+
+  this.sandbox = new SandBox(10, this.getSandboxIdentities.bind(this), (compared, reference) => {
+    if (compared.certsCount < reference.certsCount) {
+      return -1;
+    }
+    else if (compared.certsCount > reference.certsCount) {
+      return 1;
+    }
+    else if (compared.ref_block > reference.ref_block) {
+      return -1;
+    }
+    else if (compared.ref_block < reference.ref_block) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  });
+
+  this.getSandboxRoom = () => this.sandbox.getSandboxRoom();
+  this.setSandboxSize = (maxSize) => this.sandbox.maxSize = maxSize;
 }
diff --git a/app/lib/dal/sqliteDAL/MembershipDAL.js b/app/lib/dal/sqliteDAL/MembershipDAL.js
index 10a60b134b8bf46d85ffe30a50122fcc638ee8f3..99a3276eb33b3372647b45945b3a326640ce3e55 100644
--- a/app/lib/dal/sqliteDAL/MembershipDAL.js
+++ b/app/lib/dal/sqliteDAL/MembershipDAL.js
@@ -6,6 +6,7 @@ const Q = require('q');
 const co = require('co');
 const _ = require('underscore');
 const AbstractSQLite = require('./AbstractSQLite');
+const SandBox = require('./SandBox');
 
 module.exports = MembershipDAL;
 
@@ -31,7 +32,8 @@ function MembershipDAL(db) {
     'idtyHash',
     'written',
     'written_number',
-    'signature'
+    'signature',
+    'expired'
   ];
   this.arrays = [];
   this.booleans = ['written'];
@@ -155,4 +157,44 @@ function MembershipDAL(db) {
       return that.exec(queries.join('\n'));
     }
   });
+
+  this.flagExpiredMemberships = (maxNumber, onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = ' + onNumber + ' ' +
+      'WHERE expired IS NULL ' +
+      'AND blockNumber <= ' + maxNumber);
+  });
+
+  this.unflagExpiredMembershipsOf = (onNumber) => co(function *() {
+    yield that.exec('UPDATE ' + that.table + ' ' +
+      'SET expired = NULL ' +
+      'WHERE expired = ' + onNumber);
+  });
+
+  /**************************
+   * SANDBOX STUFF
+   */
+
+  this.getSandboxMemberships = () => that.query('SELECT ' +
+    '* ' +
+    'FROM ' + that.table + ' ' +
+    'WHERE expired IS NULL ' +
+    'AND written_number IS NULL ' +
+    'ORDER BY blockNumber ASC ' +
+    'LIMIT ' + (that.sandbox.maxSize), []);
+
+  this.sandbox = new SandBox(30, this.getSandboxMemberships.bind(this), (compared, reference) => {
+    if (compared.block_number > reference.block_number) {
+      return -1;
+    }
+    else if (compared.block_number < reference.block_number) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  });
+
+  this.getSandboxRoom = () => this.sandbox.getSandboxRoom();
+  this.setSandboxSize = (maxSize) => this.sandbox.maxSize = maxSize;
 }
diff --git a/app/lib/dal/sqliteDAL/MetaDAL.js b/app/lib/dal/sqliteDAL/MetaDAL.js
index b319f9a39ce8b95e96da7f96a03ee2c56111e6c4..7d234043b86fb66c875ee22d29dc0c7754ccd821 100644
--- a/app/lib/dal/sqliteDAL/MetaDAL.js
+++ b/app/lib/dal/sqliteDAL/MetaDAL.js
@@ -99,7 +99,15 @@ function MetaDAL(db) {
       }
       // Blocks since last UD have the same monetary mass as last UD block
       yield blockDAL.exec('UPDATE block SET monetaryMass = ' + monetaryMass + ' WHERE number >= ' + lastUDBlock);
-    })
+    }),
+
+    6: 'BEGIN; ALTER TABLE idty ADD COLUMN expired INTEGER NULL; COMMIT;',
+    7: 'BEGIN; ALTER TABLE cert ADD COLUMN expired INTEGER NULL; COMMIT;',
+    8: 'BEGIN; ALTER TABLE membership ADD COLUMN expired INTEGER NULL; COMMIT;',
+    9: 'BEGIN;' +
+    'ALTER TABLE txs ADD COLUMN output_base INTEGER NULL;' +
+    'ALTER TABLE txs ADD COLUMN output_amount INTEGER NULL;' +
+    'COMMIT;'
   };
 
   this.init = () => co(function *() {
@@ -112,28 +120,41 @@ function MetaDAL(db) {
       'COMMIT;', []);
   });
 
-  this.upgradeDatabase = () => co(function *() {
-    let version = yield that.getVersion();
-    while(migrations[version]) {
-      logger.debug("Upgrading from v%s to v%s...", version, version + 1);
-
-      if (typeof migrations[version] == "string") {
+  function executeMigration(migration) {
+    return co(function *() {
+      if (typeof migration == "string") {
 
         // Simple SQL script to pass
-        yield that.exec(migrations[version]);
+        yield that.exec(migration);
 
-      } else if (typeof migrations[version] == "function") {
+      } else if (typeof migration == "function") {
 
         // JS function to execute
-        yield migrations[version]();
-        
+        yield migration();
+
       }
+    });
+  }
+
+  this.upgradeDatabase = () => co(function *() {
+    let version = yield that.getVersion();
+    while(migrations[version]) {
+      logger.debug("Upgrading from v%s to v%s...", version, version + 1);
+
+      yield executeMigration(migrations[version]);
       // Automated increment
       yield that.exec('UPDATE meta SET version = version + 1');
       version++;
     }
   });
 
+  this.upgradeDatabaseVersions = (versions) => co(function *() {
+    for (const version of versions) {
+      logger.debug("Upgrading from to v%s...", version, version + 1);
+      yield executeMigration(migrations[version]);
+    }
+  });
+
   this.getRow = () => that.sqlFindOne({ id: 1 });
 
   this.getVersion = () => co(function *() {
diff --git a/app/lib/dal/sqliteDAL/SandBox.js b/app/lib/dal/sqliteDAL/SandBox.js
new file mode 100644
index 0000000000000000000000000000000000000000..e452310a348976dac7abbd39de533f23d7a2819d
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/SandBox.js
@@ -0,0 +1,31 @@
+"use strict";
+
+const co = require('co');
+const colors = require('colors');
+const logger = require('../../logger')('sqlite');
+
+module.exports = SandBox;
+
+function SandBox(maxSize, findElements, compareElements) {
+
+  const that = this;
+  this.maxSize = maxSize || 10;
+  
+  this.acceptNewSandBoxEntry = (element, pubkey) => co(function *() {
+    if (element.pubkey === pubkey) {
+      return true;
+    }
+    const elements = yield findElements();
+    if (elements.length < that.maxSize) {
+      return true;
+    }
+    const lowestElement = elements[elements.length - 1];
+    const comparison = compareElements(element, lowestElement);
+    return comparison > 0;
+  });
+
+  this.getSandboxRoom = (underBlock) => co(function *() {
+    const elems = yield findElements();
+    return that.maxSize - elems.length;
+  });
+}
\ No newline at end of file
diff --git a/app/lib/dal/sqliteDAL/TxsDAL.js b/app/lib/dal/sqliteDAL/TxsDAL.js
index 19ce44c16c8578fe1844b61bb08bf250816d2133..1aa5660a11d6dd659fe7111bbc55af60c7a7a57e 100644
--- a/app/lib/dal/sqliteDAL/TxsDAL.js
+++ b/app/lib/dal/sqliteDAL/TxsDAL.js
@@ -7,6 +7,7 @@ const co = require('co');
 const moment = require('moment');
 const Transaction = require('../../entity/transaction');
 const AbstractSQLite = require('./AbstractSQLite');
+const SandBox = require('./SandBox');
 
 module.exports = TxsDAL;
 
@@ -36,7 +37,9 @@ function TxsDAL(db) {
     'issuers',
     'signatories',
     'signatures',
-    'recipients'
+    'recipients',
+    'output_base',
+    'output_amount'
   ];
   this.arrays = ['inputs','unlocks','outputs','issuers','signatories','signatures','recipients'];
   this.booleans = ['written','removed'];
@@ -145,4 +148,36 @@ function TxsDAL(db) {
       return that.exec(queries.join('\n'));
     }
   });
+
+  /**************************
+   * SANDBOX STUFF
+   */
+
+  this.getSandboxMemberships = () => that.query('SELECT ' +
+    '* ' +
+    'FROM ' + that.table + ' ' +
+    'WHERE NOT written ' +
+    'AND NOT removed ' +
+    'LIMIT ' + (that.sandbox.maxSize), []);
+
+  this.sandbox = new SandBox(30, this.getSandboxMemberships.bind(this), (compared, reference) => {
+    if (compared.output_base < reference.output_base) {
+      return -1;
+    }
+    else if (compared.output_base > reference.output_base) {
+      return 1;
+    }
+    else if (compared.output_amount > reference.output_amount) {
+      return -1;
+    }
+    else if (compared.output_amount < reference.output_amount) {
+      return 1;
+    }
+    else {
+      return 0;
+    }
+  });
+
+  this.getSandboxRoom = () => this.sandbox.getSandboxRoom();
+  this.setSandboxSize = (maxSize) => this.sandbox.maxSize = maxSize;
 }
diff --git a/app/lib/entity/transaction.js b/app/lib/entity/transaction.js
index 568267bba8c019d309e9ec2a52374be7787df98a..786bd9c08c346fe00a654ffe94a9845207037662 100644
--- a/app/lib/entity/transaction.js
+++ b/app/lib/entity/transaction.js
@@ -18,6 +18,10 @@ let Transaction = function(obj, currency) {
    this[key] = json[key];
   });
 
+  // Store the maximum output base
+  this.output_amount = this.outputs.reduce((sum, output) => sum + parseInt(output.split(':')[0]), 0);
+  this.output_base = this.outputs.reduce((maxBase, output) => Math.max(maxBase, parseInt(output.split(':')[1])), 0);
+
   this.version = constants.DOCUMENTS_VERSION;
   this.currency = currency || this.currency;
 
diff --git a/app/lib/helpers/http2raw.js b/app/lib/helpers/http2raw.js
index 28b3f994176a3c9727e2254cd1dc3ef2a284fea6..3b70063279cdc1182df50715cf62cd15512b9ba1 100644
--- a/app/lib/helpers/http2raw.js
+++ b/app/lib/helpers/http2raw.js
@@ -10,7 +10,8 @@ module.exports = {
   peer:          requiresParameter('peer',        constants.ERRORS.HTTP_PARAM_PEER_REQUIRED),
   membership:    Http2RawMembership,
   block:         requiresParameter('block',       constants.ERRORS.HTTP_PARAM_BLOCK_REQUIRED),
-  conf:          requiresParameter('conf',        constants.ERRORS.HTTP_PARAM_CONF_REQUIRED)
+  conf:          requiresParameter('conf',        constants.ERRORS.HTTP_PARAM_CONF_REQUIRED),
+  cpu:           requiresParameter('cpu',         constants.ERRORS.HTTP_PARAM_CPU_REQUIRED)
 };
 
 function requiresParameter(parameter, err) {
diff --git a/app/lib/logger/index.js b/app/lib/logger/index.js
index 5c149af281e894a24e4857ca05873d60c3374578..7ce20d16f7eefdec2452ca720c49c806acc07e86 100644
--- a/app/lib/logger/index.js
+++ b/app/lib/logger/index.js
@@ -33,7 +33,7 @@ const logger = new (winston.Logger)({
   transports: [
     // setup console logging
     new (winston.transports.Console)({
-      level: 'trace',
+      level: 'debug',
       levels: customLevels.levels,
       handleExceptions: false,
       colorize: true,
@@ -51,7 +51,7 @@ logger.addCallbackLogs = (callbackForLog) => {
     loggerAttached = true;
     logger.add(cbLogger, {
       callback: callbackForLog,
-      level: 'trace',
+      level: 'debug',
       levels: customLevels.levels,
       handleExceptions: false,
       colorize: true,
diff --git a/app/lib/streams/bma.js b/app/lib/streams/bma.js
index 310312985568b255b4840078e2122e745402d1b5..f7a7400b8afb45e133643e5bbe6db4e7a6486094 100644
--- a/app/lib/streams/bma.js
+++ b/app/lib/streams/bma.js
@@ -1,12 +1,7 @@
 "use strict";
 
-const co = require('co');
-const es = require('event-stream');
 const network = require('../system/network');
-const dtos = require('./dtos');
-const sanitize = require('./sanitize');
-
-let WebSocketServer = require('ws').Server;
+const routes = require('./routes');
 
 module.exports = function(server, interfaces, httpLogs) {
 
@@ -27,93 +22,8 @@ module.exports = function(server, interfaces, httpLogs) {
   }
 
   return network.createServersAndListen('Duniter server', interfaces, httpLogs, null, (app, httpMethods) => {
+    
+    routes.bma(server, '', app, httpMethods);
 
-    const node         = require('../../controllers/node')(server);
-    const blockchain   = require('../../controllers/blockchain')(server);
-    const net          = require('../../controllers/network')(server, server.conf);
-    const wot          = require('../../controllers/wot')(server);
-    const transactions = require('../../controllers/transactions')(server);
-    const dividend     = require('../../controllers/uds')(server);
-    httpMethods.httpGET(  '/node/summary',                          node.summary,                         dtos.Summary);
-    httpMethods.httpGET(  '/blockchain/parameters',                 blockchain.parameters,                dtos.Parameters);
-    httpMethods.httpPOST( '/blockchain/membership',                 blockchain.parseMembership,           dtos.Membership);
-    httpMethods.httpGET(  '/blockchain/memberships/:search',        blockchain.memberships,               dtos.Memberships);
-    httpMethods.httpPOST( '/blockchain/block',                      blockchain.parseBlock,                dtos.Block);
-    httpMethods.httpGET(  '/blockchain/block/:number',              blockchain.promoted,                  dtos.Block);
-    httpMethods.httpGET(  '/blockchain/blocks/:count/:from',        blockchain.blocks,                    dtos.Blocks);
-    httpMethods.httpGET(  '/blockchain/current',                    blockchain.current,                   dtos.Block);
-    httpMethods.httpGET(  '/blockchain/hardship/:search',           blockchain.hardship,                  dtos.Hardship);
-    httpMethods.httpGET(  '/blockchain/difficulties',               blockchain.difficulties,              dtos.Difficulties);
-    httpMethods.httpGET(  '/blockchain/with/newcomers',             blockchain.with.newcomers,            dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/certs',                 blockchain.with.certs,                dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/joiners',               blockchain.with.joiners,              dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/actives',               blockchain.with.actives,              dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/leavers',               blockchain.with.leavers,              dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/excluded',              blockchain.with.excluded,             dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/revoked',               blockchain.with.revoked,              dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/ud',                    blockchain.with.ud,                   dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/with/tx',                    blockchain.with.tx,                   dtos.Stat);
-    httpMethods.httpGET(  '/blockchain/branches',                   blockchain.branches,                  dtos.Branches);
-    httpMethods.httpGET(  '/network/peering',                       net.peer,                             dtos.Peer);
-    httpMethods.httpGET(  '/network/peering/peers',                 net.peersGet,                         dtos.MerkleOfPeers);
-    httpMethods.httpPOST( '/network/peering/peers',                 net.peersPost,                        dtos.Peer);
-    httpMethods.httpGET(  '/network/peers',                         net.peers,                            dtos.Peers);
-    httpMethods.httpPOST( '/wot/add',                               wot.add,                              dtos.Identity);
-    httpMethods.httpPOST( '/wot/certify',                           wot.certify,                          dtos.Cert);
-    httpMethods.httpPOST( '/wot/revoke',                            wot.revoke,                           dtos.Result);
-    httpMethods.httpGET(  '/wot/lookup/:search',                    wot.lookup,                           dtos.Lookup);
-    httpMethods.httpGET(  '/wot/members',                           wot.members,                          dtos.Members);
-    httpMethods.httpGET(  '/wot/requirements/:search',              wot.requirements,                     dtos.Requirements);
-    httpMethods.httpGET(  '/wot/certifiers-of/:search',             wot.certifiersOf,                     dtos.Certifications);
-    httpMethods.httpGET(  '/wot/certified-by/:search',              wot.certifiedBy,                      dtos.Certifications);
-    httpMethods.httpGET(  '/wot/identity-of/:search',               wot.identityOf,                       dtos.SimpleIdentity);
-    httpMethods.httpPOST( '/tx/process',                            transactions.parseTransaction,        dtos.Transaction);
-    httpMethods.httpGET(  '/tx/sources/:pubkey',                    transactions.getSources,              dtos.Sources);
-    httpMethods.httpGET(  '/tx/history/:pubkey',                    transactions.getHistory,              dtos.TxHistory);
-    httpMethods.httpGET(  '/tx/history/:pubkey/blocks/:from/:to',   transactions.getHistoryBetweenBlocks, dtos.TxHistory);
-    httpMethods.httpGET(  '/tx/history/:pubkey/times/:from/:to',    transactions.getHistoryBetweenTimes,  dtos.TxHistory);
-    httpMethods.httpGET(  '/tx/history/:pubkey/pending',            transactions.getPendingForPubkey,     dtos.TxHistory);
-    httpMethods.httpGET(  '/tx/pending',                            transactions.getPending,              dtos.TxPending);
-    httpMethods.httpGET(  '/ud/history/:pubkey',                    dividend.getHistory,                  dtos.UDHistory);
-    httpMethods.httpGET(  '/ud/history/:pubkey/blocks/:from/:to',   dividend.getHistoryBetweenBlocks,     dtos.UDHistory);
-    httpMethods.httpGET(  '/ud/history/:pubkey/times/:from/:to',    dividend.getHistoryBetweenTimes,      dtos.UDHistory);
-
-  }, (httpServer) => {
-
-    let currentBlock = {};
-    let wssBlock = new WebSocketServer({
-      server: httpServer,
-      path: '/ws/block'
-    });
-    let wssPeer = new WebSocketServer({
-      server: httpServer,
-      path: '/ws/peer'
-    });
-
-    wssBlock.on('connection', function connection(ws) {
-      co(function *() {
-        currentBlock = yield server.dal.getCurrentBlockOrNull();
-        if (currentBlock) {
-          ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block)));
-        }
-      });
-    });
-
-    wssBlock.broadcast = (data) => wssBlock.clients.forEach((client) => client.send(data));
-    wssPeer.broadcast = (data) => wssPeer.clients.forEach((client) => client.send(data));
-
-    // Forward blocks & peers
-    server
-      .pipe(es.mapSync(function(data) {
-        // Broadcast block
-        if (data.joiners) {
-          currentBlock = data;
-          wssBlock.broadcast(JSON.stringify(sanitize(currentBlock, dtos.Block)));
-        }
-        // Broadcast peer
-        if (data.endpoints) {
-          wssPeer.broadcast(JSON.stringify(sanitize(data, dtos.Peer)));
-        }
-      }));
-  });
+  }, routes.bmaWS(server, ''));
 };
diff --git a/app/lib/streams/dtos.js b/app/lib/streams/dtos.js
index d271c0260bb765120c8f199a694c102027bef284..06abf79fea7985142632b7cb4475f9ae7b0095c7 100644
--- a/app/lib/streams/dtos.js
+++ b/app/lib/streams/dtos.js
@@ -409,11 +409,16 @@ dtos.Boolean = {
   "success": Boolean
 };
 
+dtos.SummaryConf = {
+  "cpu": Number
+};
+
 dtos.AdminSummary = {
   "version": String,
   "host": String,
   "current": dtos.Block,
   "pubkey": String,
+  "conf": dtos.SummaryConf,
   "parameters": dtos.Parameters
 };
 
diff --git a/app/lib/streams/routes.js b/app/lib/streams/routes.js
new file mode 100644
index 0000000000000000000000000000000000000000..08ebb1e90b49a79f5870252d1d4e27ba3b87c90c
--- /dev/null
+++ b/app/lib/streams/routes.js
@@ -0,0 +1,228 @@
+"use strict";
+
+const co = require('co');
+const es = require('event-stream');
+const dtos = require('./dtos');
+const sanitize = require('./sanitize');
+const limiter = require('../system/limiter');
+const constants = require('../../lib/constants');
+const logger = require('../logger')('webmin');
+
+const WebSocketServer = require('ws').Server;
+
+module.exports = {
+
+  bma: function(server, prefix, app, httpMethods) {
+
+    const node         = require('../../controllers/node')(server);
+    const blockchain   = require('../../controllers/blockchain')(server);
+    const net          = require('../../controllers/network')(server, server.conf);
+    const wot          = require('../../controllers/wot')(server);
+    const transactions = require('../../controllers/transactions')(server);
+    const dividend     = require('../../controllers/uds')(server);
+    httpMethods.httpGET(  prefix + '/node/summary',                          node.summary,                         dtos.Summary,        limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/parameters',                 blockchain.parameters,                dtos.Parameters,     limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/blockchain/membership',                 blockchain.parseMembership,           dtos.Membership,     limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/memberships/:search',        blockchain.memberships,               dtos.Memberships,    limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/blockchain/block',                      blockchain.parseBlock,                dtos.Block,          limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/block/:number',              blockchain.promoted,                  dtos.Block,          limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/blocks/:count/:from',        blockchain.blocks,                    dtos.Blocks,         limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/current',                    blockchain.current,                   dtos.Block,          limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/hardship/:search',           blockchain.hardship,                  dtos.Hardship,       limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/difficulties',               blockchain.difficulties,              dtos.Difficulties,   limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/newcomers',             blockchain.with.newcomers,            dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/certs',                 blockchain.with.certs,                dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/joiners',               blockchain.with.joiners,              dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/actives',               blockchain.with.actives,              dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/leavers',               blockchain.with.leavers,              dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/excluded',              blockchain.with.excluded,             dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/revoked',               blockchain.with.revoked,              dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/ud',                    blockchain.with.ud,                   dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/with/tx',                    blockchain.with.tx,                   dtos.Stat,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/blockchain/branches',                   blockchain.branches,                  dtos.Branches,       limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/network/peering',                       net.peer,                             dtos.Peer,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/network/peering/peers',                 net.peersGet,                         dtos.MerkleOfPeers,  limiter.limitAsVeryHighUsage());
+    httpMethods.httpPOST( prefix + '/network/peering/peers',                 net.peersPost,                        dtos.Peer,           limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/network/peers',                         net.peers,                            dtos.Peers,          limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/wot/add',                               wot.add,                              dtos.Identity,       limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/wot/certify',                           wot.certify,                          dtos.Cert,           limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/wot/revoke',                            wot.revoke,                           dtos.Result,         limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/lookup/:search',                    wot.lookup,                           dtos.Lookup,         limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/members',                           wot.members,                          dtos.Members,        limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/requirements/:search',              wot.requirements,                     dtos.Requirements,   limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/certifiers-of/:search',             wot.certifiersOf,                     dtos.Certifications, limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/certified-by/:search',              wot.certifiedBy,                      dtos.Certifications, limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/wot/identity-of/:search',               wot.identityOf,                       dtos.SimpleIdentity, limiter.limitAsHighUsage());
+    httpMethods.httpPOST( prefix + '/tx/process',                            transactions.parseTransaction,        dtos.Transaction,    limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/sources/:pubkey',                    transactions.getSources,              dtos.Sources,        limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/history/:pubkey',                    transactions.getHistory,              dtos.TxHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/history/:pubkey/blocks/:from/:to',   transactions.getHistoryBetweenBlocks, dtos.TxHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/history/:pubkey/times/:from/:to',    transactions.getHistoryBetweenTimes,  dtos.TxHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/history/:pubkey/pending',            transactions.getPendingForPubkey,     dtos.TxHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/tx/pending',                            transactions.getPending,              dtos.TxPending,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/ud/history/:pubkey',                    dividend.getHistory,                  dtos.UDHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/ud/history/:pubkey/blocks/:from/:to',   dividend.getHistoryBetweenBlocks,     dtos.UDHistory,      limiter.limitAsHighUsage());
+    httpMethods.httpGET(  prefix + '/ud/history/:pubkey/times/:from/:to',    dividend.getHistoryBetweenTimes,      dtos.UDHistory,      limiter.limitAsHighUsage());
+  },
+  
+  webmin: function(webminCtrl, app, httpMethods) {
+    httpMethods.httpGET(  '/webmin/summary',                   webminCtrl.summary, dtos.AdminSummary);
+    httpMethods.httpPOST( '/webmin/key/preview',               webminCtrl.previewPubkey, dtos.PreviewPubkey);
+    httpMethods.httpGET(  '/webmin/server/http/start',         webminCtrl.startHTTP, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/http/stop',          webminCtrl.stopHTTP,  dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/http/upnp/open',     webminCtrl.openUPnP,  dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/http/upnp/regular',  webminCtrl.regularUPnP,  dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/preview_next',       webminCtrl.previewNext,  dtos.Block);
+    httpMethods.httpPOST( '/webmin/server/send_conf',          webminCtrl.sendConf, dtos.Identity);
+    httpMethods.httpPOST( '/webmin/server/net_conf',           webminCtrl.applyNetworkConf, dtos.Boolean);
+    httpMethods.httpPOST( '/webmin/server/key_conf',           webminCtrl.applyNewKeyConf, dtos.Boolean);
+    httpMethods.httpPOST( '/webmin/server/cpu_conf',           webminCtrl.applyCPUConf, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/republish_selfpeer', webminCtrl.publishANewSelfPeer, dtos.Boolean);
+    httpMethods.httpPOST( '/webmin/server/test_sync',          webminCtrl.testPeer, dtos.Block);
+    httpMethods.httpPOST( '/webmin/server/start_sync',         webminCtrl.startSync, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/auto_conf_network',  webminCtrl.autoConfNetwork, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/services/start_all', webminCtrl.startAllServices, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/services/stop_all',  webminCtrl.stopAllServices, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/server/reset/data',         webminCtrl.resetData, dtos.Boolean);
+    httpMethods.httpGET(  '/webmin/network/interfaces',        webminCtrl.listInterfaces, dtos.NetworkInterfaces);
+    httpMethods.httpGETFile('/webmin/data/duniter_export',     webminCtrl.exportData);
+    httpMethods.httpPOST( '/webmin/data/duniter_import',       webminCtrl.importData);
+  },
+  
+  bmaWS: function(server, prefix) {
+    return (httpServer) => {
+
+      let currentBlock = {};
+      let wssBlock = new WebSocketServer({
+        server: httpServer,
+        path: prefix + '/ws/block'
+      });
+      let wssPeer = new WebSocketServer({
+        server: httpServer,
+        path: prefix + '/ws/peer'
+      });
+
+      wssBlock.on('connection', function connection(ws) {
+        co(function *() {
+          currentBlock = yield server.dal.getCurrentBlockOrNull();
+          if (currentBlock) {
+            ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block)));
+          }
+        });
+      });
+
+      wssBlock.broadcast = (data) => wssBlock.clients.forEach((client) => client.send(data));
+      wssPeer.broadcast = (data) => wssPeer.clients.forEach((client) => client.send(data));
+
+      // Forward blocks & peers
+      server
+        .pipe(es.mapSync(function(data) {
+          // Broadcast block
+          if (data.joiners) {
+            currentBlock = data;
+            wssBlock.broadcast(JSON.stringify(sanitize(currentBlock, dtos.Block)));
+          }
+          // Broadcast peer
+          if (data.endpoints) {
+            wssPeer.broadcast(JSON.stringify(sanitize(data, dtos.Peer)));
+          }
+        }));
+    };
+  },
+
+  webminWS: function(webminCtrl) {
+    return (httpServer) => {
+
+      // Socket for synchronization events
+      let wssEvents = new WebSocketServer({
+        server: httpServer,
+        path: '/webmin/ws'
+      });
+
+      let lastLogs = [];
+      wssEvents.on('connection', function connection(ws) {
+
+        ws.on('message', () => {
+          wssEvents.broadcast(JSON.stringify({
+            type: 'log',
+            value: lastLogs
+          }));
+        });
+
+        wssEvents.broadcast(JSON.stringify({
+          type: 'log',
+          value: lastLogs
+        }));
+
+        // The callback which write each new log message to websocket
+        logger.addCallbackLogs((level, msg, timestamp) => {
+          lastLogs.splice(0, Math.max(0, lastLogs.length - constants.WEBMIN_LOGS_CACHE + 1));
+          lastLogs.push({
+            timestamp: timestamp,
+            level: level,
+            msg: msg
+          });
+          wssEvents.broadcast(JSON.stringify({
+            type: 'log',
+            value: [{
+              timestamp: timestamp,
+              level: level,
+              msg: msg
+            }]
+          }));
+        });
+      });
+
+      wssEvents.broadcast = (data) => wssEvents.clients.forEach((client) => {
+        try {
+          client.send(data);
+        } catch (e) {
+          console.log(e);
+        }
+      });
+
+      // Forward blocks & peers
+      webminCtrl
+        .pipe(es.mapSync(function(data) {
+          // Broadcast block
+          if (data.download !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'download',
+              value: data.download
+            }));
+          }
+          if (data.applied !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'applied',
+              value: data.applied
+            }));
+          }
+          if (data.sync !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'sync',
+              value: data.sync,
+              msg: (data.msg && (data.msg.message || data.msg))
+            }));
+          }
+          if (data.started !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'started',
+              value: data.started
+            }));
+          }
+          if (data.stopped !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'stopped',
+              value: data.stopped
+            }));
+          }
+          if (data.pulling !== undefined) {
+            wssEvents.broadcast(JSON.stringify({
+              type: 'pulling',
+              value: data.pulling
+            }));
+          }
+        }));
+    };
+  }
+};
diff --git a/app/lib/streams/webmin.js b/app/lib/streams/webmin.js
index 5aac53edb7c3485b8822ca0e31f5e480ce5affed..7702503801d90ef8ed091408eaf3f1b7f53f067e 100644
--- a/app/lib/streams/webmin.js
+++ b/app/lib/streams/webmin.js
@@ -1,11 +1,11 @@
 "use strict";
 
 const path = require('path');
-const es = require('event-stream');
-const constants = require('../../lib/constants');
+const routes = require('./routes');
 const network = require('../system/network');
 const dtos = require('../../lib/streams/dtos');
-const logger = require('../logger')('webmin');
+
+const ENABLE_FILE_UPLOAD = true;
 
 let WebSocketServer = require('ws').Server;
 
@@ -17,116 +17,13 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) {
 
   let httpLayer = network.createServersAndListen('Duniter web admin', interfaces, httpLogs, fullPath, (app, httpMethods) => {
 
-    httpMethods.httpGET(  '/webmin/summary',                   webminCtrl.summary, dtos.AdminSummary);
-    httpMethods.httpPOST( '/webmin/key/preview',               webminCtrl.previewPubkey, dtos.PreviewPubkey);
-    httpMethods.httpGET(  '/webmin/server/http/start',         webminCtrl.startHTTP, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/stop',          webminCtrl.stopHTTP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/upnp/open',     webminCtrl.openUPnP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/http/upnp/regular',  webminCtrl.regularUPnP,  dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/preview_next',       webminCtrl.previewNext,  dtos.Block);
-    httpMethods.httpPOST( '/webmin/server/send_conf',          webminCtrl.sendConf, dtos.Identity);
-    httpMethods.httpPOST( '/webmin/server/net_conf',           webminCtrl.applyNetworkConf, dtos.Boolean);
-    httpMethods.httpPOST( '/webmin/server/key_conf',           webminCtrl.applyNewKeyConf, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/republish_selfpeer', webminCtrl.publishANewSelfPeer, dtos.Boolean);
-    httpMethods.httpPOST( '/webmin/server/start_sync',         webminCtrl.startSync, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/auto_conf_network',  webminCtrl.autoConfNetwork, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/services/start_all', webminCtrl.startAllServices, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/services/stop_all',  webminCtrl.stopAllServices, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/server/reset/data',         webminCtrl.resetData, dtos.Boolean);
-    httpMethods.httpGET(  '/webmin/network/interfaces',        webminCtrl.listInterfaces, dtos.NetworkInterfaces);
-  }, (httpServer) => {
-
-    // Socket for synchronization events
-    let wssEvents = new WebSocketServer({
-      server: httpServer,
-      path: '/webmin/ws'
-    });
-
-    let lastLogs = [];
-    wssEvents.on('connection', function connection(ws) {
-
-      ws.on('message', () => {
-        wssEvents.broadcast(JSON.stringify({
-          type: 'log',
-          value: lastLogs
-        }));
-      });
+    routes.bma(webminCtrl.server, '/bma', app, httpMethods);
+    routes.webmin(webminCtrl, app, httpMethods);
 
-      wssEvents.broadcast(JSON.stringify({
-        type: 'log',
-        value: lastLogs
-      }));
-
-      // The callback which write each new log message to websocket
-      logger.addCallbackLogs((level, msg, timestamp) => {
-        lastLogs.splice(0, Math.max(0, lastLogs.length - constants.WEBMIN_LOGS_CACHE + 1));
-        lastLogs.push({
-          timestamp: timestamp,
-          level: level,
-          msg: msg
-        });
-        wssEvents.broadcast(JSON.stringify({
-          type: 'log',
-          value: [{
-            timestamp: timestamp,
-            level: level,
-            msg: msg
-          }]
-        }));
-      });
-    });
-
-    wssEvents.broadcast = (data) => wssEvents.clients.forEach((client) => {
-      try {
-        client.send(data);
-      } catch (e) {
-        console.log(e);
-      }
-    });
-
-    // Forward blocks & peers
-    webminCtrl
-      .pipe(es.mapSync(function(data) {
-        // Broadcast block
-        if (data.download !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'download',
-            value: data.download
-          }));
-        }
-        if (data.applied !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'applied',
-            value: data.applied
-          }));
-        }
-        if (data.sync !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'sync',
-            value: data.sync,
-            msg: (data.msg && (data.msg.message || data.msg))
-          }));
-        }
-        if (data.started !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'started',
-            value: data.started
-          }));
-        }
-        if (data.stopped !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'stopped',
-            value: data.stopped
-          }));
-        }
-        if (data.pulling !== undefined) {
-          wssEvents.broadcast(JSON.stringify({
-            type: 'pulling',
-            value: data.pulling
-          }));
-        }
-      }));
-  });
+  }, (httpServer) => {
+    routes.bmaWS(webminCtrl.server, '/bma')(httpServer);
+    routes.webminWS(webminCtrl)(httpServer);
+  }, ENABLE_FILE_UPLOAD);
 
   return {
     httpLayer: httpLayer,
diff --git a/app/lib/sync.js b/app/lib/sync.js
index 6b06a7a4c9e4b97278d556ef18b6bd12aa2c1133..ca12ad5352ce1cc033b1369121f3964cdd2cae6c 100644
--- a/app/lib/sync.js
+++ b/app/lib/sync.js
@@ -79,6 +79,14 @@ function Synchroniser (server, host, port, conf, interactive) {
     }
   });
 
+  this.test = (to, chunkLen, askedCautious, nopeers) => co(function*() {
+    const vucoin = yield getVucoin(host, port, vucoinOptions);
+    const peering = yield Q.nfcall(vucoin.network.peering.get);
+    const peer = new Peer(peering);
+    const node = yield peer.connect();
+    return Q.nfcall(node.blockchain.current);
+  });
+
   this.sync = (to, chunkLen, askedCautious, nopeers) => co(function*() {
 
     try {
@@ -219,7 +227,6 @@ function Synchroniser (server, host, port, conf, interactive) {
             });
             entry.signature = sign;
             watcher.writeStatus('Peer ' + entry.pubkey);
-            logger.info('Peer ' + entry.pubkey);
             yield PeeringService.submitP(entry, false, to === undefined);
           }
         }
diff --git a/app/lib/system/limiter.js b/app/lib/system/limiter.js
new file mode 100644
index 0000000000000000000000000000000000000000..29959e83dbb7063e0733f724d5e14c46ce2cb479
--- /dev/null
+++ b/app/lib/system/limiter.js
@@ -0,0 +1,114 @@
+"use strict";
+
+const A_MINUTE = 60 * 1000;
+const A_SECOND = 1000;
+
+const Limiter = {
+
+  /**
+   * Tells wether the quota is reached at current time or not.
+   */
+  canAnswerNow() {
+    // Rapid decision first.
+    // Note: we suppose limitPerSecond < limitPerMinute
+    if (this.reqsSecLen < this.limitPerSecond && this.reqsMinLen < this.limitPerMinute) {
+      return true;
+    }
+    this.updateRequests();
+    return this.reqsSecLen < this.limitPerSecond && this.reqsMinLen < this.limitPerMinute;
+  },
+
+  /**
+   * Filter the current requests stock to remove the too old ones
+   */
+  updateRequests() {
+    // Clean current requests stock and make the test again
+    const now = Date.now();
+    let i = 0, reqs = this.reqsMin, len = this.reqsMinLen;
+    // Reinit specific indicators
+    this.reqsSec = [];
+    this.reqsMin = [];
+    while (i < len) {
+      const duration = now - reqs[i];
+      if (duration < A_SECOND) {
+        this.reqsSec.push(reqs[i]);
+      }
+      if (duration < A_MINUTE) {
+        this.reqsMin.push(reqs[i]);
+      }
+      i++;
+    }
+    this.reqsSecLen = this.reqsSec.length;
+    this.reqsMinLen = this.reqsMin.length;
+  },
+  
+  processRequest() {
+    const now = Date.now();
+    this.reqsSec.push(now);
+    this.reqsSecLen++;
+    this.reqsMin.push(now);
+    this.reqsMinLen++;
+  }
+};
+
+let HIGH_USAGE_STRATEGY = Object.create(Limiter);
+HIGH_USAGE_STRATEGY.limitPerSecond = 10;
+HIGH_USAGE_STRATEGY.limitPerMinute = 300;
+
+let VERY_HIGH_USAGE_STRATEGY = Object.create(Limiter);
+VERY_HIGH_USAGE_STRATEGY.limitPerSecond = 30;
+VERY_HIGH_USAGE_STRATEGY.limitPerMinute = 30 * 60; // Limit is only per second
+
+let TEST_STRATEGY = Object.create(Limiter);
+TEST_STRATEGY.limitPerSecond = 5;
+TEST_STRATEGY.limitPerMinute = 6;
+
+let NO_LIMIT_STRATEGY = Object.create(Limiter);
+NO_LIMIT_STRATEGY.limitPerSecond = 1000000;
+NO_LIMIT_STRATEGY.limitPerMinute = 1000000 * 60;
+
+let disableLimits = false;
+
+module.exports = {
+  
+  limitAsHighUsage() {
+    return disableLimits ? createObject(NO_LIMIT_STRATEGY) : createObject(HIGH_USAGE_STRATEGY);
+  },
+
+  limitAsVeryHighUsage() {
+    return disableLimits ? createObject(NO_LIMIT_STRATEGY) : createObject(VERY_HIGH_USAGE_STRATEGY);
+  },
+
+  limitAsUnlimited() {
+    return createObject(NO_LIMIT_STRATEGY);
+  },
+
+  limitAsTest() {
+    return disableLimits ? createObject(NO_LIMIT_STRATEGY) : createObject(TEST_STRATEGY);
+  },
+
+  noLimit() {
+    disableLimits = true;
+  },
+
+  withLimit() {
+    disableLimits = false;
+  }
+};
+
+function createObject(strategy) {
+
+  const obj = Object.create(strategy);
+
+  // Stock of request times
+  obj.reqsSec = [];
+
+    // The length of reqs.
+    // It is better to have it instead of calling reqs.length
+  obj.reqsSecLen = 0;
+
+    // Minute specific
+  obj.reqsMin = [];
+  obj.reqsMinLen = 0;
+  return obj;
+}
\ No newline at end of file
diff --git a/app/lib/system/network.js b/app/lib/system/network.js
index 8cefa8547039878a10d04badebc853f76e5adacf..6bdd5e4ec18ffd1e5183ddb3160fc025b1dcb983 100644
--- a/app/lib/system/network.js
+++ b/app/lib/system/network.js
@@ -11,6 +11,7 @@ const morgan = require('morgan');
 const errorhandler = require('errorhandler');
 const bodyParser = require('body-parser');
 const cors = require('cors');
+const fileUpload = require('express-fileupload');
 const constants = require('../constants');
 const sanitize = require('../streams/sanitize');
 const logger = require('../logger')('network');
@@ -74,7 +75,7 @@ module.exports = {
     }
   },
 
-  createServersAndListen: (name, interfaces, httpLogs, staticPath, routingCallback, listenWebSocket) => co(function *() {
+  createServersAndListen: (name, interfaces, httpLogs, staticPath, routingCallback, listenWebSocket, enableFileUpload) => co(function *() {
 
     const app = express();
 
@@ -92,6 +93,11 @@ module.exports = {
     // CORS for **any** HTTP request
     app.use(cors());
 
+    if (enableFileUpload) {
+      // File upload for backup API
+      app.use(fileUpload());
+    }
+
     app.use(bodyParser.urlencoded({
       extended: true
     }));
@@ -103,8 +109,9 @@ module.exports = {
     }
 
     routingCallback(app, {
-      httpGET:  (uri, promiseFunc, dtoContract) => handleRequest(app.get.bind(app), uri, promiseFunc, dtoContract),
-      httpPOST: (uri, promiseFunc, dtoContract) => handleRequest(app.post.bind(app), uri, promiseFunc, dtoContract)
+      httpGET:     (uri, promiseFunc, dtoContract, limiter) => handleRequest(app.get.bind(app), uri, promiseFunc, dtoContract, limiter),
+      httpPOST:    (uri, promiseFunc, dtoContract, limiter) => handleRequest(app.post.bind(app), uri, promiseFunc, dtoContract, limiter),
+      httpGETFile: (uri, promiseFunc, dtoContract, limiter) => handleFileRequest(app.get.bind(app), uri, promiseFunc, limiter)
     });
 
     if (staticPath) {
@@ -204,12 +211,17 @@ module.exports = {
   })
 };
 
-const handleRequest = (method, uri, promiseFunc, dtoContract) => {
+const handleRequest = (method, uri, promiseFunc, dtoContract, theLimiter) => {
+  const limiter = theLimiter || require('../system/limiter').limitAsUnlimited();
   method(uri, function(req, res) {
     res.set('Access-Control-Allow-Origin', '*');
     res.type('application/json');
     co(function *() {
       try {
+        if (!limiter.canAnswerNow()) {
+          throw constants.ERRORS.HTTP_LIMITATION;
+        }
+        limiter.processRequest();
         let result = yield promiseFunc(req);
         // Ensure of the answer format
         result = sanitize(result, dtoContract);
@@ -223,7 +235,30 @@ const handleRequest = (method, uri, promiseFunc, dtoContract) => {
       }
     });
   });
-}
+};
+
+const handleFileRequest = (method, uri, promiseFunc, theLimiter) => {
+  const limiter = theLimiter || require('../system/limiter').limitAsUnlimited();
+  method(uri, function(req, res) {
+    res.set('Access-Control-Allow-Origin', '*');
+    co(function *() {
+      try {
+        if (!limiter.canAnswerNow()) {
+          throw constants.ERRORS.HTTP_LIMITATION;
+        }
+        limiter.processRequest();
+        let fileStream = yield promiseFunc(req);
+        // HTTP answer
+        fileStream.pipe(res);
+      } catch (e) {
+        let error = getResultingError(e);
+        // HTTP error
+        res.status(error.httpCode).send(JSON.stringify(error.uerr, null, "  "));
+        throw e
+      }
+    });
+  });
+};
 
 function getResultingError(e) {
   // Default is 500 unknown error
diff --git a/app/service/BlockchainService.js b/app/service/BlockchainService.js
index 9f216f31d85dd22d830f7d53ce457493a9baa9e0..e6fd7160ef13c019316449733d2d619e1f6ef56f 100644
--- a/app/service/BlockchainService.js
+++ b/app/service/BlockchainService.js
@@ -518,13 +518,6 @@ function BlockchainService () {
     return dal.pushStats(stats);
   }
 
-  this.obsoleteInMainBranch = (block) => co(function*(){
-    // Compute obsolete links
-    yield mainContext.computeObsoleteLinks(block);
-    // Compute obsolete memberships (active, joiner)
-    yield mainContext.computeObsoleteMemberships(block);
-  });
-
   this.getCertificationsExludingBlock = () => co(function*() {
     try {
       const current = yield dal.getCurrentBlockOrNull();
diff --git a/app/service/IdentityService.js b/app/service/IdentityService.js
index f3f9cea4b2303b833b0dcdcecd9da0e996b5389b..f61a815e7dc18e22c91b3ea0933b9af5b0c1060c 100644
--- a/app/service/IdentityService.js
+++ b/app/service/IdentityService.js
@@ -10,6 +10,8 @@ const Revocation      = require('../../app/lib/entity/revocation');
 const AbstractService = require('./AbstractService');
 const co              = require('co');
 
+const BY_ABSORPTION = true;
+
 module.exports = () => {
   return new IdentityService();
 };
@@ -62,7 +64,7 @@ function IdentityService () {
 
   this.getPendingFromPubkey = (pubkey) => dal.getNonWritten(pubkey);
 
-  this.submitIdentity = (obj) => {
+  this.submitIdentity = (obj, byAbsorption) => {
     let idty = new Identity(obj);
     // Force usage of local currency name, do not accept other currencies documents
     idty.currency = conf.currency || idty.currency;
@@ -89,6 +91,13 @@ function IdentityService () {
           throw constants.ERRORS.UID_ALREADY_USED;
         }
         idty = new Identity(idty);
+        if (byAbsorption === BY_ABSORPTION) {
+          idty.certsCount = 1;
+        }
+        idty.ref_block = parseInt(idty.buid.split('-')[0]);
+        if (!(yield dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, conf.pair && conf.pair.pub))) {
+          throw constants.ERRORS.SANDBOX_FOR_IDENTITY_IS_FULL;
+        }
         yield dal.savePendingIdentity(idty);
         logger.info('✔ IDTY %s %s', idty.pubkey, idty.uid);
         return idty;
@@ -113,7 +122,7 @@ function IdentityService () {
         uid: cert.idty_uid,
         buid: cert.idty_buid,
         sig: cert.idty_sig
-      });
+      }, BY_ABSORPTION);
     }
     return that.pushFIFO(() => co(function *() {
       logger.info('⬇ CERT %s block#%s -> %s', cert.from, cert.block_number, idty.uid);
@@ -141,14 +150,11 @@ function IdentityService () {
         });
         let existingCert = yield dal.existsCert(mCert);
         if (!existingCert) {
-          try {
-            yield dal.registerNewCertification(new Certification(mCert));
-            logger.info('✔ CERT %s', mCert.from);
-          } catch (e) {
-            // TODO: This is weird...
-            logger.error(e);
-            logger.info('✔ CERT %s', mCert.from);
+          if (!(yield dal.certDAL.sandbox.acceptNewSandBoxEntry(mCert, conf.pair && conf.pair.pub))) {
+            throw constants.ERRORS.SANDBOX_FOR_CERT_IS_FULL;
           }
+          yield dal.registerNewCertification(new Certification(mCert));
+          logger.info('✔ CERT %s', mCert.from);
         }
       } else {
         logger.info('✘ CERT %s %s', cert.from, cert.err);
@@ -185,6 +191,11 @@ function IdentityService () {
         // Create identity given by the revocation
         const idty = new Identity(revoc);
         idty.revocation_sig = revoc.signature;
+        idty.certsCount = 0;
+        idty.ref_block = parseInt(idty.buid.split('-')[0]);
+        if (!(yield dal.idtyDAL.sandbox.acceptNewSandBoxEntry(idty, conf.pair && conf.pair.pub))) {
+          throw constants.ERRORS.SANDBOX_FOR_IDENTITY_IS_FULL;
+        }
         yield dal.savePendingIdentity(idty);
         return jsonResultTrue();
       }
diff --git a/app/service/MembershipService.js b/app/service/MembershipService.js
index 42679cf97d9a12ad0d3d22efee78ba5f00e4a8c8..04ad95c41b39ab9e23a2860b5281805f27903d03 100644
--- a/app/service/MembershipService.js
+++ b/app/service/MembershipService.js
@@ -47,6 +47,10 @@ function MembershipService () {
     }
     const current = yield dal.getCurrentBlockOrNull();
     yield rules.HELPERS.checkMembershipBlock(entry, current, conf, dal);
+    entry.pubkey = entry.issuer;
+    if (!(yield dal.msDAL.sandbox.acceptNewSandBoxEntry(entry, conf.pair && conf.pair.pub))) {
+      throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL;
+    }
     // Saves entry
     yield dal.savePendingMembership(entry);
     logger.info('✔ %s %s', entry.issuer, entry.membership);
diff --git a/app/service/TransactionsService.js b/app/service/TransactionsService.js
index ab49618eda38b67f4439d0fda432db683f5154df..9769f611f9e3ebe677ab7bdb056b713e0a253f33 100644
--- a/app/service/TransactionsService.js
+++ b/app/service/TransactionsService.js
@@ -2,7 +2,7 @@
 
 const co              = require('co');
 const Q               = require('q');
-const moment          = require('moment');
+const constants       = require('../lib/constants');
 const rules           = require('../lib/rules');
 const Transaction     = require('../lib/entity/transaction');
 const AbstractService = require('./AbstractService');
@@ -34,6 +34,11 @@ function TransactionService () {
     const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
     yield Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(transaction);
     yield rules.HELPERS.checkSingleTransaction(transaction, nextBlockWithFakeTimeVariation, conf, dal);
+    const server_pubkey = conf.pair && conf.pair.pub;
+    transaction.pubkey = transaction.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : '';
+    if (!(yield dal.txsDAL.sandbox.acceptNewSandBoxEntry(transaction, server_pubkey))) {
+      throw constants.ERRORS.SANDBOX_FOR_TRANSACTION_IS_FULL;
+    }
     yield dal.saveTransaction(tx);
     return tx;
   }));
diff --git a/appveyor.yml b/appveyor.yml
index 713181df657dabd51aac296d3863c67935caea4a..5743f1d03afb1beac4e135353a6959e985e743f7 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -79,7 +79,7 @@ artifacts:
     name: Duniter
 
 deploy:
-  release: v0.21.3
+  release: v0.22.0
   provider: GitHub
   auth_token:
     secure: Vp/M0r0i1yhGR2nhrPWEbTiDIF6r0cmwbNDFZUzdFe5clWxPXtuC0lgIpOQI78zt
diff --git a/bin/duniter b/bin/duniter
old mode 100644
new mode 100755
diff --git a/gui/index.html b/gui/index.html
index e8d7f917b952a261b9e67bc9b850cfa2dcfd6d0e..f1ae1a4c9d049ca65307017018d465d99b9150d9 100644
--- a/gui/index.html
+++ b/gui/index.html
@@ -3,7 +3,7 @@
 <head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <title>Duniter 0.21.3</title>
+  <title>Duniter 0.22.0</title>
   <style>
     html {
       font-family: "Courier New", Courier, monospace;
diff --git a/gui/package.json b/gui/package.json
index 51fc1861f7c8b2ee2913233628b1c9bf9b68c8bd..fbda2444a104a9c9986560972518852fd2a09640 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -1,10 +1,10 @@
 {
-  "name": "v0.21.3",
+  "name": "v0.22.0",
   "main": "index.html",
   "node-main": "../sources/bin/duniter",
   "window": {
     "icon": "duniter.png",
-    "title": "v0.21.3",
+    "title": "v0.22.0",
     "width": 800,
     "height": 800,
     "min_width": 750,
diff --git a/install.sh b/install.sh
index 466e556215c85e6a32c24f0fbc048da5392c3ad9..52a44d82462a7143fd21eabe0a18ae82015b24a3 100755
--- a/install.sh
+++ b/install.sh
@@ -11,7 +11,7 @@ if [ -z "$DUNITER_DIR" ]; then
 fi
 
 latest_version() {
-  echo "v0.21.3"
+  echo "v0.22.0"
 }
 
 repo_url() {
diff --git a/package.json b/package.json
index 4e6ec1037dc05ac23d93ba8c9d8fd00791b295ca..59c409d2ab2a4789c989148c79505e5908a23cf6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "duniter",
-  "version": "0.21.3",
+  "version": "0.22.0",
   "engines": {
     "node": ">=4.2.0",
     "npm": ">=2.11"
@@ -34,6 +34,7 @@
     "url": "https://github.com/duniter/duniter/issues"
   },
   "dependencies": {
+    "archiver": "1.0.1",
     "async": "1.5.2",
     "bindings": "1.2.1",
     "body-parser": "1.14.2",
@@ -46,6 +47,7 @@
     "event-stream": "3.1.5",
     "express": "4.13.4",
     "express-cors": "0.0.3",
+    "express-fileupload": "0.0.5",
     "inquirer": "0.8.5",
     "jison": "0.4.17",
     "merkle": "0.4.0",
@@ -65,6 +67,8 @@
     "superagent": "1.4.0",
     "tweetnacl": "0.14.1",
     "underscore": "1.8.3",
+    "unzip": "0.1.11",
+    "unzip2": "0.2.5",
     "vucoin": "0.30.4",
     "winston": "2.1.1",
     "wotb": "0.4.10",
diff --git a/server.js b/server.js
index a50185af47e18f75ffdb2d77eec390f159fee477..63b33da9359fd777133003c9c03495e90deb39fb 100644
--- a/server.js
+++ b/server.js
@@ -6,6 +6,9 @@ const path        = require('path');
 const co          = require('co');
 const _           = require('underscore');
 const Q           = require('q');
+const archiver    = require('archiver');
+const unzip       = require('unzip2');
+const fs          = require('fs');
 const parsers     = require('./app/lib/streams/parsers');
 const constants   = require('./app/lib/constants');
 const fileDAL     = require('./app/lib/dal/fileDAL');
@@ -29,6 +32,7 @@ function Server (dbConf, overrideConf) {
   const paramsP = directory.getHomeParams(dbConf && dbConf.memory, home);
   const logger = require('./app/lib/logger')('server');
   const that = this;
+  that.home = home;
   that.conf = null;
   that.dal = null;
   that.version = jsonpckg.version;
@@ -73,6 +77,11 @@ function Server (dbConf, overrideConf) {
     that.dal = fileDAL(params);
   });
 
+  this.unplugFileSystem = () => co(function *() {
+    logger.debug('Unplugging file system...');
+    yield that.dal.close();
+  });
+
   this.softResetData = () => co(function *() {
     logger.debug('Soft data reset... [cache]');
     yield that.dal.cleanCaches();
@@ -269,8 +278,18 @@ function Server (dbConf, overrideConf) {
       }
   });
 
+  this.resetHome = () => co(function *() {
+    const params = yield paramsP;
+    const myFS = params.fs;
+    const rootPath = params.home;
+    const existsDir = yield myFS.exists(rootPath);
+    if (existsDir) {
+      yield myFS.removeTree(rootPath);
+    }
+  });
+
   this.resetAll = (done) => {
-    const files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE];
+    const files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE, 'export.zip', 'import.zip'];
     const dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
     return resetFiles(files, dirs, done);
   };
@@ -299,6 +318,35 @@ function Server (dbConf, overrideConf) {
     return that.dal.resetPeers(done);
   };
 
+  this.exportAllDataAsZIP = () => co(function *() {
+    const params = yield paramsP;
+    const rootPath = params.home;
+    const myFS = params.fs;
+    const archive = archiver('zip');
+    if (yield myFS.exists(path.join(rootPath, 'indicators'))) {
+      archive.directory(path.join(rootPath, 'indicators'), '/indicators', undefined, { name: 'indicators'});
+    }
+    const files = ['duniter.db', 'stats.json', 'wotb.bin'];
+    for (const file of files) {
+      if (yield myFS.exists(path.join(rootPath, file))) {
+        archive.file(path.join(rootPath, file), { name: file });
+      }
+    }
+    archive.finalize();
+    return archive;
+  });
+
+  this.importAllDataFromZIP = (zipFile) => co(function *() {
+    const params = yield paramsP;
+    yield that.resetData();
+    const output = unzip.Extract({ path: params.home });
+    fs.createReadStream(zipFile).pipe(output);
+    return new Promise((resolve, reject) => {
+      output.on('error', reject);
+      output.on('close', resolve);
+    });
+  });
+
   this.cleanDBData = () => co(function *() {
     yield _.values(that.dal.newDals).map((dal) => dal.cleanData && dal.cleanData());
     that.dal.wotb.resetWoT();
@@ -398,6 +446,11 @@ function Server (dbConf, overrideConf) {
       syncPromise: syncPromise
     };
   };
+  
+  this.testForSync = (onHost, onPort) => {
+    const remote = new Synchroniser(that, onHost, onPort);
+    return remote.test();
+  };
 
   /**
    * Enable routing features:
diff --git a/test/dal/dal.js b/test/dal/dal.js
index 77f6a98982b4f8fd0d2e31746ede74256c6b5d0d..f15471e10d24017706d2edfa7727de866a0acab4 100644
--- a/test/dal/dal.js
+++ b/test/dal/dal.js
@@ -170,10 +170,10 @@ describe("DAL", function(){
     return fileDAL.saveConf({ currency: "meta_brouzouf" });
   }));
 
-  it('should have DB version 6', () => co(function *() {
+  it('should have DB version 10', () => co(function *() {
     let version = yield fileDAL.getDBVersion();
     should.exist(version);
-    version.should.equal(6);
+    version.should.equal(10);
   }));
 
   it('should have no peer in a first time', function(){
diff --git a/test/fast/limiter-test.js b/test/fast/limiter-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..efc1ca239db1b5ec8c764f9901a2052c691e7478
--- /dev/null
+++ b/test/fast/limiter-test.js
@@ -0,0 +1,60 @@
+"use strict";
+const should = require('should');
+const co = require('co');
+const limiter = require('../../app/lib/system/limiter');
+const toolbox = require('../integration/tools/toolbox');
+const user    = require('../integration/tools/user');
+const bma     = require('../../app/lib/streams/bma');
+
+limiter.noLimit();
+
+const s1 = toolbox.server({
+  pair: {
+    pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+    sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+  }
+});
+
+const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+
+let theLimiter;
+
+describe('Limiter', () => {
+
+  before(() => {
+    limiter.withLimit();
+    theLimiter = limiter.limitAsTest();
+  });
+
+  it('should not be able to send more than 10 reqs/s', () => {
+    theLimiter.canAnswerNow().should.equal(true);
+    for (let i = 1; i <= 4; i++) {
+        theLimiter.processRequest();
+    }
+    theLimiter.canAnswerNow().should.equal(true);
+    theLimiter.processRequest(); // 5 in 1sec
+    theLimiter.canAnswerNow().should.equal(false);
+  });
+
+  it('should be able to send 1 more request (by minute constraint)', () => co(function*(){
+    yield new Promise((resolve) => setTimeout(resolve, 1000));
+    theLimiter.canAnswerNow().should.equal(true);
+    theLimiter.processRequest(); // 1 in 1sec, 6 in 1min
+    theLimiter.canAnswerNow().should.equal(false);
+  }));
+
+  it('should work with BMA API', () => co(function*(){
+    yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+    yield cat.createIdentity();
+    try {
+      for (let i = 0; i < 11; i++) {
+        yield s1.get('/wot/lookup/cat');
+      }
+      throw 'Should have thrown a limiter error';
+    } catch (e) {
+      e.should.have.property('error').property('ucode').equal(1006);
+    }
+  }));
+
+  after(() => limiter.noLimit());
+});
diff --git a/test/integration/branches.js b/test/integration/branches.js
index 98d2aa9cce2a7830dff575ec1ee09fa7e5dff175..e01e89a6dbce1fbae82254626f070e892584c718 100644
--- a/test/integration/branches.js
+++ b/test/integration/branches.js
@@ -44,7 +44,7 @@ describe("Branches", () => co(function*() {
     it('should have a 3 blocks fork window size', function() {
       return expectAnswer(rp('http://127.0.0.1:7778/node/summary', { json: true }), function(res) {
         res.should.have.property('duniter').property('software').equal('duniter');
-        res.should.have.property('duniter').property('version').equal('0.21.3');
+        res.should.have.property('duniter').property('version').equal('0.22.0');
         res.should.have.property('duniter').property('forkWindowSize').equal(3);
       });
     });
diff --git a/test/integration/documents-currency.js b/test/integration/documents-currency.js
index 056d8d30420fb373609e4fe6c862d6d03e793189..2b2e82789a654ad8b328e7c9b7502ac1baa10af0 100644
--- a/test/integration/documents-currency.js
+++ b/test/integration/documents-currency.js
@@ -49,7 +49,7 @@ describe("Document pool currency", function() {
 
   it('Identity with wrong currency should be rejected', () => co(function*() {
     const idtyCat1 = yield s1.lookup2identity(cat1.pub);
-    console.log(idtyCat1.createIdentity());
+    idtyCat1.createIdentity();
     try {
       yield s2.postIdentity(idtyCat1);
       throw "Identity should not have been accepted, since it has an unknown currency name";
diff --git a/test/integration/forwarding.js b/test/integration/forwarding.js
index 6155b12c812b9836a99af13fb1a42b2bdd7726f8..1fd477872f5b02712cf3c85d03196c6e8bf5a23b 100644
--- a/test/integration/forwarding.js
+++ b/test/integration/forwarding.js
@@ -7,6 +7,9 @@ const co     = require('co');
 const node   = require('./tools/node');
 const user   = require('./tools/user');
 const jspckg = require('../../package');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
 
 const MEMORY_MODE = true;
 
diff --git a/test/integration/identity-same-pubkey.js b/test/integration/identity-same-pubkey.js
index 966aa2bf06ac22d30a04a9b660939d898ac0f4b8..2614ab0fdffa2e7d8e7efa660f7f64a1dfaa9090 100644
--- a/test/integration/identity-same-pubkey.js
+++ b/test/integration/identity-same-pubkey.js
@@ -38,7 +38,6 @@ describe("Identities with shared pubkey", function() {
   }));
 
   it('should exit 2 pubkey result', () => s1.expect('/wot/lookup/cat', (res) => {
-    console.log(JSON.stringify(res, null, ' '));
     res.results.should.have.length(2);
     res.results[0].should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd');
     res.results[1].should.have.property('pubkey').equal('2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc');
diff --git a/test/integration/identity-test.js b/test/integration/identity-test.js
index b8aee52c13935c2b0c8a523432fed625cc402b1d..b3a4013e563990030ca813328030c7be4f7edeb3 100644
--- a/test/integration/identity-test.js
+++ b/test/integration/identity-test.js
@@ -10,6 +10,9 @@ const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
 
 const expectAnswer   = httpTest.expectAnswer;
 
diff --git a/test/integration/migrations.js b/test/integration/migrations.js
index 54e9ee350a23b7d678a511ac57ebd7657f258cf9..81c1a13650f066f9b96fdeac18dc7a2e39f4b549 100644
--- a/test/integration/migrations.js
+++ b/test/integration/migrations.js
@@ -61,8 +61,7 @@ describe("Migration", function() {
      */
 
     it('should be able to fix the issue', () => co(function*() {
-      yield node.server.dal.metaDAL.exec('UPDATE meta SET version = 3');
-      yield node.server.dal.metaDAL.upgradeDatabase();
+      yield node.server.dal.metaDAL.upgradeDatabaseVersions([3,4,5]);
     }));
 
     it('it should exist a received tx for toc', node.expectHttp('/tx/history/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', (res) => {
diff --git a/test/integration/revocation-test.js b/test/integration/revocation-test.js
index 1a78b7792e0f09e4f2aa76d3ad31a259b81cb809..27509104a65ecafeb1fb725f0fa40074205c9a67 100644
--- a/test/integration/revocation-test.js
+++ b/test/integration/revocation-test.js
@@ -8,6 +8,9 @@ const user      = require('./tools/user');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
 
 const expectAnswer   = httpTest.expectAnswer;
 
diff --git a/test/integration/server-import-export.js b/test/integration/server-import-export.js
new file mode 100644
index 0000000000000000000000000000000000000000..c0372d89ea18daf3d5672c35b7f42a5cb045bf95
--- /dev/null
+++ b/test/integration/server-import-export.js
@@ -0,0 +1,60 @@
+"use strict";
+const _ = require('underscore');
+const should = require('should');
+const fs = require('fs');
+const co = require('co');
+const unzip = require('unzip');
+const toolbox = require('../integration/tools/toolbox');
+const user    = require('../integration/tools/user');
+const bma     = require('../../app/lib/streams/bma');
+
+const serverConfig = {
+  memory: false,
+  pair: {
+    pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+    sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+  }
+};
+
+let s1;
+
+describe('Import/Export', () => {
+
+  before(() => co(function *() {
+    const s0 = toolbox.server(_.extend({ homename: 'dev_unit_tests1' }, serverConfig));
+    yield s0.resetHome();
+
+    s1 = toolbox.server(_.extend({ homename: 'dev_unit_tests1' }, serverConfig));
+
+    const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+
+    yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+    yield cat.createIdentity();
+    yield tac.createIdentity();
+    yield cat.cert(tac);
+    yield tac.cert(cat);
+    yield cat.join();
+    yield tac.join();
+    yield s1.commit();
+  }));
+
+  it('should be able to export data', () => co(function *() {
+    const archive = yield s1.exportAllDataAsZIP();
+    const output = require('fs').createWriteStream(s1.home + '/export.zip');
+    archive.pipe(output);
+    return new Promise((resolve, reject) => {
+      archive.on('error', reject);
+      output.on('close', function() {
+        console.log(archive.pointer() + ' total bytes');
+        console.log('archiver has been finalized and the output file descriptor has closed.');
+        resolve();
+      });
+    });
+  }));
+
+  it('should be able to import data', () => co(function *() {
+    yield s1.unplugFileSystem();
+    yield s1.importAllDataFromZIP(s1.home + '/export.zip');
+  }));
+});
diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js
new file mode 100644
index 0000000000000000000000000000000000000000..5be8d59bd04f43566b47e7d1039b83b2ca9eb33d
--- /dev/null
+++ b/test/integration/server-sandbox.js
@@ -0,0 +1,258 @@
+"use strict";
+
+const co        = require('co');
+const should    = require('should');
+const bma       = require('../../app/lib/streams/bma');
+const user      = require('./tools/user');
+const commit    = require('./tools/commit');
+const until     = require('./tools/until');
+const toolbox   = require('./tools/toolbox');
+const multicaster = require('../../app/lib/streams/multicaster');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
+
+const s1 = toolbox.server({
+  idtyWindow: 10,
+  certWindow: 10,
+  msWindow: 10,
+  dt: 10,
+  pair: {
+    pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+    sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+  }
+});
+
+const s2 = toolbox.server({
+  pair: {
+    pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV',
+    sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'
+  }
+});
+
+const s3 = toolbox.server({
+  pair: {
+    pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX',
+    sec: '2ANWb1qjjYRtT2TPFv1rBWA4EVfY7pqE4WqFUuzEgWG4vzcuvyUxMtyeBSf93M4V3g4MeEkELaj6NjA72jxnb4yF'
+  }
+});
+
+const i1 = user('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+const i2 = user('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+const i3 = user('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+const i4 = user('i4',   { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+const i5 = user('i5',   { pub: '91dWdiyf7KaC4GAiKrwU7nGuue1vvmHqjCXbPziJFYtE', sec: '4Zno2b8ZULwBLY3RU5JcZhUf2a5FfXLVUMaYwPEzzN6i4ow9vXPsiCq7u2pEhkgJywqWdj97Hje1fdqnnzHeFgQe'}, { server: s1 });
+const i6 = user('i6',   { pub: '3C95HniUZsUN55AJy7z4wkz1UwtebbNd63dVAZ6EaUNm', sec: '3iJMz8JKNeU692L7jvug8xVnKvzN9RDee2m6QkMKbWKrvoHhv6apS4LR9hP786PUyFYJWz8bReMrFK8PY3aGxB8m'}, { server: s1 });
+const i7 = user('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s1 });
+const i7onS2 = user('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s2 });
+const i8 = user('i8',   { pub: '6GiiWjJxr29Stc4Ph4J4EipZJCzaQW1j6QXKANTNzRV3', sec: 'Yju625FGz6FHErshRc7jZyJUJ83MG4Zh9TXUNML62rKLXz7VJmwofnhJzeRRensranFJGQMYBLNSAeycAAsp62m' }, { server: s1 });
+const i9 = user('i9',   { pub: '6v4HnmiGxNzKwEjnBqxicWAmdKo6Bk51GvfQByS5YmiB', sec: '2wXPPDYfM3a8jmpYiFihS9qzdqFZrLWryu4uwpNPRuw5TRW3JCdJPsMa64eAcpshLTnMXkrKL94argk3FGxzzBKh' }, { server: s1 });
+const i10 = user('i10', { pub: '6kr9Xr86qmrrwGq3XEjUXRVpHqS63FL52tcutcYGcRiv', sec: '2jCzQx7XUWoxboH67mMMv2z8VcrQabtYWpxS39iF6hNQnSBwN1d9RVauVC52PTRz6mgMzTjrSMETPrrB5N3oC7qQ' }, { server: s1 });
+const i11 = user('i11', { pub: '5VLVTp96iX3YAq7NXwZeM2N6RjCkmxaU4G6bwMg1ZNwf', sec: '3BJtyeH1Q8jPcKuzL35m4eVPGuFXpcfRiGSseVawToCWykz1qAic9V2wk31wzEqXjqCq7ZKW4MjtZrzKCGN5K7sT' }, { server: s1 });
+const i12 = user('i12', { pub: 'D6zJSPxZqs1bpgGpzJu9MgkCH7UxkG7D5u4xnnSH62wz', sec: '375vhCZdmVx7MaYD4bMZCevRLtebSuNPucfGevyPiPtdqpRzYLLNfd1h25Q59h4bm54dakpZ1RJ45ZofAyBmX4Et' }, { server: s1 });
+const i13 = user('i13', { pub: 'BQ1fhCsJGohYKKfCbt58zQ8RpiSy5M8vwzdXzm4rH7mZ', sec: '4bTX2rMeAv8x79xQdFWPgY8zQLbPZ4HE7MWKXoXHyCoYgeCFpiWLdfvXwTbt31UMGrkNp2CViEt68WkjAZAQkjjm' }, { server: s1 });
+const i14 = user('i14', { pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX', sec: '2ANWb1qjjYRtT2TPFv1rBWA4EVfY7pqE4WqFUuzEgWG4vzcuvyUxMtyeBSf93M4V3g4MeEkELaj6NjA72jxnb4yF' }, { server: s1 });
+// const i15 = user('i15', { pub: '8cHWEmVrdT249w8vJdiBms9mbu6CguQgXx2gRVE8gfnT', sec: '5Fy9GXiLMyhvRLCpoFf35XXNj24WXX29wM6xeCQiy5Uk7ggNhRcZjjp8GcpjRyE94oNR2jRNK4eAGiYUFnvbEnGB' }, { server: s1 });
+// const i16 = user('i16', { pub: 'vi8hUTxss825cFCQE4SzmqBaAwLS236NmtrTQZBAAhG',  sec: '5dVvAdWKcndQSaR9pzjEriRhGkCjef74HzecqKnydBVHdxXDewpAu3mcSU72PRKcCkTYTJPpgWmwuCyZubDKmoy4' }, { server: s1 });
+
+describe("Sandboxes", function() {
+
+  const now = parseInt(Date.now() / 1000);
+
+  before(() => co(function*() {
+
+    yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+    yield s2.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+    yield s3.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+    s1.dal.idtyDAL.setSandboxSize(3);
+    s1.dal.certDAL.setSandboxSize(7);
+    s1.dal.msDAL.setSandboxSize(2);
+    s1.dal.txsDAL.setSandboxSize(2);
+    s2.dal.idtyDAL.setSandboxSize(10);
+    s3.dal.idtyDAL.setSandboxSize(3);
+  }));
+
+  describe('Identities', () => {
+
+
+    it('should i1, i2, i3', () => co(function *() {
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(3);
+      yield i1.createIdentity();
+      yield i2.createIdentity();
+      yield i3.createIdentity();
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+    }));
+
+    it('should reject i4', () => shouldThrow(co(function *() {
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield i4.createIdentity();
+    })));
+
+    it('should create i4 by i1->i4', () => co(function *() {
+      yield i4.createIdentity(null, s2);
+      yield i1.cert(i4, s2);
+    }));
+
+    it('should accept i1 (already here) by i4->i1', () => co(function *() {
+      yield i4.cert(i1);
+    }));
+
+    it('should commit & make room for sandbox, and commit again', () => co(function *() {
+      yield i1.join();
+      yield i4.join();
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield s1.commit();
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(1); // i2, i3
+      yield s1.commit();
+      yield s2.syncFrom(s1, 0, 1);
+      yield s3.syncFrom(s1, 0, 1);
+    }));
+
+    it('should create i5(1)', () => co(function *() {
+      yield i5.createIdentity();
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+    }));
+
+    it('should reject i7(1)', () => shouldThrow(i7.createIdentity()));
+
+    it('should reject i7(1) by revocation', () => shouldThrow(co(function *() {
+      yield i7onS2.createIdentity();
+      const idty = yield i7onS2.lookup(i7onS2.pub);
+      yield i7.revoke(idty);
+    })));
+
+    it('should accept i7(1), i8(1), i9(1) by i1->i7(1), i1->i8(1), i1->i9(1)', () => co(function *() {
+      yield i1.cert(i7, s2);
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield i8.createIdentity(null, s2);
+      yield i1.cert(i8, s2);
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield i9.createIdentity(null, s2);
+      yield i1.cert(i9, s2);
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+    }));
+
+    it('should reject i10(1) by i1->i10(1)', () => shouldThrow(co(function *() {
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield i10.createIdentity(null, s2);
+      yield i1.cert(i10, s2);
+    })));
+
+    it('should accept i10(0) by i1->i10(0) because of an anterior date compared to others in sandbox', () => co(function *() {
+      yield i10.createIdentity(true, s3);
+      yield i1.cert(i10, s3);
+    }));
+
+    it('should accept i11(0) and i12(0) for the same reason', () => co(function *() {
+      yield i11.createIdentity(true, s3);
+      yield i1.cert(i11, s3);
+      yield i12.createIdentity(true, s3);
+      yield i1.cert(i12, s3);
+    }));
+
+    it('should reject i13(0) because absolutely no more room is available', () => shouldThrow(co(function *() {
+      yield i13.createIdentity(true, s3);
+      yield i1.cert(i13, s3);
+    })));
+
+    it('should accept an identity with the same key as server, always', () => co(function *() {
+      (yield s3.dal.idtyDAL.getSandboxRoom()).should.equal(0);
+      yield i14.createIdentity(null, s3);
+    }));
+
+    it('should make room as identities get expired', () => co(function *() {
+      yield s1.commit({
+        time: now + 1000
+      });
+      yield s1.commit({
+        time: now + 1000
+      });
+      yield s1.commit({
+        time: now + 1000
+      });
+      (yield s1.dal.idtyDAL.getSandboxRoom()).should.equal(3);
+    }));
+  });
+
+  describe('Certifications', () => {
+
+    it('should accept i4->i7(0),i4->i8(0),i4->i9(0)', () => co(function *() {
+      s1.dal.certDAL.setSandboxSize(3);
+      (yield s1.dal.certDAL.getSandboxRoom()).should.equal(3);
+      yield i4.cert(i7);
+      yield i4.cert(i8);
+      yield i4.cert(i9);
+      (yield s1.dal.certDAL.getSandboxRoom()).should.equal(0);
+    }));
+
+    it('should reject i4->i10(0)', () => shouldThrow(co(function *() {
+      (yield s1.dal.certDAL.getSandboxRoom()).should.equal(0);
+      yield i4.cert(i10);
+    })));
+
+    it('should accept a certification from the same key as server, always', () => co(function *() {
+      (yield s1.dal.certDAL.getSandboxRoom()).should.equal(0);
+      yield i1.cert(i8);
+    }));
+
+    it('should make room as certs get expired', () => co(function *() {
+      yield s1.commit({
+        time: now + 1000
+      });
+      (yield s1.dal.certDAL.getSandboxRoom()).should.equal(3);
+    }));
+  });
+
+  describe('Memberships', () => {
+
+    it('should accept i8,i9', () => co(function *() {
+      (yield s1.dal.msDAL.getSandboxRoom()).should.equal(2);
+      yield i8.join();
+      yield i9.join();
+      (yield s1.dal.msDAL.getSandboxRoom()).should.equal(0);
+    }));
+
+    it('should reject i7', () => shouldThrow(co(function *() {
+      (yield s1.dal.msDAL.getSandboxRoom()).should.equal(0);
+      yield i7.join();
+    })));
+
+    it('should accept a membership from the same key as server, always', () => co(function *() {
+      (yield s1.dal.msDAL.getSandboxRoom()).should.equal(0);
+      yield i1.join();
+    }));
+
+    it('should make room as membership get expired', () => co(function *() {
+      yield s1.commit({
+        time: now + 1000
+      });
+      (yield s1.dal.msDAL.getSandboxRoom()).should.equal(2);
+    }));
+  });
+
+  describe('Transaction', () => {
+
+    it('should accept 2 transactions of 20, 30 units', () => co(function *() {
+      yield i4.send(20, i1);
+      yield i4.send(30, i1);
+    }));
+
+    it('should reject amount of 10', () => shouldThrow(co(function *() {
+      yield i4.send(10, i1);
+    })));
+
+    it('should accept a transaction from the same key as server, always', () => co(function *() {
+      yield i1.send(10, i4);
+    }));
+
+    it('should make room as transactions get commited', () => co(function *() {
+      yield s1.commit();
+      yield s1.commit();
+      (yield s1.dal.txsDAL.getSandboxRoom()).should.equal(2);
+    }));
+  });
+});
+
+function shouldThrow(promise) {
+  return promise.should.be.rejected();
+}
diff --git a/test/integration/tests.js b/test/integration/tests.js
index c133890eee9f1b6a9187ab31e3b3a334e97f5685..ab2c03cceb13b8e9e7ce1c48f3cb8eb5baa3b729 100644
--- a/test/integration/tests.js
+++ b/test/integration/tests.js
@@ -13,6 +13,9 @@ const jspckg = require('../../package');
 const commit    = require('./tools/commit');
 const httpTest  = require('./tools/http');
 const rp        = require('request-promise');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
 
 const expectAnswer   = httpTest.expectAnswer;
 const MEMORY_MODE = true;
diff --git a/test/integration/tools/toolbox.js b/test/integration/tools/toolbox.js
index abc635e1dbc3e3bfbabd43b012b1526599e735bf..968279d380adad261a434d29b293e24b784c2125 100644
--- a/test/integration/tools/toolbox.js
+++ b/test/integration/tools/toolbox.js
@@ -37,8 +37,8 @@ module.exports = {
       sigQty: 1
     };
     const server = duniter({
-      memory: MEMORY_MODE,
-      name: 'dev_unit_tests'
+      memory: conf.memory !== undefined ? conf.memory : MEMORY_MODE,
+      name: conf.homename || 'dev_unit_tests'
     }, _.extend(conf, commonConf));
 
     server.port = port;
diff --git a/test/integration/tools/user.js b/test/integration/tools/user.js
index 08be2c3d108b5f1447b65395cf3213accf7d80da..cf07926322e10a1a1d8789c511e1f56bdbef7ad5 100644
--- a/test/integration/tools/user.js
+++ b/test/integration/tools/user.js
@@ -55,7 +55,7 @@ function User (uid, options, node) {
     }
   }
 
-  this.createIdentity = (useRoot) => co(function*() {
+  this.createIdentity = (useRoot, fromServer) => co(function*() {
     if (!pub)
       yield Q.nfcall(init);
     const current = yield node.server.BlockchainService.current();
@@ -67,9 +67,9 @@ function User (uid, options, node) {
       currency: node.server.conf.currency
     });
     createIdentity += keyring.Key(pub, sec).signSync(createIdentity) + '\n';
-    yield Q.nfcall(post, '/wot/add', {
+    yield doPost('/wot/add', {
       "identity": createIdentity
-    });
+    }, fromServer);
   });
 
   this.makeCert = (user, fromServer, overrideProps) => co(function*() {
@@ -95,7 +95,7 @@ function User (uid, options, node) {
 
   this.cert = (user, fromServer) => co(function*() {
     const cert = yield that.makeCert(user, fromServer);
-    yield Q.nfcall(post, '/wot/certify', {
+    yield doPost('/wot/certify', {
       "cert": cert.getRaw()
     });
   });
@@ -331,6 +331,21 @@ function User (uid, options, node) {
     postReq.form(data);
   }
 
+  function doPost(uri, data, fromServer) {
+    const ip = fromServer ? fromServer.conf.ipv4 : node.server.conf.remoteipv4;
+    const port = fromServer ? fromServer.conf.port : node.server.conf.remoteport;
+    return new Promise((resolve, reject) => {
+      var postReq = request.post({
+        "uri": 'http://' + [ip, port].join(':') + uri,
+        "timeout": 1000 * 100000
+      }, function (err, res, body) {
+        err = err || (res.statusCode != 200 && body != 'Already up-to-date' && body) || null;
+        err ? reject(err) : resolve(res);
+      });
+      postReq.form(data);
+    });
+  }
+
   function getVucoin(fromServer) {
     return Q.Promise(function(resolve, reject){
       let theNode = (fromServer && { server: fromServer }) || node;
diff --git a/test/integration/transactions-test.js b/test/integration/transactions-test.js
index acdd67af3dcc0c6ab45b1c9fa96cb44f83117443..8e638f206be407add8fb8dd15e7bc72bc3ab8537 100644
--- a/test/integration/transactions-test.js
+++ b/test/integration/transactions-test.js
@@ -11,6 +11,9 @@ const node   = require('./tools/node');
 const user   = require('./tools/user');
 const unit   = require('./tools/unit');
 const http   = require('./tools/http');
+const limiter = require('../../app/lib/system/limiter');
+
+limiter.noLimit();
 
 describe("Testing transactions", function() {
 
diff --git a/web-ui b/web-ui
index bd229a872a7346262530a6b6d81e046d9c6a9f72..68f9b73ae824612ae1f39b91dfbca156eb8cafe1 160000
--- a/web-ui
+++ b/web-ui
@@ -1 +1 @@
-Subproject commit bd229a872a7346262530a6b6d81e046d9c6a9f72
+Subproject commit 68f9b73ae824612ae1f39b91dfbca156eb8cafe1