diff --git a/.gitignore b/.gitignore
index d41ca053c57ee2e5a482c6728ae9fc590281080d..80e9514b212a1d7f659e51f53f4e00b9e05915a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,4 @@ npm-debug.log
 bin/jpgp*.jar
 .idea/
 naclb/build
-naclb/node_modules
-dump*/
-/ui/
\ No newline at end of file
+naclb/node_modules
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..dd15f02a551959395883ce811b6c0a64c209fe52
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "web-ui"]
+	path = web-ui
+	url = https://github.com/duniter/duniter-ui.git
diff --git a/.travis.yml b/.travis.yml
index 229034e4f2f418c0dca6345c2621a5b057ff66f8..ddc57c53aa577de047f003d3ab56afe2edfe8351 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,62 +26,25 @@ after_success:
 
 before_deploy:
   # Download & embed Nodejs binary
-  - NVER=`node -v`
-  - DUNITER_VER=`git describe --exact-match --tags $(git log -n1 --pretty='%h') | grep -Po "\d.*"`
-  - DUNITER_DEB_VER=" $DUNITER_VER"
-  - wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz
-  - tar xzf node-${NVER}-linux-x64.tar.gz
-  - mv node-${NVER}-linux-x64 node
-  - rm node-${NVER}-linux-x64.tar.gz
-  # Clean testing packages
-  - npm prune --production
-  - tar czf ../ucoin-x64.tar.gz ./ --exclude ".git" --exclude "coverage" --exclude "test"
-  - SRC=`pwd`
-  - cd ..
-  # GUI Version
-  - mkdir ucoin_release
-  - NW_RELEASE="v0.13.0-rc2"
-  - NW="nwjs-${NW_RELEASE}-linux-x64"
-  - NW_GZ="${NW}.tar.gz"
-  - wget http://dl.nwjs.io/${NW_RELEASE}/${NW_GZ}
-  - tar xvzf ${NW_GZ}
-  - mv ${NW} ucoin_release/nw
-  - cp ${SRC}/gui/* ucoin_release/nw/
-  - cp -R ${SRC}/ ucoin_release/sources/
-  - rm -Rf ucoin_release/sources/ui/package/node_modules
-  - rm -Rf ucoin_release/sources/ui/package/bower_components
-  - cd ucoin_release
-  - tar czf ../duniter-x64.tar.gz * --exclude ".git" --exclude "coverage" --exclude "test"
-  - cd ..
-  - git clone https://github.com/ucoin-io/debpkg.git duniter-x64
-  - rm -Rf duniter-x64/.git
-  - mkdir -p duniter-x64/opt/duniter/
-  - chmod 755 duniter-x64/DEBIAN/post*
-  - chmod 755 duniter-x64/DEBIAN/pre*
-  - sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-x64/DEBIAN/control
-  - cd ucoin_release/sources
-  - rm -Rf .git
-  - zip -qr ../duniter-desktop.nw *
-  - cd ../nw
-  - zip -qr ../nw.nwb *
-  - cd ../..
-  - mv ucoin_release/duniter-desktop.nw duniter-x64/opt/duniter/
-  - mv ucoin_release/nw.nwb duniter-x64/opt/duniter/
-  - fakeroot dpkg-deb --build duniter-x64
-  - mv duniter-x64.deb duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb
-  - mv duniter-x64.tar.gz duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz
-
-# Releases deployed on GitHub
+  - ci/travis/before_deploy.sh
 deploy:
-  provider: releases
-  prerelease: true
-  skip_cleanup: true
-  api_key:
-    secure: eZ+DHkTYXkBqoHp/Qea1duDQXTAgJ+oYWbWET7PNlNbq6IGHRae//VqGkOIGaYvPCu6ezZbI/z/nQvBhm794vihYYek1Y8AAXp0QmxmRJhCgsCxRcVmyLu99un2aAYiZzUKKOylQrhc30JsObgo3JrovHAdMJFDf3f123whAPCA=
-  file:
-    - ucoin-x64.tar.gz
-    - duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb
-    - duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz
-  on:
-    repo: duniter/duniter
-    tags: true
+  - provider: npm
+    email: cem.moreau@gmail.com
+    skip_cleanup: true
+    api_key:
+      secure: gZV7yLxDwwxD4oQXl1hwugmtnWXqP8vojuVGtAGwtMlwJE0n270w6O5xZHDd7DSmOZLftk6/wue/RdhLDsD6J1z3Uxu+VoUWy7aG/sFcGRaBwct+bGqFGkyd+I1mCXFnAZMDwbtgdkQlOCS9PM1BfMEYq49XXqaLaDnwouR+2bI=
+    on:
+      tags: true
+      repo: duniter/duniter
+  - provider: releases
+    prerelease: true
+    skip_cleanup: true
+    api-key:
+      secure: h+GgYhh8/I/l5EWdI1+bDQYtN57jvDf0xhiwcxlfm/AvaJWOZa+q81HsXdHqtwgmsCoRklKtOqTM/nqgSOcWPXsAditRjEXOui2FjZiJCosDjDPMnN6HKzw6XVLtSOx7CeCX/xFmoNTIdsAuC+HUfDWCUFgrsMj9F0V9J3U1nDs=
+    file:
+    - /home/travis/build/ucoin-x64.tar.gz
+    - /home/travis/build/duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb
+    - /home/travis/build/duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz
+    on:
+      repo: duniter/duniter
+      tags: true
diff --git a/readme.md b/README.md
similarity index 73%
rename from readme.md
rename to README.md
index 233b3923417ed593d9a4549548f0879009e1ec0d..f6ca5713f048e1a66fa863bbfdd72bae1d9a1607 100644
--- a/readme.md
+++ b/README.md
@@ -4,36 +4,36 @@
 
 Duniter (previously uCoin) is a libre software allowing to create a new kind of P2P crypto-currencies based on individuals and Universal Dividend.
 
-Inspired by [Bitcoin](https://github.com/bitcoin/bitcoin) and [OpenUDC project](https://github.com/Open-UDC/open-udc).
+Inspired by [Bitcoin](https://github.com/bitcoin/bitcoin) and [OpenUDC](https://github.com/Open-UDC/open-udc) projects.
 
 ## Development state
 
 Software is still under development, and **no production currency using Duniter exists for now**.
 
-However, it already exists a testing currency named [MetaBrouzouf](http://duniter.org/try/). Want to test it? Two ways.
+However, it already exists a testing currency named [Test_Net](https://forum.duniter.org/t/join-our-new-testnet-currency/813). Want to test it? Two ways.
 
 ### Add your node to the network
 
 ```
 $ wget -qO- https://raw.githubusercontent.com/duniter/duniter/master/install.sh | bash
 $ duniter init
-$ duniter sync metab.ucoin.io 9201
+$ duniter sync duniter.org 8999
 $ duniter start
 ```
 
-and you are done! See [Install documentation](https://github.com/duniter/duniter/wiki/Install-uCoin-node) for further details.
+and you are done! See [Install documentation](https://github.com/duniter/duniter/wiki/Install-Duniter-node) for further details.
 
 ### Try Sakia Wallet
 
-In the world of crypto-currencies, [Sakia Wallet](http://sakia-wallet.org/) would be called a [SPV](https://en.bitcoin.it/wiki/Thin_Client_Security#Simplified_Payment_Verification_.28SPV.29_Clients) client. It is a graphical client connecting to the network. See this [tutorial](https://forum.duniter.org/t/subscribing-to-meta-brouzouf-testing-currency/199) to join in the testing currency with Sakia.
+In the world of crypto-currencies, [Sakia Wallet](http://sakia-wallet.org/) would be called a [SPV](https://en.bitcoin.it/wiki/Thin_Client_Security#Simplified_Payment_Verification_.28SPV.29_Clients) client. It is a graphical client connecting to the network. See this [tutorial](https://forum.duniter.org/t/join-our-new-testnet-currency/813) to join in the testing currency with Sakia.
 
-![Sakia screenshot](http://sakia-wallet.org/img/Dividends.png)
+<p align="center"><img src="http://sakia-wallet.org/img/Dividends.png" /></p>
 
 ## Going further
 
 ### Documentation
 
-Visit [Duniter website](http://duniter.org): it gathers theoretical informations, FAQ and several useful links. If you want to learn, this is the first place to visit.
+Visit [Duniter website](https://duniter.org): it gathers theoretical informations, FAQ and several useful links. If you want to learn, this is the first place to visit.
 
 ### Talk about/get involved in Duniter project
 
@@ -47,8 +47,8 @@ If you wish to participate/debate on Duniter, you can:
 # References
 
 ## Theoretical
-* [[en] Relative theory of money](http://vit.free.fr/TRM/en_US/)
-* [[fr] Théorie relative de la monaie](http://trm.creationmonetaire.info/)
+* [[en] Relative theory of money](http://en.trm.creationmonetaire.info)
+* [[fr] Théorie relative de la monaie](http://trm.creationmonetaire.info)
 
 ## OpenUDC
 
diff --git a/app/controllers/abstract.js b/app/controllers/abstract.js
index 9c57444660af390e3936318a48e633eb2ee47b31..8c80d1e933058b10fbe53a7aee7f264cf2fc0f49 100644
--- a/app/controllers/abstract.js
+++ b/app/controllers/abstract.js
@@ -5,11 +5,10 @@ var dos2unix = require('../lib/dos2unix');
 
 module.exports = function AbstractController (server) {
 
-  this.pushEntity = (req, rawer, parser) => co(function *() {
+  this.pushEntity = (req, rawer, type) => co(function *() {
     let rawDocument = rawer(req);
     rawDocument = dos2unix(rawDocument);
-    let obj = parser.syncWrite(rawDocument);
-    let written = yield server.singleWritePromise(obj);
+    let written = yield server.writeRaw(rawDocument, type);
     return written.json();
   });
 };
diff --git a/app/controllers/blockchain.js b/app/controllers/blockchain.js
index aad2fd7636d51eee43698e6ccb7aa3127d59491f..55a209cf18b796ebf2f5157d6fc762f4b7e12a30 100644
--- a/app/controllers/blockchain.js
+++ b/app/controllers/blockchain.js
@@ -2,11 +2,9 @@
 
 var co               = require('co');
 var _                = require('underscore');
-var moment           = require('moment');
 var rules            = require('../lib/rules');
 var constants        = require('../lib/constants');
 var http2raw         = require('../lib/streams/parsers/http2raw');
-var parsers          = require('../lib/streams/parsers/doc');
 var Membership       = require('../lib/entity/membership');
 var AbstractController = require('./abstract');
 
@@ -29,9 +27,9 @@ function BlockchainBinding (server) {
   var Block      = require('../lib/entity/block');
   var Stat       = require('../lib/entity/stat');
 
-  this.parseMembership = (req) => this.pushEntity(req, http2raw.membership, parsers.parseMembership);
+  this.parseMembership = (req) => this.pushEntity(req, http2raw.membership, constants.ENTITY_MEMBERSHIP);
 
-  this.parseBlock = (req) => this.pushEntity(req, http2raw.block, parsers.parseBlock);
+  this.parseBlock = (req) => this.pushEntity(req, http2raw.block, constants.ENTITY_BLOCK);
 
   this.parameters = () => server.dal.getParameters();
 
diff --git a/app/controllers/network.js b/app/controllers/network.js
index 0689f2222cec19ac4f8779945034354dfc276b06..a5e5a156b81bc97a9ddc557ac29cabe015789c71 100644
--- a/app/controllers/network.js
+++ b/app/controllers/network.js
@@ -2,9 +2,7 @@
 var _                = require('underscore');
 var co               = require('co');
 var Q                = require('q');
-var async            = require('async');
 var http2raw         = require('../lib/streams/parsers/http2raw');
-var parsers          = require('../lib/streams/parsers/doc');
 var constants        = require('../lib/constants');
 var Peer             = require('../lib/entity/peer');
 var AbstractController = require('./abstract');
@@ -53,7 +51,7 @@ function NetworkBinding (server) {
     });
   });
 
-  this.peersPost = (req) => this.pushEntity(req, http2raw.peer, parsers.parsePeer);
+  this.peersPost = (req) => this.pushEntity(req, http2raw.peer, constants.ENTITY_PEER);
 
   this.peers = () => co(function *() {
     let peers = yield server.dal.listAllPeers();
diff --git a/app/controllers/transactions.js b/app/controllers/transactions.js
index cec9883530c466332aea7fcc8c52c9562db66214..a1930f87c499f61d22733fcd010047c2e6ad514b 100644
--- a/app/controllers/transactions.js
+++ b/app/controllers/transactions.js
@@ -4,8 +4,8 @@ var Q                = require('q');
 var async            = require('async');
 var _                = require('underscore');
 var http2raw         = require('../lib/streams/parsers/http2raw');
-var parsers          = require('../lib/streams/parsers/doc');
 var Transaction      = require('../lib/entity/transaction');
+var constants        = require('../lib/constants');
 var AbstractController = require('./abstract');
 
 module.exports = function (server) {
@@ -26,7 +26,7 @@ function TransactionBinding(server) {
 
   let getHistoryP = (pubkey, filter) => Q.nbind(getHistory, this)(pubkey, filter);
 
-  this.parseTransaction = (req) => this.pushEntity(req, http2raw.transaction, parsers.parseTransaction);
+  this.parseTransaction = (req) => this.pushEntity(req, http2raw.transaction, constants.ENTITY_TRANSACTION);
 
   this.getSources = (req) => co(function *() {
     let pubkey = yield ParametersService.getPubkeyP(req);
diff --git a/app/controllers/webmin.controller.js b/app/controllers/webmin.controller.js
index 939776000abe0bd915511d2b1483c087efab3e7e..71ec11fe77138726a78a66e50aca72ef27154ebc 100644
--- a/app/controllers/webmin.controller.js
+++ b/app/controllers/webmin.controller.js
@@ -7,20 +7,16 @@ var _ = require('underscore');
 var Q = require('q');
 let co = require('co');
 let ucoin = require('../../index');
-var upnp = require('../lib/upnp');
 let ucp = require('../lib/ucp');
 let constants = require('../lib/constants');
 let base58 = require('../lib/base58');
 let rawer = require('../lib/rawer');
 let crypto = require('../lib/crypto');
 let http2raw = require('../lib/streams/parsers/http2raw');
-let parsers = require('../lib/streams/parsers/doc');
 let bma = require('../lib/streams/bma');
 let Identity = require('../lib/entity/identity');
 let network = require('../lib/network');
 let AbstractController = require('../controllers/abstract');
-var Synchroniser = require('../lib/sync');
-var multicaster = require('../lib/streams/multicaster');
 var logger = require('../lib/logger')('webmin');
 
 module.exports = (dbConf, overConf) => {
@@ -50,13 +46,7 @@ function WebAdmin (dbConf, overConf) {
     yield pluggedConfP;
 
     // Routing documents
-    server
-    // The router asks for multicasting of documents
-      .pipe(server.router())
-      // The documents get sent to peers
-      .pipe(multicaster(server.conf))
-      // The multicaster may answer 'unreachable peer'
-      .pipe(server.router());
+    server.routing();
 
     return plugForDAL();
   });
@@ -96,7 +86,7 @@ function WebAdmin (dbConf, overConf) {
 
   this.openUPnP = () => co(function *() {
     yield pluggedDALP;
-    return upnp(server.conf.port, server.conf.remoteport);
+    return server.upnp();
   });
 
   this.regularUPnP = () => co(function *() {
@@ -105,7 +95,7 @@ function WebAdmin (dbConf, overConf) {
       server.upnpAPI.stopRegular();
     }
     try {
-      server.upnpAPI = yield upnp(server.conf.port, server.conf.remoteport);
+      yield server.upnp();
       server.upnpAPI.startRegular();
     } catch (e) {
       logger.error(e);
@@ -134,7 +124,7 @@ function WebAdmin (dbConf, overConf) {
     yield server.dal.saveConf({
       routing: true,
       createNext: true,
-      cpu: 0.5,
+      cpu: constants.DEFAULT_CPU,
       ipv4: conf.local_ipv4,
       ipv6: conf.local_ipv6,
       port: conf.lport,
@@ -187,7 +177,7 @@ function WebAdmin (dbConf, overConf) {
     if (!found) {
       let selfCert = rawer.getOfficialIdentity(entity);
       selfCert += crypto.signSync(selfCert, secretKey) + '\n';
-      found = yield that.pushEntity({ body: { identity: selfCert }}, http2raw.identity, parsers.parseIdentity);
+      found = yield that.pushEntity({ body: { identity: selfCert }}, http2raw.identity, constants.ENTITY_IDENTITY);
     }
     yield server.dal.fillInMembershipsOfIdentity(Q(found));
     if (_.filter(found.memberships, { membership: 'IN'}).length == 0) {
@@ -202,7 +192,7 @@ function WebAdmin (dbConf, overConf) {
         "certts": block
       });
       join += crypto.signSync(join, secretKey) + '\n';
-      yield that.pushEntity({ body: { membership: join }}, http2raw.membership, parsers.parseMembership);
+      yield that.pushEntity({ body: { membership: join }}, http2raw.membership, constants.ENTITY_MEMBERSHIP);
       yield server.recomputeSelfPeer();
     }
     //
@@ -366,46 +356,48 @@ function WebAdmin (dbConf, overConf) {
   });
 
   this.autoConfNetwork = () => co(function *() {
-    let bestLocal4 = network.getBestLocalIPv4();
-    let bestLocal6 = network.getBestLocalIPv6();
-    let upnpConf = {
-      remoteipv4: bestLocal4,
-      remoteipv6: bestLocal6,
-      upnp: false
-    };
-    try {
-      upnpConf = yield network.upnpConf();
-      upnpConf.upnp = true;
-    } catch (e) {
-      logger.error(e.stack || e);
+    // Reconfigure the network if it has not been initialized yet
+    if (!server.conf.remoteipv4 && !server.conf.remoteipv6 && !server.conf.remotehost) {
+      let bestLocal4 = network.getBestLocalIPv4();
+      let bestLocal6 = network.getBestLocalIPv6();
+      let upnpConf = {
+        remoteipv4: bestLocal4,
+        remoteipv6: bestLocal6,
+        upnp: false
+      };
+      try {
+        upnpConf = yield network.upnpConf();
+        upnpConf.upnp = true;
+      } catch (e) {
+        logger.error(e.stack || e);
+      }
+      let randomPort = network.getRandomPort(server.conf);
+      _.extend(server.conf, {
+        ipv4: bestLocal4,
+        ipv6: bestLocal6,
+        port: randomPort,
+        remoteipv4: upnpConf.remoteipv4,
+        remoteipv6: upnpConf.remoteipv6,
+        remoteport: randomPort,
+        upnp: upnpConf.upnp
+      });
+      yield server.dal.saveConf(server.conf);
+      pluggedConfP = co(function *() {
+        yield bmapi.closeConnections();
+        yield server.loadConf();
+        bmapi = yield bma(server, null, true);
+      });
     }
-    let randomPort = network.getRandomPort(server.conf);
-    _.extend(server.conf, {
-      ipv4: bestLocal4,
-      ipv6: bestLocal6,
-      port: randomPort,
-      remoteipv4: upnpConf.remoteipv4,
-      remoteipv6: upnpConf.remoteipv6,
-      remoteport: randomPort,
-      upnp: upnpConf.upnp
-    });
-    yield server.dal.saveConf(server.conf);
-    pluggedConfP = co(function *() {
-      yield bmapi.closeConnections();
-      yield server.loadConf();
-      bmapi = yield bma(server, null, true);
-    });
     return {};
   });
 
   this.startSync = (req) => co(function *() {
-    // Synchronize
-    var remote = new Synchroniser(server, req.body.host, parseInt(req.body.port), server.conf, false);
-    remote.pipe(es.mapSync(function(data) {
+    let sync = server.synchronize(req.body.host, parseInt(req.body.port), parseInt(req.body.to), parseInt(req.body.chunkLen));
+    sync.flow.pipe(es.mapSync(function(data) {
       // Broadcast block
       that.push(data);
     }));
-    yield remote.sync(parseInt(req.body.to), parseInt(req.body.chunkLen));
+    yield sync.syncPromise;
     return {};
   });
 
diff --git a/app/controllers/wot.js b/app/controllers/wot.js
index 6a3857eaf9cf39135d69faa5bf0dc1f2c6965b4c..d24ef622774399f65604b1484e3cb94c2a3dd283 100644
--- a/app/controllers/wot.js
+++ b/app/controllers/wot.js
@@ -1,15 +1,7 @@
 "use strict";
 var co = require('co');
-var async    = require('async');
 var _        = require('underscore');
-var Q        = require('q');
-var moment = require('moment');
-var dos2unix = require('../lib/dos2unix');
 var http2raw = require('../lib/streams/parsers/http2raw');
-var jsoner   = require('../lib/streams/jsoner');
-var parsers  = require('../lib/streams/parsers/doc');
-var es       = require('event-stream');
-var http400  = require('../lib/http/http400');
 var constants = require('../lib/constants');
 var AbstractController = require('./abstract');
 var logger   = require('../lib/logger')();
@@ -63,7 +55,11 @@ function WOTBinding (server) {
         let cert = _.clone(signed[j]);
         if (!(excluding && cert.block <= excluding.number)) {
           cert.idty = yield server.dal.getIdentityByHashOrNull(cert.target);
-          validSigned.push(cert);
+          if (cert.idty) {
+            validSigned.push(cert);
+          } else {
+            logger.debug('A certification to an unknown identity was found (%s => %s)', cert.from, cert.to);
+          }
         }
       }
       idty.signed = validSigned;
@@ -224,9 +220,9 @@ function WOTBinding (server) {
     };
   });
 
-  this.add = (req) => this.pushEntity(req, http2raw.identity, parsers.parseIdentity);
+  this.add = (req) => this.pushEntity(req, http2raw.identity, constants.ENTITY_IDENTITY);
 
-  this.certify = (req) => this.pushEntity(req, http2raw.certification, parsers.parseCertification);
+  this.certify = (req) => this.pushEntity(req, http2raw.certification, constants.ENTITY_CERTIFICATION);
 
-  this.revoke = (req) => this.pushEntity(req, http2raw.revocation, parsers.parseRevocation);
+  this.revoke = (req) => this.pushEntity(req, http2raw.revocation, constants.ENTITY_REVOCATION);
 }
diff --git a/app/lib/blockGenerator.js b/app/lib/blockGenerator.js
index 18a030ca548917e564b93a6566d04f2c8c7e92d9..790f1d26d6658f53fd9d74383179f43e1878ddd7 100644
--- a/app/lib/blockGenerator.js
+++ b/app/lib/blockGenerator.js
@@ -5,6 +5,7 @@ var co              = require('co');
 var Q               = require('q');
 var moment          = require('moment');
 var inquirer        = require('inquirer');
+var rawer           = require('./rawer');
 var hashf           = require('./hashf');
 var constants       = require('./constants');
 var base58          = require('./base58');
@@ -658,6 +659,9 @@ function BlockGenerator(mainContext, prover) {
           }
         }
       }
+      // InnerHash
+      block.time = block.medianTime;
+      block.inner_hash = hashf(rawer.getBlockInnerPart(block)).toUpperCase();
       return block;
     });
   }
diff --git a/app/lib/constants.js b/app/lib/constants.js
index ff878304f66a28b118fa8e743586580a0df9c3b3..a3a3a5b3a4c2bd7da67669d7ccc7e37c3f1e9539 100644
--- a/app/lib/constants.js
+++ b/app/lib/constants.js
@@ -16,7 +16,7 @@ var SIGNATURE    = "[A-Za-z0-9+\\/=]{87,88}";
 var FINGERPRINT  = "[A-F0-9]{64}";
 var COMMENT      = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}";
 var UNLOCK       = "(SIG\\(" + INTEGER + "\\)|XHX\\(" + INTEGER + "\\))";
-var CONDITIONS   = "(&&|\\|\\|| |[()]|(SIG\\([0-9a-zA-Z]{44,45}\\)|(XHX\\([A-F0-9]{64}\\))))*";
+var CONDITIONS   = "(&&|\\|\\|| |[()]|(SIG\\([0-9a-zA-Z]{43,44}\\)|(XHX\\([A-F0-9]{64}\\))))*";
 //var CONDITIONS   = "(&&|\|\|| |[()]|(SIG\\(\\da-zA-Z\\))|(XHX\\(" + FINGERPRINT + "\\)))*";
 var BLOCK_UID    = INTEGER + "-" + FINGERPRINT;
 var META_TS      = "META:TS:" + BLOCK_UID;
@@ -25,7 +25,15 @@ var IPV4_REGEXP = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9
 var IPV6_REGEXP = /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(([0-9A-Fa-f]{1,4}:){0,5}:((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|(::([0-9A-Fa-f]{1,4}:){0,5}((b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b).){3}(b((25[0-5])|(1d{2})|(2[0-4]d)|(d{1,2}))b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/;
 
 module.exports = {
-  
+
+  ENTITY_TRANSACTION: 'transaction',
+  ENTITY_BLOCK: 'block',
+  ENTITY_MEMBERSHIP: 'membership',
+  ENTITY_PEER: 'peer',
+  ENTITY_IDENTITY: 'identity',
+  ENTITY_CERTIFICATION: 'certification',
+  ENTITY_REVOCATION: 'revocation',
+
   ERROR: {
 
     PEER: {
@@ -254,6 +262,8 @@ module.exports = {
     A_MONTH: (3600 * 24 * 365.25) / 12
   },
 
+  DEFAULT_CPU: 0.6,
+
   CONTRACT: {
     DEFAULT: {
       C: 0.007376575,
diff --git a/app/lib/dal/fileDAL.js b/app/lib/dal/fileDAL.js
index 61a3b487a64834dd2d958839be72bf53f0a74f28..bd2c0eb5f07403df7f2a526dad408b5fd3453834 100644
--- a/app/lib/dal/fileDAL.js
+++ b/app/lib/dal/fileDAL.js
@@ -2,7 +2,6 @@
 var Q       = require('q');
 var co      = require('co');
 var _       = require('underscore');
-var path    = require('path');
 var hashf   = require('../hashf');
 var wotb    = require('../wot');
 var logger = require('../logger')('filedal');
@@ -25,7 +24,7 @@ function FileDAL(params) {
 
   let rootPath = params.home;
   let myFS = params.fs;
-  let sqlite = params.db;
+  let sqlite = params.dbf();
   let wotbInstance = params.wotb;
   let that = this;
 
@@ -1143,86 +1142,17 @@ function FileDAL(params) {
     yield _.values(that.newDals).map((dal) => dal.cleanCache && dal.cleanCache());
   });
 
-  this.cleanDBData = () => co(function *() {
-    yield _.values(that.newDals).map((dal) => dal.cleanData && dal.cleanData());
-    that.wotb.resetWoT();
-    var files = ['stats', 'cores', 'current'];
-    var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
-    return resetFiles(files, dirs);
-  });
-
   this.close = () => co(function *() {
     yield _.values(that.newDals).map((dal) => dal.cleanCache && dal.cleanCache());
     return Q.nbind(sqlite.close, sqlite);
   });
 
-  this.resetAll = function(done) {
-    var files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE];
-    var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
-    return resetFiles(files, dirs, done);
-  };
-
-  this.resetData = function(done) {
-    var files = ['stats', 'cores', 'current', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE];
-    var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
-    return resetFiles(files, dirs, done);
-  };
-
-  this.resetConf = function(done) {
-    var files = ['conf'];
-    var dirs  = [];
-    return resetFiles(files, dirs, done);
-  };
-
-  this.resetStats = function(done) {
-    var files = ['stats'];
-    var dirs  = ['ud_history'];
-    return resetFiles(files, dirs, done);
-  };
-
   this.resetPeers = function(done) {
-    var files = [];
-    var dirs  = ['peers'];
     return co(function *() {
       that.peerDAL.removeAll();
-      yield resetFiles(files, dirs);
       return that.close();
     })
       .then(() => done && done())
       .catch((err) => done && done(err));
   };
-
-  this.resetTransactions = function(done) {
-    var files = [];
-    var dirs  = ['txs'];
-    return resetFiles(files, dirs, done);
-  };
-
-  function resetFiles(files, dirs, done) {
-    return co(function *() {
-      for (let i = 0, len = files.length; i < len; i++) {
-        let fName = files[i];
-        // JSON file?
-        let existsJSON = yield myFS.exists(rootPath + '/' + fName + '.json');
-        if (existsJSON) {
-          yield myFS.remove(rootPath + '/' + fName + '.json');
-        } else {
-          // Normal file?
-          let existsFile = yield myFS.exists(rootPath + '/' + fName);
-          if (existsFile) {
-            yield myFS.remove(rootPath + '/' + fName);
-          }
-        }
-      }
-      for (let i = 0, len = dirs.length; i < len; i++) {
-        let dirName = dirs[i];
-        let existsDir = yield myFS.exists(rootPath + '/' + dirName);
-        if (existsDir) {
-          yield myFS.removeTree(rootPath + '/' + dirName);
-        }
-      }
-      done && done();
-    })
-      .catch((err) => done && done(err));
-  }
 }
diff --git a/app/lib/dal/fileDALs/confDAL.js b/app/lib/dal/fileDALs/confDAL.js
index d6671dfb7227014cf9fb82688485e666e34b4fe2..44860d663f7e151f12a5b3dfb16c5652c15aa369 100644
--- a/app/lib/dal/fileDALs/confDAL.js
+++ b/app/lib/dal/fileDALs/confDAL.js
@@ -37,7 +37,7 @@ function ConfDAL(rootPath, qioFS, parentCore, localDAL, AbstractStorage) {
         "msWindow": parseInt(conf.msWindow,10),
         "xpercent": parseFloat(conf.xpercent,10),
         "msValidity": parseInt(conf.msValidity,10),
-        "stepMax": parseInt(3,10), // uCoin only handles 3 step currencies for now
+        "stepMax": parseInt(3,10), // Duniter only handles 3 step currencies for now
         "medianTimeBlocks": parseInt(conf.medianTimeBlocks,10),
         "avgGenTime": parseInt(conf.avgGenTime,10),
         "dtDiffEval": parseInt(conf.dtDiffEval,10),
diff --git a/app/lib/dal/sqliteDAL/BlockDAL.js b/app/lib/dal/sqliteDAL/BlockDAL.js
index 84844c8729e675c50206e0afd5215d74e7084bef..0cda1a19ca5fa8380b52f69bf950d1979bb30d5e 100644
--- a/app/lib/dal/sqliteDAL/BlockDAL.js
+++ b/app/lib/dal/sqliteDAL/BlockDAL.js
@@ -117,10 +117,11 @@ function BlockDAL(db) {
   });
 
   this.saveBlock = (block) => co(function *() {
+    let saved = yield saveBlockAs(block, IS_NOT_FORK);
     if (!current || current.number < block.number) {
       current = block;
     }
-    return saveBlockAs(block, IS_NOT_FORK);
+    return saved;
   });
 
   this.saveSideBlock = (block) =>
diff --git a/app/lib/directory.js b/app/lib/directory.js
index 8c518686fc4ad23034a2a94b9a3803f96d572137..c33f6e840f621b3769998d7c25b389559a16fe69 100644
--- a/app/lib/directory.js
+++ b/app/lib/directory.js
@@ -41,11 +41,11 @@ let dir = module.exports = {
     let home = params.home;
     yield someDelayFix();
     if (isMemory) {
-      params.db = new sqlite3.Database(':memory:');
+      params.dbf = () => new sqlite3.Database(':memory:');
       params.wotb = require('./wot').memoryInstance();
     } else {
       let sqlitePath = path.join(home, dir.UCOIN_DB_NAME + '.db');
-      params.db = new sqlite3.Database(sqlitePath);
+      params.dbf = () => new sqlite3.Database(sqlitePath);
       params.wotb = require('./wot').fileInstance(path.join(home, dir.WOTB_FILE));
     }
     return params;
diff --git a/app/lib/entity/configuration.js b/app/lib/entity/configuration.js
index bd309fda4fb240d6175a3877c6012a0b67c96a01..1d4d9d3ace33f8510763ff3a1186fc543f42980b 100644
--- a/app/lib/entity/configuration.js
+++ b/app/lib/entity/configuration.js
@@ -15,7 +15,7 @@ var defaultConf = function() {
     "remoteport": constants.NETWORK.DEFAULT_PORT,
     "salt": "",
     "passwd": "",
-    "cpu": 0.9,
+    "cpu": constants.DEFAULT_CPU,
     "upInterval": 3600 * 1000,
     "c": constants.CONTRACT.DEFAULT.C,
     "dt": constants.CONTRACT.DEFAULT.DT,
diff --git a/app/lib/network.js b/app/lib/network.js
index 63a9b211432e231af97973ac82c0ef4bf8f684d7..b90e204b38e320fad04db7acbe5f110d8843c0d1 100644
--- a/app/lib/network.js
+++ b/app/lib/network.js
@@ -143,7 +143,7 @@ module.exports = {
     var listenings = interfaces.map(() => false);
 
     if (httpServers.length == 0){
-      throw 'uCoin does not have any interface to listen to.';
+      throw 'Duniter does not have any interface to listen to.';
     }
 
     // Return API
diff --git a/app/lib/proof.js b/app/lib/proof.js
index 4a3040cbc129758df4e7a3d711d7e443993e62e8..8519b47c6deb11c850662d08975b128ddfee36cc 100644
--- a/app/lib/proof.js
+++ b/app/lib/proof.js
@@ -25,7 +25,7 @@ process.on('message', function(stuff){
   var nbZeros = stuff.zeros;
   var pair = stuff.pair;
   var forcedTime = stuff.forcedTime;
-  var cpu = conf.cpu || 1;
+  var cpu = conf.cpu || constants.DEFAULT_CPU;
   var highMark = stuff.highMark;
   async.waterfall([
     function(next) {
diff --git a/app/lib/streams/bma.js b/app/lib/streams/bma.js
index 298ded516472ae5f08dc7b9b8e54bc37c6abfa4c..3786961e2bbe55c910e185359ca47c553198608a 100644
--- a/app/lib/streams/bma.js
+++ b/app/lib/streams/bma.js
@@ -26,7 +26,7 @@ module.exports = function(server, interfaces, httpLogs) {
     }
   }
 
-  return network.createServersAndListen('uCoin server', interfaces, httpLogs, null, (app, httpMethods) => {
+  return network.createServersAndListen('Duniter server', interfaces, httpLogs, null, (app, httpMethods) => {
 
     var node         = require('../../controllers/node')(server);
     var blockchain   = require('../../controllers/blockchain')(server);
@@ -93,8 +93,9 @@ module.exports = function(server, interfaces, httpLogs) {
     wssBlock.on('connection', function connection(ws) {
       co(function *() {
         currentBlock = yield server.dal.getCurrent();
-        wssBlock.broadcast(JSON.stringify(sanitize(currentBlock, dtos.Block)));
-        ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block)));
+        if (currentBlock) {
+          ws.send(JSON.stringify(sanitize(currentBlock, dtos.Block)));
+        }
       });
     });
 
diff --git a/app/lib/streams/webmin.js b/app/lib/streams/webmin.js
index 18acf760506786b49778a168089bc851d03a2633..9fe7f418cb742a4699d0972fb31ee0520fb8b96a 100644
--- a/app/lib/streams/webmin.js
+++ b/app/lib/streams/webmin.js
@@ -13,9 +13,9 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) {
 
   var webminCtrl = require('../../controllers/webmin.controller')(dbConf, overConf);
 
-  var fullPath = path.join(__dirname, '../../../ui/package/public');
+  var fullPath = path.join(__dirname, '../../../web-ui/public');
 
-  return network.createServersAndListen('uCoin web admin', interfaces, httpLogs, fullPath, (app, httpMethods) => {
+  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);
@@ -120,4 +120,9 @@ module.exports = function(dbConf, overConf, interfaces, httpLogs) {
         }
       }));
   });
+
+  return {
+    httpLayer: httpLayer,
+    webminCtrl: webminCtrl
+  };
 };
diff --git a/app/lib/wizard.js b/app/lib/wizard.js
index d519a7ff58cc85368c9131d1740f2c6d95ce1cfe..bde1f9d83c266329bb33fa0f8a6466fe2f6669c5 100644
--- a/app/lib/wizard.js
+++ b/app/lib/wizard.js
@@ -232,9 +232,9 @@ function upnpResolve(noupnp, done) {
   return co(function *() {
     try {
       let conf = yield network.upnpConf(noupnp);
-      done(null, false, conf);
+      done(null, true, conf);
     } catch (err) {
-      done(null, true, {});
+      done(null, false, {});
     }
   });
 }
@@ -483,6 +483,7 @@ function getRemoteNetworkOperations(conf, remoteipv4, remoteipv6, autoconf) {
             next();
           });
         } else {
+          conf.remoteipv6 = answers.remoteipv6;
           next();
         }
       });
diff --git a/app/service/PeeringService.js b/app/service/PeeringService.js
index c55afb203c5df2ccbdfb15d00efd3e213f4f25ec..4d0b5f8eaf0615f7ce5efe9d285c65924fe0a60b 100644
--- a/app/service/PeeringService.js
+++ b/app/service/PeeringService.js
@@ -220,26 +220,19 @@ function PeeringService(server) {
           block: targetBlock ? [targetBlock.number, targetBlock.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
           endpoints: [endpoint]
         };
-        var raw1 = dos2unix(new Peer(p1).getRaw());
         var raw2 = dos2unix(new Peer(p2).getRaw());
-        logger.info('External access:', new Peer(raw1 == raw2 ? p1 : p2).getURL());
-        if (raw1 != raw2) {
-          logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]);
-          p2.signature = yield Q.nfcall(server.sign, raw2);
-          p2.pubkey = selfPubkey;
-          p2.documentType = 'peer';
-          // Submit & share with the network
-          yield server.submitP(p2, false);
-        } else {
-          p1.documentType = 'peer';
-          // Share with the network
-          server.push(p1);
-        }
+        logger.info('External access:', new Peer(p2).getURL());
+        logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]);
+        p2.signature = yield Q.nfcall(server.sign, raw2);
+        p2.pubkey = selfPubkey;
+        p2.documentType = 'peer';
+        // Submit & share with the network
+        yield server.submitP(p2, false);
         let selfPeer = yield dal.getPeer(selfPubkey);
         // Set peer's statut to UP
         selfPeer.documentType = 'selfPeer';
         yield that.peer(selfPeer);
-        server.push(selfPeer);
+        server.streamPush(selfPeer);
         logger.info("Next peering signal in %s min", signalTimeInterval / 1000 / 60);
         done && done();
         return selfPeer;
@@ -309,6 +302,7 @@ function PeeringService(server) {
       try {
         logger.debug('Crawling peers of %s %s', aPeer.pubkey.substr(0, 6), aPeer.getNamedURL());
         let node = yield aPeer.connectP();
+        yield checkPeerValidity(aPeer, node);
         //let remotePeer = yield Q.nbind(node.network.peering.get)();
         let json = yield Q.nbind(node.network.peering.peers.get, node)({ leaves: true });
         for (let i = 0, len = json.leaves.length; i < len; i++) {
@@ -345,6 +339,7 @@ function PeeringService(server) {
               // Now we test
               let node = yield Q.nfcall(p.connect);
               let peering = yield Q.nfcall(node.network.peering.get);
+              yield checkPeerValidity(p, node);
               // The node answered, it is no more DOWN!
               logger.info('Node %s (%s:%s) is UP!', p.pubkey.substr(0, 6), p.getHostPreferDNS(), p.getPort());
               yield dal.setPeerUP(p.pubkey);
@@ -404,6 +399,35 @@ function PeeringService(server) {
     return waitRemaining;
   }
 
+  function checkPeerValidity(p, node) {
+    return co(function *() {
+      try {
+        let document = yield Q.nfcall(node.network.peering.get);
+        let thePeer = Peer.statics.peerize(document);
+        let goodSignature = that.checkPeerSignature(thePeer);
+        if (!goodSignature) {
+          throw 'Signature from a peer must match';
+        }
+        if (p.currency !== thePeer.currency) {
+          throw 'Currency has changed from ' + p.currency + ' to ' + thePeer.currency;
+        }
+        if (p.pubkey !== thePeer.pubkey) {
+          throw 'Public key of the peer has changed from ' + p.pubkey + ' to ' + thePeer.pubkey;
+        }
+        let sp1 = p.block.split('-');
+        let sp2 = thePeer.block.split('-');
+        let blockNumber1 = parseInt(sp1[0]);
+        let blockNumber2 = parseInt(sp2[0]);
+        if (blockNumber2 < blockNumber1) {
+          throw 'Signature date has changed from block ' + blockNumber1 + ' to older block ' + blockNumber2;
+        }
+      } catch (e) {
+        logger.warn(e);
+        throw { code: "E_DUNITER_PEER_CHANGED" };
+      }
+    });
+  }
+
   function syncBlock(callback, pubkey) {
     currentSyncP = co(function *() {
       let current = yield dal.getCurrentBlockOrNull();
@@ -420,6 +444,7 @@ function PeeringService(server) {
           try {
             let node = yield Q.nfcall(p.connect);
             node.pubkey = p.pubkey;
+            yield checkPeerValidity(p, node);
             let dao = pulling.abstractDao({
 
               // Get the local blockchain current block
@@ -450,7 +475,10 @@ function PeeringService(server) {
               }),
 
               // Simulate the adding of a single new block on local blockchain
-              applyMainBranch: (block) => server.BlockchainService.submitBlock(block, true, constants.FORK_ALLOWED),
+              applyMainBranch: (block) => co(function *() {
+                let addedBlock = yield server.BlockchainService.submitBlock(block, true, constants.FORK_ALLOWED);
+                server.streamPush(addedBlock);
+              }),
 
               // Eventually remove forks later on
               removeForks: () => Q(),
@@ -497,7 +525,8 @@ function PeeringService(server) {
 
   function isConnectionError(err) {
     return err && (
-      err.code == "EINVAL"
+      err.code == "E_DUNITER_PEER_CHANGED"
+      || err.code == "EINVAL"
       || err.code == "ECONNREFUSED"
       || err.code == "ETIMEDOUT"
       || (err.httpCode !== undefined && err.httpCode !== 404));
diff --git a/appveyor.yml b/appveyor.yml
index 177d9e74fbdcda1b7f9b362619800825d00fde3b..ea30e94d742cf25eb8300d35f95674f9b1b4182a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,11 +1,13 @@
 environment:
   matrix:
     - nodejs_version: "5.9.1"
+      ADDON_VERSION: "47"
 
 platform:
   - x64
 
 install:
+  - echo %ADDON_VERSION%
   - ps: Install-Product node $env:nodejs_version $env:platform
   - node -v
   - npm install -g npm
@@ -18,36 +20,41 @@ test_script:
 build: off
 
 after_test:
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] powershell .\\ci\\appveyor\\inno_setup.ps1
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %APPDATA%\..\Local\NuGet\Cache
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] choco install -y 7zip InnoSetup
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set PATH="C:\Program Files\7-Zip";"C:\Program Files (x86)\Inno Setup 5";%PATH%
-  # GUI Version
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set PATH="C:\Program Files (x86)\Inno Setup 5";%PATH%
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] git submodule init
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] git submodule update
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd "web-ui"
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] dir
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ..
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set SRC=%cd%
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] echo %SRC%
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy misc\MSVSVersion.py %APPDATA%\npm\node_modules\nw-gyp\gyp\pylib\gyp\MSVSVersion.py
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_VERSION=0.13.0-rc3
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_RELEASE=v0.13.0-rc3
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_VERSION=0.14.5
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] set NW_RELEASE=v0.14.5
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] echo %NW_RELEASE%
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd node_modules/wotb
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\wotb.node %cd%\lib\binding\Release\node-v47-win32-x64\wotb.node /Y
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\wotb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\wotb.node /Y
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../naclb
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\naclb.node %cd%\lib\binding\Release\node-v47-win32-x64\naclb.node /Y
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\naclb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\naclb.node /Y
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../scryptb
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\scryptb.node %cd%\lib\binding\Release\node-v47-win32-x64\scryptb.node /Y
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\scryptb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\scryptb.node /Y
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../sqlite3b
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm install --build-from-source
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\node_sqlite3.node %cd%\lib\binding\Release\node-v47-win32-x64\node_sqlite3.node /Y
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\node_sqlite3.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\node_sqlite3.node /Y
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ../..
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] npm prune --production
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] cd ..
@@ -62,8 +69,8 @@ after_test:
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] mkdir %cd%\duniter_release\sources
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] xcopy %SRC%\gui\* %cd%\duniter_release\nw\ /s /e
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] xcopy %SRC%\* %cd%\duniter_release\sources\ /s /e
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\ui\package\node_modules
-  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\ui\package\bower_components
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\web-ui\node_modules
+  - if [%APPVEYOR_REPO_TAG_NAME%] neq [] rd /s /q %cd%\duniter_release\sources\web-ui\bower_components
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] iscc %cd%\duniter_release\sources\duniter.iss /DROOT_PATH=%cd%\duniter_release
   - if [%APPVEYOR_REPO_TAG_NAME%] neq [] move %cd%\duniter_release\Duniter.exe %cd%\duniter\duniter-%APPVEYOR_REPO_TAG_NAME%-windows-x64.exe
 
@@ -72,7 +79,7 @@ artifacts:
     name: Duniter
 
 deploy:
-  release: v0.20.0a57
+  release: v0.20.0a84
   provider: GitHub
   auth_token:
     secure: Vp/M0r0i1yhGR2nhrPWEbTiDIF6r0cmwbNDFZUzdFe5clWxPXtuC0lgIpOQI78zt
diff --git a/bin/daemon b/bin/daemon
index 48ffe57e80e6a8b0d9c09e7271f0241b3bd2d1fb..e0ba25c994380740dc5cf00d0fcaae7731263ca3 100755
--- a/bin/daemon
+++ b/bin/daemon
@@ -1,19 +1,16 @@
 #!/usr/bin/env node
 "use strict";
 
-var directory = require('../app/lib/directory');
-var path = require('path');
+const directory = require('../app/lib/directory');
+const path = require('path');
+const spawn = require('child_process').spawn;
 
-var daemon = require("daemonize2").setup({
-  main: "ucoind",
-  name: directory.INSTANCE_NAME,
-  pidfile: path.join(directory.INSTANCE_HOME, "app.pid")
-});
+var daemon = getDaemon('start');
 
 switch (process.argv[2]) {
 
   case "start":
-    daemon.start();
+    start(daemon);
     break;
 
   case "stop":
@@ -21,18 +18,33 @@ switch (process.argv[2]) {
     break;
 
   case "restart":
-    daemon = require("daemonize2").setup({
-      main: "ucoind",
-      name: directory.INSTANCE_NAME,
-      pidfile: path.join(directory.INSTANCE_HOME, "app.pid"),
-
-      // We must redefine the main argument to 'start' because uCoin will receive it as command argument and does not
-      // know about 'restart' command.
-      argv: process.argv.slice(2).map((arg, index) => index == 0 ? 'start' : arg)
+    daemon = getDaemon('start');
+    daemon.stop(function(err) {
+      err && console.error(err);
+      start(daemon);
     });
+    break;
+
+  case "webwait":
+    daemon = getDaemon('webwait');
+    start(daemon);
+    break;
+
+  case "webstart":
+    daemon = getDaemon('webstart');
+    start(daemon);
+    break;
+
+  case "webstop":
+    daemon = getDaemon();
+    daemon.stop();
+    break;
+
+  case "webrestart":
+    daemon = getDaemon('webstart');
     daemon.stop(function(err) {
       err && console.error(err);
-      daemon.start();
+      start(daemon);
     });
     break;
 
@@ -42,5 +54,46 @@ switch (process.argv[2]) {
     break;
 
   default:
-    console.log("Usage: [start|stop|restart]");
+    console.log("Usage: [webstart|webwait|webstop|webrestart|start|stop|restart]");
+}
+
+function getDaemon(overrideCommand) {
+  return require("daemonize2").setup({
+    main: "ucoind",
+    name: directory.INSTANCE_NAME,
+    pidfile: path.join(directory.INSTANCE_HOME, "app.pid"),
+
+    // We must redefine the main argument to 'start' because Duniter will receive it as command argument and does not
+    // know about 'restart' command.
+    argv: getCommand(overrideCommand)
+  });
+}
+
+function getCommand(overrideCommand) {
+  return process.argv.slice(2).map((arg, index) => index == 0 && overrideCommand ? overrideCommand : arg);
+}
+
+function getFullCommand(overrideCommand) {
+  let ucoind = path.resolve(path.dirname(process.argv[1]), './ucoind');
+  return [ucoind].concat(getCommand(overrideCommand));
+}
+
+function start(daemonToStart) {
+  let checkConf = spawn(process.argv[0], getFullCommand('check-config'));
+  let echos = [];
+
+  // Error messages
+  checkConf.stdout.on('data', (data) => echos.push(data));
+  // checkConf.stderr.on('data', (data) => console.error(data.toString('utf8')));
+
+  // Result
+  checkConf.on('close', (code) => {
+    if (code !== 0 && code !== '0' && code !== '' && code !== null && code !== undefined) {
+      console.log('Error code \'%s\'', code);
+      echos.forEach((echo) => console.log(echo.toString('utf8').replace(/\n$/, '')));
+      console.log('You have configuration issues. Please fix them and retry to start your node with `duniter restart` or `duniter webrestart`.');
+    } else {
+      daemonToStart.start();
+    }
+  });
 }
diff --git a/bin/ucoind b/bin/ucoind
index 7355a4be3e0ffeb25107d9b1ea15bab5202532ca..56c48ab2bb4dcc1982227a1d64c03e9d30d89c6a 100755
--- a/bin/ucoind
+++ b/bin/ucoind
@@ -23,7 +23,6 @@ var multicaster = require('../app/lib/streams/multicaster');
 var signature   = require('../app/lib/signature');
 var crypto      = require('../app/lib/crypto');
 var base58      = require('../app/lib/base58');
-var Synchroniser = require('../app/lib/sync');
 var pjson       = require('../package.json');
 var ucoin       = require('./../index');
 var Peer        = require('../app/lib/entity/peer');
@@ -36,7 +35,7 @@ program
   .version(pjson.version)
   .usage('<command> [options]')
 
-  .option('--home <path>',         'Path to uCoin HOME (defaults to "$HOME/.config/duniter").')
+  .option('--home <path>',         'Path to Duniter HOME (defaults to "$HOME/.config/duniter").')
   .option('-d, --mdb <name>',          'Database name (defaults to "ucoin_default").')
 
   .option('--autoconf',                'With `init` command, will guess the best network and key options witout aksing for confirmation')
@@ -95,12 +94,17 @@ program
 
 program
   .command('start')
-  .description('Start uCoin server.')
+  .description('Start Duniter server.')
   .action(service(serverStart));
 
+program
+  .command('webwait')
+  .description('Start Duniter web admin.')
+  .action(webWait);
+
 program
   .command('webstart')
-  .description('Start uCoin server + web admin.')
+  .description('Start Duniter web admin + immediately start the server.')
   .action(webStart);
 
 program
@@ -119,7 +123,7 @@ program
 
 program
   .command('sync [host] [port] [to]')
-  .description('Synchronize blockchain from a remote uCoin node')
+  .description('Synchronize blockchain from a remote Duniter node')
   .action(service(function (host, port, to, server, conf) {
     if (!host) {
       throw 'Host is required.';
@@ -129,7 +133,14 @@ program
     }
     return co(function *() {
       try {
-        yield sync(server, host, port, conf, to);
+        let cautious;
+        if (program.nocautious) {
+          cautious = false;
+        }
+        if (program.cautious) {
+          cautious = true;
+        }
+        yield server.synchronize(host, port, parseInt(to), 0, !program.nointeractive, cautious, program.nopeers);
       } catch (err) {
         logger.error(err.stack || err.message);
       }
@@ -138,19 +149,6 @@ program
     });
   }));
 
-function sync(server, host, port, conf, to) {
-  // Synchronize
-  var remote = new Synchroniser(server, host, port, conf, !program.nointeractive);
-  let cautious;
-  if (program.nocautious) {
-    cautious = false;
-  }
-  if (program.cautious) {
-    cautious = true;
-  }
-  return remote.sync(parseInt(to), 0, cautious, program.nopeers);
-}
-
 program
   .command('peer [host] [port]')
   .description('Exchange peerings with another node')
@@ -192,7 +190,7 @@ program
 
 program
   .command('forward [host] [port] [to]')
-  .description('Forward local blockchain to a remote uCoin node')
+  .description('Forward local blockchain to a remote Duniter node')
   .action(service(function (host, port, to, server) {
 
     var remoteCurrent;
@@ -405,7 +403,7 @@ program
       .catch(function(err) {
         logger.warn(err.message || err);
         server.disconnect();
-        process.exit();
+        process.exit(1);
       });
   }));
 
@@ -438,45 +436,44 @@ program
   .command('reset [config|data|peers|tx|stats|all]')
   .description('Reset configuration, data, peers, transactions or everything in the database')
   .action((type) => {
-    let init = type != 'peers' ? connect : service;
+    let init = type == 'data' ? server : connect;
     init(function (server) {
-      if(!~['config', 'data', 'peers', 'tx', 'stats', 'all'].indexOf(type)){
-        logger.error('Bad command: usage `reset config`, `reset data`, `reset peers`, `reset tx`, `reset stats` or `reset all`');
+      if(!~['config', 'data', 'peers', 'stats', 'all'].indexOf(type)){
+        logger.error('Bad command: usage `reset config`, `reset data`, `reset peers`, `reset stats` or `reset all`');
         server.disconnect();
         return;
       }
-      if(type == 'data'){
-        server.resetData(resetDone(server, 'Data successfully reseted.'));
-      }
-      if(type == 'peers'){
-        server.resetPeers(resetDone(server, 'Peers successfully reseted.'));
-      }
-      if(type == 'tx'){
-        server.resetTxs(resetDone(server, 'Transactions successfully reseted.'));
-      }
-      if(type == 'stats'){
-        server.resetStats(resetDone(server, 'Stats successfully reseted.'));
-      }
-      if(type == 'config'){
-        server.resetConf(resetDone(server, 'Configuration successfully reseted.'));
-      }
-      if(type == 'all'){
-        server.reset(resetDone(server, 'Data & Configuration successfully reseted.'));
-      }
+      co(function*(){
+        try {
+          if(type == 'data'){
+            yield server.resetData();
+            logger.warn('Data successfully reseted.');
+          }
+          if(type == 'peers'){
+            yield server.resetPeers();
+            logger.warn('Peers successfully reseted.');
+          }
+          if(type == 'stats'){
+            yield server.resetStats();
+            logger.warn('Stats successfully reseted.');
+          }
+          if(type == 'config'){
+            yield server.resetConf();
+            logger.warn('Configuration successfully reseted.');
+          }
+          if(type == 'all'){
+            yield server.resetAll();
+            logger.warn('Data & Configuration successfully reseted.');
+          }
+        } catch (e) {
+          logger.error(e);
+        }
+        server.disconnect();
+        process.exit();
+      });
     }, type != 'peers')(type);
   });
 
-function resetDone(server, msg) {
-  return function(err) {
-    if(err)
-      logger.error(err);
-    else
-      logger.warn(msg);
-    server.disconnect();
-    process.exit();
-  }
-}
-
 function serverStart(server, conf) {
 
   return co(function *() {
@@ -550,7 +547,7 @@ function getBootstrapOperations(host, port, server, conf) {
   ops = ops.concat([
     function(next) {
       // Reset data
-      server.reset(next);
+      server.resetAll(next);
     },
     function(next) {
       conf.upnp = !program.noupnp;
@@ -732,6 +729,27 @@ function connect(callback, useDefaultConf) {
   };
 }
 
+/**
+ * Super basic server with only its home path set
+ * @param callback
+ * @param useDefaultConf
+ * @returns {Function}
+ */
+function server(callback, useDefaultConf) {
+  return function () {
+    var cbArgs = arguments;
+    var dbName = program.mdb || "duniter_default";
+    var dbHome = program.home;
+
+    var server = ucoin({ home: dbHome, name: dbName }, commandLineConf());
+
+    cbArgs.length--;
+    cbArgs[cbArgs.length++] = server;
+    cbArgs[cbArgs.length++] = server.conf;
+    callback.apply(this, cbArgs);
+  };
+}
+
 function service(callback, nologs) {
 
   return function () {
@@ -807,7 +825,26 @@ program
     process.exit();
   });
 
+function webWait() {
+  return co(function *() {
+    let webminapi = yield webInit();
+    return webminapi.httpLayer.openConnections();
+  })
+    .catch(mainError);
+}
+
 function webStart() {
+  return co(function *() {
+    let webminapi = yield webInit();
+    yield webminapi.httpLayer.openConnections();
+    yield webminapi.webminCtrl.startHTTP();
+    yield webminapi.webminCtrl.startAllServices();
+    yield webminapi.webminCtrl.regularUPnP();
+  })
+    .catch(mainError);
+}
+
+function webInit() {
   return co(function *() {
     var dbName = program.mdb;
     var dbHome = program.home;
@@ -818,9 +855,7 @@ function webStart() {
       // Add log files for this instance
       logger.addHomeLogs(params.home);
     }
-    let webminapi = yield ucoin.statics.enableHttpAdmin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf(), false, program.webmhost, program.webmport);
-    return webminapi.openConnections();
-
+    return yield ucoin.statics.enableHttpAdmin({ home: dbHome, name: dbName, memory: program.memory }, commandLineConf(), false, program.webmhost, program.webmport);
   });
 }
 
@@ -828,13 +863,18 @@ program.parse(process.argv);
 
 if (program.args.length == 0) {
 
-  console.log('No command given, using default: ucoind webstart');
+  console.log('No command given, using default: ucoind webwait');
   return co(function *() {
     try {
-      yield webStart();
+      yield webWait();
     } catch (e) {
       logger.error(e);
       process.exit();
     }
   });
 }
+
+function mainError(err) {
+  logger.error(err.code || err.message || err);
+  process.exit();
+}
diff --git a/ci/appveyor/inno_setup.ps1 b/ci/appveyor/inno_setup.ps1
new file mode 100644
index 0000000000000000000000000000000000000000..aaae7c7b59ca800483ed71353cf94bff00937f92
--- /dev/null
+++ b/ci/appveyor/inno_setup.ps1
@@ -0,0 +1,6 @@
+$exePath = "$env:USERPROFILE\innosetup-5.5.9-unicode.exe"
+Write-Host "Downloading InnoSetup 5.5.9..."
+(New-Object Net.WebClient).DownloadFile('http://files.jrsoftware.org/is/5/innosetup-5.5.9-unicode.exe', $exePath)
+Write-Host "Installing..."
+cmd /c start /wait $exePath /silent
+Write-Host "Installed InnoSetup 5.5.9" -ForegroundColor Green
\ No newline at end of file
diff --git a/ci/arm/make_arm_deb_package.sh b/ci/arm/make_arm_deb_package.sh
new file mode 100644
index 0000000000000000000000000000000000000000..12e96bb7111d9269bde20235fd2236e390f02488
--- /dev/null
+++ b/ci/arm/make_arm_deb_package.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+ARCH=$1
+DUNITER_VER=$2
+NVER="v5.9.1"
+DUNITER_DEB_VER=" $DUNITER_VER"
+echo "$ARCH"
+echo "$NVER"
+echo "$DUNITER_VER"
+echo "$DUNITER_DEB_VER"
+
+echo "Downloading Nodejs"
+wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-${ARCH}.tar.gz
+echo "Extracting Nodejs"
+tar xzf node-${NVER}-linux-${ARCH}.tar.gz
+mv node-${NVER}-linux-${ARCH} node
+rm node-${NVER}-linux-${ARCH}.tar.gz
+
+echo "npm install"
+node/bin/npm install
+node/bin/npm prune --production
+SRC=`pwd`
+echo $SRC
+cd ..
+mkdir -p duniter_release/sources
+cp -R ${SRC}/* duniter_release/sources/
+rm -Rf duniter_release/sources/web-ui/node_modules
+rm -Rf duniter_release/sources/web-ui/bower_components
+
+# Creating DEB packaging
+mv duniter_release/sources/ci/travis/debian duniter-${ARCH}
+mkdir -p duniter-${ARCH}/opt/duniter/
+chmod 755 duniter-${ARCH}/DEBIAN/post*
+chmod 755 duniter-${ARCH}/DEBIAN/pre*
+sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-${ARCH}/DEBIAN/control
+cd duniter_release/sources
+pwd
+rm -Rf .git
+echo "Zipping..."
+zip -qr ../duniter-desktop.nw *
+cd ../..
+mv duniter_release/duniter-desktop.nw duniter-${ARCH}/opt/duniter/
+echo "Making deb package"
+fakeroot dpkg-deb --build duniter-${ARCH}
+mv duniter-${ARCH}.deb duniter-v${DUNITER_VER}-linux-${ARCH}.deb
+echo "Uploading release..."
+./github-release upload -u duniter -r duniter --tag v${DUNITER_VER} --name duniter-v${DUNITER_VER}-linux-${ARCH}.deb --file ./duniter-v${DUNITER_VER}-linux-${ARCH}.deb
diff --git a/ci/travis/before_deploy.sh b/ci/travis/before_deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..afef3627f110eccc116cbd52730fb11a9384f26c
--- /dev/null
+++ b/ci/travis/before_deploy.sh
@@ -0,0 +1,78 @@
+#!/usr/bin/env bash
+
+if [[ ! -f before_deploy ]]; then
+
+  # Process this only once
+  touch before_deploy
+
+  # Clean testing packages
+  npm prune --production
+
+  cd ..
+  cp -R duniter gh_duniter
+
+  #### GitHub release
+  cd gh_duniter
+
+  # Install UI
+  cd web-ui
+  git submodule init
+  git submodule update
+  npm install
+  cd ..
+
+  # Download Node.js
+  NVER=`node -v`
+  DUNITER_VER=`git describe --exact-match --tags $(git log -n1 --pretty='%h') | grep -Po "\d.*"`
+  DUNITER_VER="0.20.0dev"
+  DUNITER_DEB_VER=" $DUNITER_VER"
+  wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz
+  tar xzf node-${NVER}-linux-x64.tar.gz
+  mv node-${NVER}-linux-x64 node
+  rm node-${NVER}-linux-x64.tar.gz
+
+  tar czf ../../ucoin-x64.tar.gz ./ --exclude ".git" --exclude "coverage" --exclude "test"
+  SRC=`pwd`
+  cd ..
+
+  # Install Nw.js
+  mkdir ucoin_release
+  NW_RELEASE="v0.14.5"
+  NW="nwjs-${NW_RELEASE}-linux-x64"
+  NW_GZ="${NW}.tar.gz"
+  wget http://dl.nwjs.io/${NW_RELEASE}/${NW_GZ}
+  tar xvzf ${NW_GZ}
+  mv ${NW} ucoin_release/nw
+  cp ${SRC}/gui/* ucoin_release/nw/
+  cp -R ${SRC}/ ucoin_release/sources/
+  rm -Rf ucoin_release/sources/web-ui/node_modules
+  rm -Rf ucoin_release/sources/web-ui/bower_components
+  cd ucoin_release
+  tar czf ../duniter-x64.tar.gz * --exclude ".git" --exclude "coverage" --exclude "test"
+  cd ..
+
+  # Create .deb tree + package it
+  mv ucoin_release/sources/ci/travis/debian duniter-x64
+  mkdir -p duniter-x64/opt/duniter/
+  chmod 755 duniter-x64/DEBIAN/post*
+  chmod 755 duniter-x64/DEBIAN/pre*
+  sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" duniter-x64/DEBIAN/control
+  cd ucoin_release/sources
+  rm -Rf .git
+  zip -qr ../duniter-desktop.nw *
+  cd ../nw
+  zip -qr ../nw.nwb *
+  cd ../..
+  mv ucoin_release/duniter-desktop.nw duniter-x64/opt/duniter/
+  mv ucoin_release/nw.nwb duniter-x64/opt/duniter/
+  fakeroot dpkg-deb --build duniter-x64
+  mv duniter-x64.deb ../duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.deb
+  mv duniter-x64.tar.gz ../duniter-${TRAVIS_TAG}-${TRAVIS_OS_NAME}-x64.tar.gz
+  pwd
+  ls -al
+  pwd
+  ls -al ../
+
+  ###### NPM release
+  cd ../duniter
+fi
diff --git a/ci/travis/debian/DEBIAN/control b/ci/travis/debian/DEBIAN/control
new file mode 100644
index 0000000000000000000000000000000000000000..c225dcf315d95863eee7a62d722ccabbab19ebe2
--- /dev/null
+++ b/ci/travis/debian/DEBIAN/control
@@ -0,0 +1,8 @@
+Package: duniter
+Version: 0.4
+Section: misc
+Priority: optional
+Architecture: all
+Installed-Size: 200000
+Maintainer: Cedric Moreau <cem.moreau@gmail.com>
+Description: Duniter desktop version
diff --git a/ci/travis/debian/DEBIAN/postinst b/ci/travis/debian/DEBIAN/postinst
new file mode 100755
index 0000000000000000000000000000000000000000..0984d064a30e21031af0f3910d280146da7e7d4b
--- /dev/null
+++ b/ci/travis/debian/DEBIAN/postinst
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+DUN_ROOT=/opt/duniter
+DUN_SOURCES=$DUN_ROOT/sources
+DUN_NW=$DUN_ROOT/nw
+mkdir -p $DUN_SOURCES
+
+# Duniter sources
+if [[ -f $DUN_ROOT/duniter-desktop.nw ]]; then
+  unzip -d $DUN_SOURCES/ $DUN_ROOT/duniter-desktop.nw
+fi
+
+# Duniter-Desktop
+if [[ -f $DUN_ROOT/nw.nwb ]]; then
+  unzip -d $DUN_NW $DUN_ROOT/nw.nwb
+  cp -R $DUN_SOURCES/gui/* $DUN_NW/
+  chmod +x $DUN_NW/nw $DUN_NW/lib $DUN_NW/locales
+  ln -s $DUN_NW/nw /usr/bin/duniter-desktop
+  sed -i "s/DEB_PACKAGING=.*/DEB_PACKAGING=true/g" $DUN_SOURCES/duniter.sh
+fi
+
+# Duniter CLI executes with embedded node
+if [[ -d $DUN_SOURCES/node ]]; then
+  chmod 755 $DUN_SOURCES/bin/ucoind
+  sed -i "s/usr\/bin\/env node/opt\/duniter\/sources\/node\/bin\/node/g" $DUN_SOURCES/bin/ucoind
+  sed -i "s/DEB_PACKAGING=.*/DEB_PACKAGING=true/g" $DUN_SOURCES/duniter.sh
+fi
+# Else will execute with environment node
+
+ln -s $DUN_SOURCES/duniter.sh /usr/bin/duniter
+
+chmod +r -R $DUN_ROOT
diff --git a/ci/travis/debian/DEBIAN/prerm b/ci/travis/debian/DEBIAN/prerm
new file mode 100755
index 0000000000000000000000000000000000000000..d86acf239b6248bfd0870ff7013ba3f0944ad586
--- /dev/null
+++ b/ci/travis/debian/DEBIAN/prerm
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+rm /usr/bin/duniter
+rm /usr/bin/duniter-desktop
+rm -Rf /opt/duniter
diff --git a/ci/travis/debian/usr/share/applications/duniter.desktop b/ci/travis/debian/usr/share/applications/duniter.desktop
new file mode 100644
index 0000000000000000000000000000000000000000..9f25ff4657325871a94e5b0095f9ed149cadbb7c
--- /dev/null
+++ b/ci/travis/debian/usr/share/applications/duniter.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Name=Duniter Desktop
+Exec=duniter-desktop
+Icon=/opt/duniter/nw/duniter.png
+Type=Application
+Categories=Utility
diff --git a/doc/HTTP_API.md b/doc/HTTP_API.md
index 8dcfecdc221893363ccd8825ab24386fe9e8a02e..5fc80a116b24a870141b0a143d356c5afbb0eab9 100644
--- a/doc/HTTP_API.md
+++ b/doc/HTTP_API.md
@@ -1,4 +1,4 @@
-# uCoin HTTP API
+# Duniter HTTP API
 
 ## Contents
 
@@ -56,7 +56,7 @@
 
 ## Overview
 
-Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit uCoin specificities.
+Data is made accessible through an HTTP API mainly inspired from [OpenUDC_exchange_formats draft](https://github.com/Open-UDC/open-udc/blob/master/docs/OpenUDC_exchange_formats.draft.txt), and has been adapted to fit Duniter specificities.
 
     http[s]://Node[:port]/...
     |-- wot/
@@ -106,9 +106,9 @@ Merkle URL is a special kind of URL applicable for resources:
 
 * `network/peering/peers (GET)`
 
-Such kind of URL returns Merkle tree hashes informations. In uCoin, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect.
+Such kind of URL returns Merkle tree hashes informations. In Duniter, Merkle trees are an easy way to detect unsynced data and where the differences come from. For example, `network/peering/peers` is a Merkle tree whose leaves are peers' key fingerprint sorted ascending way. Thus, if any new peer is added, a branch of the tree will see its hash modified and propagated to the root hash. Change is then easy to detect.
 
-For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. uCoin Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them.
+For commodity issues, this URL uses query parameters to retrieve partial data of the tree, as most of the time all the data is not required. Duniter Merkle tree has a determined number of parent nodes (given a number of leaves), which allows to ask only for interval of them.
 
 Here is an example of members Merkle tree with 5 members (taken from [Tree Hash EXchange format (THEX)](http://web.archive.org/web/20080316033726/http://www.open-content.net/specs/draft-jchapweske-thex-02.html)):
 
@@ -128,9 +128,9 @@ Here is an example of members Merkle tree with 5 members (taken from [Tree Hash
 
 Where A,B,C,D,E are already hashed data.
 
-With such a tree structure, uCoin consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.
+With such a tree structure, Duniter consider the tree has exactly 6 nodes: `[ROOT,H,E,F,G,E]`. Nodes are just an array, and for a Lambda Server LS1, it is easy to ask for the values of another server LS2 for level 1 (`H` and `E`, the second level): it requires nodes interval `[1;2]`.
 
-Hence it is quite easy for anyone who wants to check if a `Z` member joined the uCoin community as it would alter the `E` branch of the tree:
+Hence it is quite easy for anyone who wants to check if a `Z` member joined the Duniter community as it would alter the `E` branch of the tree:
 
                         ROOT'=H(H+E')
                         /            \
@@ -197,7 +197,7 @@ Merkle URL result with `leaf=AE4F281DF5A5D0FF3CAD6371F76D5C29B6D953EC`.
 }
 ```
 
-### uCoin Merkle trees leaves
+### Duniter Merkle trees leaves
 
 Each tree manages different data, and has a different goal. Hence, each tree has its own rules on how are generated and sorted tree leaves.
 Here is a summup of such rules:
@@ -1109,7 +1109,7 @@ Top block of each branch, i.e. the last received block of each branch. An array
 
 ### network/*
 
-This URL is used for uCoin Gossip protocol (exchanging UCG messages).
+This URL is used for Duniter Gossip protocol (exchanging UCG messages).
 
 #### `network/peers`
 **Goal**
diff --git a/doc/Protocol.md b/doc/Protocol.md
index 69fc43d593657b63b143ab34ea7f58cc933b0d03..0becce7374d4796949e3bce4420b59caa1cdff5e 100644
--- a/doc/Protocol.md
+++ b/doc/Protocol.md
@@ -1,4 +1,4 @@
-# UCP - uCoin Protocol
+# UCP - Duniter Protocol
 
 > This document is still regularly updated (as of February 2015)
 
@@ -32,7 +32,7 @@
 
 Word                  | Description
 --------------------- | -------------
-UCP                   | Acronym for *UCoin Protocol*. A set of rules to create uCoin based currencies.
+UCP                   | Acronym for *UCoin Protocol*. A set of rules to create Duniter based currencies.
 Signature             | The cryptographical act of certifying a document using a private key.
 WoT                   | Acronym for *Web of Trust*. A groupment of individuals recognizing each other's identity through public keys and certification mechanisms
 UD                    | Acronym for *Universal Dividend*. Means money issuance **directly** and **exclusively** by and to WoT members
@@ -873,7 +873,7 @@ Field | Description
     PROTOCOL_NAME[ OPTIONS]
     [...]
 
-For example, the first written uCoin peering protocol is BASIC_MERKLED_API, which defines an HTTP API. An endpoint of such protocol would look like:
+For example, the first written Duniter peering protocol is BASIC_MERKLED_API, which defines an HTTP API. An endpoint of such protocol would look like:
 
     BASIC_MERKLED_API[ DNS][ IPv4][ IPv6] PORT
 
@@ -1426,10 +1426,10 @@ TRUE
 
 UCP does not imposes a particular API to deal with UCP data. Instead, UCP prefers to allow for any API definition using [Peer](#peer) document, and then leting peers deal themselves with the API(s) they prefer.
 
-At this stage, only [uCoin HTTP API](/HTTP_API.md) (named BASIC_MERKLED_API) is known as a valid UCP API.
+At this stage, only [Duniter HTTP API](/HTTP_API.md) (named BASIC_MERKLED_API) is known as a valid UCP API.
 
 ## References
 
 * [Relative Money Theory](http://fr.wikipedia.org/wiki/Th%C3%A9orie_relative_de_la_monnaie), the theoretical reference behind Universal Dividend
-* [OpenUDC](www.openudc.org), the inspiration project of uCoin
+* [OpenUDC](www.openudc.org), the inspiration project of Duniter
 * [Bitcoin](https://github.com/bitcoin/bitcoin), the well known crypto-currency system
\ No newline at end of file
diff --git a/doc/architecture.md b/doc/architecture.md
index c3a7c246f42a71e4e3250fcda11192e85d69e382..e878af908e4cf8c276420c0c11ba0d47186b5d2a 100644
--- a/doc/architecture.md
+++ b/doc/architecture.md
@@ -2,14 +2,14 @@
 
 ```
          -------------
-         |    BMA    | uCoin Basic Merkled API (HTTP)
+         |    BMA    | Duniter Basic Merkled API (HTTP)
          -------------
                ▲
                |
             Trought
                |
          -------------
-         |   ucoin   | The software
+         |  duniter   | The software
          -------------
                |
                |                                
@@ -17,7 +17,7 @@
                |                                
                ▼                                
        -----------------                        
-       |      UCP      | uCoin protocol         
+       |      UCP      | Duniter protocol
        | ------------- |                        
        | |  Ed25519  | | Cryptography features
        -----------------
@@ -27,7 +27,7 @@ More details on each layer:
 
 Layer | Role
 ----- | ----
-uCoin | The software that implements UCP. May implement UCP throught BMA or any other future protocol.
+Duniter | The software that implements UCP. May implement UCP throught BMA or any other future protocol.
 [BMA](./HTTP_API.md) | A communication protocol to exchange HDC and Network messages over HTTP.
-[UCP](./Protocol.md) | A protocol defining how to handle Network and HDC messages in order to build a uCoin network.
+[UCP](./Protocol.md) | A protocol defining how to handle Network and HDC messages in order to build a Duniter network.
 Ed25519 | Cryptography format providing authentication features.
diff --git a/doc/manual-config.md b/doc/manual-config.md
index 091a7fdd594d1d0cbaa32f9f486caf1280ebb377..94e74a3db7ebbe6913af1a0c1e3d961865c714fb 100644
--- a/doc/manual-config.md
+++ b/doc/manual-config.md
@@ -1,6 +1,6 @@
 # Manual configuration
 
-To add mnually configuration parameters to uCoin, use `config` command:
+To add mnually configuration parameters to Duniter, use `config` command:
 
 ```bash
 $ ucoind config
@@ -8,7 +8,7 @@ $ ucoind config
 
 ## Currency
 
-First of all, tell uCoin which currency to be used through command:
+First of all, tell Duniter which currency to be used through command:
 
 ```bash
 $ ucoind config --currency mycurrency
@@ -45,13 +45,13 @@ Or both:
 $ ucoind config -p 8888 --ipv4 127.0.0.1 --ipv6 ::1
 ```
 
-Launching uCoin (when completely configured) will results:
+Launching Duniter (when completely configured) will results:
 
 ```bash
 $ ucoind start
 
-uCoin server listening on 127.0.0.1 port 8888
-uCoin server listening on ::1 port 8888
+Duniter server listening on 127.0.0.1 port 8888
+Duniter server listening on ::1 port 8888
 ```
 
 Note too that listening to multiple interfaces doesn't imply mutiple program instances: only *one* is running on multiple interfaces.
@@ -60,7 +60,7 @@ Note too that listening to multiple interfaces doesn't imply mutiple program ins
 
 ### Peering informations
 
-uCoin protocol uses peering mecanisms, hence needs any ucoin node to be reachable through the network.
+Duniter protocol uses peering mecanisms, hence needs any ucoin node to be reachable through the network.
 
 As the server may be behind a reverse proxy, or because hosts may change of address, remote informations are likely to be different from listening host and port parameters. ucoin software defines 4 remote parameters you need to precise for your ucoin instance to be working:
 
@@ -75,17 +75,17 @@ You must define at least `--remote4` and `--remotep` not to have any error. Here
 $ ucoind config --remoteh "some.remote.url" --remotep "8844" --remote4 "11.11.11.11" --remote6 "::1"
 ```
 
-Note that this is not required and may be removed in the future, as uCoin protocol already include peering mecanisms giving network informations.
+Note that this is not required and may be removed in the future, as Duniter protocol already include peering mecanisms giving network informations.
 
 ### Authentication
 
-uCoin protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes' messages. To use this feature, just configure uCoin using `--pgpkey` parameter:
+Duniter protocol requires your responses to be signed in order to be interpreted. Such a feature is very important to authenticate nodes' messages. To use this feature, just configure Duniter using `--pgpkey` parameter:
 
 ```bash
 $ ucoind config --pgpkey /path/to/private/key
 ```
 
-Eventually, you might need to give a password, otherwise uCoin will crash:
+Eventually, you might need to give a password, otherwise Duniter will crash:
 
 ```bash
 $ ucoind config --pgppasswd "ultr[A]!%HiGhly-s3cuR3-p4ssw0d"
@@ -97,6 +97,6 @@ Resulting in:
 $ ucoind start
 
 Signed requests with PGP: enabled.
-uCoin server listening on 127.0.0.1 port 8888
-uCoin server listening on ::1 port 8888
+Duniter server listening on 127.0.0.1 port 8888
+Duniter server listening on ::1 port 8888
 ```
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..6218444e3dc6c27cd61463a109d1e6f703bf8e62
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,16 @@
+FROM debian:jessie
+
+RUN apt update
+RUN apt -y install curl git build-essential python
+RUN useradd --create-home -ms /bin/bash duser
+
+USER duser
+WORKDIR /home/duser
+
+RUN curl -kL https://raw.githubusercontent.com/duniter/duniter/master/install.sh | bash
+
+EXPOSE 8999
+
+ADD go /home/duser/go
+
+CMD ["bash", "-i", "/home/duser/go"]
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..aaab6df2f3299ac6c26403a341b5977af10b3558
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,15 @@
+# Duniter in a Docker container
+
+Download `Dockerfile` and `go` files in a repository.
+
+#### Build a container
+
+```sh
+docker build -t="duniter" .
+```
+
+#### Execute the container
+
+```sh
+docker run -p 8999:8999 -dt duniter
+````
diff --git a/docker/go b/docker/go
new file mode 100644
index 0000000000000000000000000000000000000000..504245885ee44025d74608f341f29f8efc1e38ef
--- /dev/null
+++ b/docker/go
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+locale_ip=`awk 'NR==7 {print $1}' /etc/hosts`
+remote_ip=`curl -s https://4.ifcfg.me/`
+
+duniter init --autoconf
+duniter config --noupnp --remote4 $remote_ip --ipv4 $locale_ip
+duniter sync twiced.fr 9330
+duniter start
+tail -f /dev/null
diff --git a/duniter.sh b/duniter.sh
index c40887935f66bef5c74f6f391194a4546368623f..6c30501200e8982e8df456c49c3455e27ac097c2 100755
--- a/duniter.sh
+++ b/duniter.sh
@@ -6,6 +6,12 @@
 # Wraps bin/ucoind.js that is called with Node.js
 #
 
+DEB_PACKAGING=
+
+if [[ $DEB_PACKAGING ]]; then
+  DUNITER_DIR=/opt/duniter/sources/
+fi
+
 duniter() {
 
 	local NODE
@@ -16,6 +22,9 @@ duniter() {
 		### Production mode
 		if [[ -d $DUNITER_DIR/node ]]; then
 			NODE=$DUNITER_DIR/node/bin/node
+	  else
+	    echo "Node.js is not embedded in this version of Duniter"
+	    return
 		fi;
 	else
 
@@ -26,8 +35,8 @@ duniter() {
 
 	VERSION=`$NODE -v`
 
-	if [[ $VERSION != v5* ]]; then
-	  echo "$NODE v5 is required";
+	if [[ $VERSION != v5* && $VERSION != v6* ]]; then
+	  echo "$NODE v5 or v6 is required";
 	else
 
 		case "$1" in
@@ -36,10 +45,14 @@ duniter() {
 		#  UCOIN DAEMON MANAGEMENT
 		#---------------------------------
 
-		start|stop|restart)
+		webwait|webstart|webstop|webrestart|start|stop|restart)
 		$NODE "$DUNITER_DIR/bin/daemon" $*
 		;;
 
+		direct_start)
+		$NODE "$DUNITER_DIR/bin/ucoind" start ${@:2}
+		;;
+
 		logs)
 		LOGS_FILE=`$NODE "$DUNITER_DIR/bin/daemon" $*`
 		tail -f -n 500 "$LOGS_FILE"
@@ -57,7 +70,7 @@ duniter() {
 	fi;
 }
 
-# If the script was launched with parameters, try to launch the uCoin command
+# If the script was launched with parameters, try to launch the Duniter command
 if [ ! -z $1 ]; then
 	duniter $*
 fi
diff --git a/gui/index.html b/gui/index.html
index c0c6de2ce1f247eb76a5721057e1216eaa712a8f..8a23ac8a04cd9170e830efcac14376baf775c118 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.20.0a57</title>
+  <title>Duniter 0.20.0a84</title>
   <style>
     html {
       font-family: "Courier New", Courier, monospace;
diff --git a/gui/package.json b/gui/package.json
index c33128399551a1fb6fdb4802c28aee95321ada82..698cafd629658ec0be165bcb902533e854d1a040 100644
--- a/gui/package.json
+++ b/gui/package.json
@@ -1,10 +1,10 @@
 {
-  "name": "v0.20.0a57",
+  "name": "v0.20.0a84",
   "main": "index.html",
   "node-main": "../sources/bin/ucoind",
   "window": {
     "icon": "duniter.png",
-    "title": "v0.20.0a57",
+    "title": "v0.20.0a84",
     "width": 800,
     "height": 800,
     "min_width": 750,
diff --git a/index.js b/index.js
index d8e001ba1ebb509acaa0bb0fec93bd11cccd2341..6ee44fcab20c650e083eaa1f025bc6d67135a9a9 100644
--- a/index.js
+++ b/index.js
@@ -4,8 +4,6 @@ var co = require('co');
 var Server = require('./server');
 var bma  = require('./app/lib/streams/bma');
 var webmin  = require('./app/lib/streams/webmin');
-var upnp = require('./app/lib/upnp');
-var multicaster = require('./app/lib/streams/multicaster');
 var logger = require('./app/lib/logger')('ucoin');
 
 module.exports = function (dbConf, overConf) {
@@ -27,13 +25,7 @@ module.exports.statics = {
     let bmapi = yield bma(server, null, conf.httplogs);
 
     // Routing documents
-    server
-    // The router asks for multicasting of documents
-      .pipe(server.router())
-      // The documents get sent to peers
-      .pipe(multicaster(server.conf))
-      // The multicaster may answer 'unreachable peer'
-      .pipe(server.router());
+    server.routing();
 
     // Services
     yield module.exports.statics.startServices(server);
@@ -57,7 +49,7 @@ module.exports.statics = {
         if (server.upnpAPI) {
           server.upnpAPI.stopRegular();
         }
-        server.upnpAPI = yield upnp(server.conf.port, server.conf.remoteport);
+        yield server.upnp();
         server.upnpAPI.startRegular();
       } catch (e) {
         logger.warn(e);
diff --git a/install.sh b/install.sh
index 50d92c66643fd16b82fa2dff83bdf88ba111e2f9..b701d927dce38064918bff83fcccb20e00b658bb 100755
--- a/install.sh
+++ b/install.sh
@@ -11,7 +11,7 @@ if [ -z "$DUNITER_DIR" ]; then
 fi
 
 latest_version() {
-  echo "v0.20.0a57"
+  echo "v0.20.0a84"
 }
 
 repo_url() {
@@ -161,7 +161,7 @@ do_install() {
     exit 1
   fi
   if ! is_installed "python"; then
-    echo "=> python is not available. You will likely need to install 'build-essential' package."
+    echo "=> python is not available. You will likely need to install 'python' package."
     exit 1
   fi
 
diff --git a/package.json b/package.json
index 75d37827889bec32a0115864b1e4495ca4dae017..e103d409a35d1d12e5044e4e26638b2fb5cd8ce1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "ucoin",
-  "version": "0.20.0a57",
+  "name": "duniter",
+  "version": "0.20.0a84",
   "engines": {
     "node": ">=4.2.0",
     "npm": ">=2.11"
@@ -8,30 +8,30 @@
   "engineStrict": true,
   "private": false,
   "description": "Crypto-currency software allowing to build P2P free currencies",
-  "main": "bin/ucoin",
+  "main": "index.js",
   "directories": {
     "test": "test"
   },
   "scripts": {
     "test": "mocha --growl --timeout 20000 test test/fast test/fast/block test/integration test/",
     "start": "node bin/ucoind start",
-    "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec --timeout 20000 test test/fast test/fast/block test/integration test/",
-    "postinstall": "cd ui && npm pack ucoin-ui@0.1.24 && tar xzf ucoin-ui-0.1.24.tgz && cd package && npm install && cd .. && rm ucoin-ui-0.1.24.tgz"
+    "test-travis": "node ./node_modules/istanbul/lib/cli.js cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec --timeout 20000 test test/fast test/fast/block test/integration test/"
   },
   "repository": {
     "type": "git",
-    "url": "git@github.com:ucoin-io/ucoin.git"
+    "url": "git@github.com:duniter/duniter.git"
   },
   "keywords": [
-    "openudc",
+    "duniter",
     "ucoin",
+    "openudc",
     "crypto-currency"
   ],
   "author": "Cedric Moreau <cem.moreau@gmail.com>",
   "license": "GPLv3",
-  "readmeFilename": "readme.md",
+  "readmeFilename": "README.md",
   "bugs": {
-    "url": "https://github.com/ucoin-io/ucoin/issues"
+    "url": "https://github.com/duniter/duniter/issues"
   },
   "dependencies": {
     "async": "1.5.2",
@@ -52,7 +52,7 @@
     "moment": "2.6.0",
     "morgan": "1.6.1",
     "multimeter": "0.1.1",
-    "naclb": "1.3.1",
+    "naclb": "1.3.2",
     "nnupnp": "1.0.1",
     "optimist": "0.6.1",
     "q": "1.1.2",
@@ -64,9 +64,9 @@
     "superagent": "1.4.0",
     "tweetnacl": "0.14.1",
     "underscore": "1.8.3",
-    "vucoin": "0.30.3",
+    "vucoin": "0.30.4",
     "winston": "2.1.1",
-    "wotb": "0.4.9",
+    "wotb": "0.4.10",
     "ws": "1.0.1"
   },
   "devDependencies": {
diff --git a/release.sh b/release.sh
index 91e4a55f49b4f6e300ad029e4844902dc9992c0c..35954b5bcef249cfc446abd28e46d38870f3f7fb 100755
--- a/release.sh
+++ b/release.sh
@@ -17,6 +17,9 @@ if [[ $2 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then
       sed -i "s/title\": .*/title\": \"v$2\",/g" gui/package.json
       sed -i "s/<title>Duniter.*<\/title>/<title>Duniter $2<\/title>/g" gui/index.html
 
+      # Bump the install.sh
+      sed -i "s/echo \"v$current\"/echo \"v$2\"/g" install.sh
+
       # (pre)-release management
       if [[ "$1" =~ ^rel$ ]]; then
         # This is RELEASE: change the version in public installer + add the RELEASE flag
@@ -37,10 +40,10 @@ if [[ $2 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then
   git reset HEAD
   case "$1" in
     rel)
-      git add package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html install.sh
+      git add install.sh package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html install.sh
       ;;
     pre)
-      git add package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html
+      git add install.sh package.json .travis.yml appveyor.yml test/integration/branches.js gui/package.json gui/index.html
       ;;
   esac
   git commit -m "v$2"
diff --git a/server.js b/server.js
index 380b9ad54d7dc2071b35e124561ce6e873f23c11..ff3003105b1d639df99717dc4a2fa4cdf6aa330f 100644
--- a/server.js
+++ b/server.js
@@ -2,6 +2,7 @@
 var stream      = require('stream');
 var async       = require('async');
 var util        = require('util');
+var path        = require('path');
 var co          = require('co');
 var _           = require('underscore');
 var Q           = require('q');
@@ -15,14 +16,20 @@ var crypto      = require('./app/lib/crypto');
 var signature   = require('./app/lib/signature');
 var directory   = require('./app/lib/directory');
 var dos2unix    = require('./app/lib/dos2unix');
+var Synchroniser = require('./app/lib/sync');
+var multicaster = require('./app/lib/streams/multicaster');
+var upnp        = require('./app/lib/upnp');
+var bma         = require('./app/lib/streams/bma');
+var rawer       = require('./app/lib/rawer');
 
 function Server (dbConf, overrideConf) {
 
   stream.Duplex.call(this, { objectMode: true });
 
   let home = directory.getHome(dbConf.name, dbConf.home);
-  var logger = require('./app/lib/logger')('server');
-  var that = this;
+  let paramsP = directory.getHomeParams(dbConf && dbConf.memory, home);
+  let logger = require('./app/lib/logger')('server');
+  let that = this;
   that.conf = null;
   that.dal = null;
   that.version = jsonpckg.version;
@@ -37,13 +44,13 @@ function Server (dbConf, overrideConf) {
 
   // Create document mapping
   let documentsMapping = {
-    'identity':      that.IdentityService.submitIdentity,
-    'certification': that.IdentityService.submitCertification,
-    'revocation':    that.IdentityService.submitRevocation,
-    'membership':    that.MembershipService.submitMembership,
-    'peer':          that.PeeringService.submitP,
-    'transaction':   that.TransactionsService.processTx,
-    'block':         _.partial(that.BlockchainService.submitBlock, _, true, constants.NO_FORK_ALLOWED)
+    'identity':      { action: that.IdentityService.submitIdentity,                                               parser: parsers.parseIdentity },
+    'certification': { action: that.IdentityService.submitCertification,                                          parser: parsers.parseCertification},
+    'revocation':    { action: that.IdentityService.submitRevocation,                                             parser: parsers.parseRevocation },
+    'membership':    { action: that.MembershipService.submitMembership,                                           parser: parsers.parseMembership },
+    'peer':          { action: that.PeeringService.submitP,                                                       parser: parsers.parsePeer },
+    'transaction':   { action: that.TransactionsService.processTx,                                                parser: parsers.parseTransaction },
+    'block':         { action: _.partial(that.BlockchainService.submitBlock, _, true, constants.NO_FORK_ALLOWED), parser: parsers.parseBlock }
   };
 
   // Unused, but made mandatory by Duplex interface
@@ -51,9 +58,19 @@ function Server (dbConf, overrideConf) {
 
   this._write = (obj, enc, writeDone) => that.submit(obj, false, () => writeDone);
 
+  /**
+   * Facade method to control what is pushed to the stream (we don't want it to be closed)
+   * @param obj An object to be pushed to the stream.
+   */
+  this.streamPush = (obj) => {
+    if (obj) {
+      that.push(obj);
+    }
+  };
+
   this.plugFileSystem = () => co(function *() {
     logger.debug('Plugging file system...');
-    var params = yield directory.getHomeParams(dbConf && dbConf.memory, home);
+    let params = yield paramsP;
     that.dal = fileDAL(params);
   });
 
@@ -61,7 +78,7 @@ function Server (dbConf, overrideConf) {
     logger.debug('Soft data reset... [cache]');
     yield that.dal.cleanCaches();
     logger.debug('Soft data reset... [data]');
-    yield that.dal.cleanDBData();
+    yield that.cleanDBData();
   });
 
   this.loadConf = (useDefaultConf) => co(function *() {
@@ -71,7 +88,7 @@ function Server (dbConf, overrideConf) {
     var defaultValues = {
       remoteipv6:         that.conf.ipv6,
       remoteport:         that.conf.port,
-      cpu:                1,
+      cpu:                constants.DEFAULT_CPU,
       c:                  constants.CONTRACT.DEFAULT.C,
       dt:                 constants.CONTRACT.DEFAULT.DT,
       ud0:                constants.CONTRACT.DEFAULT.UD0,
@@ -143,7 +160,7 @@ function Server (dbConf, overrideConf) {
         throw 'Document type not given';
       }
       try {
-        let action = documentsMapping[obj.documentType];
+        let action = documentsMapping[obj.documentType].action;
         let res;
         if (typeof action == 'function') {
           // Handle the incoming object
@@ -154,7 +171,7 @@ function Server (dbConf, overrideConf) {
         if (res) {
           // Only emit valid documents
           that.emit(obj.documentType, _.clone(res));
-          that.push(_.clone(res));
+          that.streamPush(_.clone(res));
         }
         if (done) {
           isInnerWrite ? done(null, res) : done();
@@ -282,29 +299,78 @@ function Server (dbConf, overrideConf) {
       });
   };
 
-  this.reset = function(done) {
-    return that.dal.resetAll(done);
+  this.resetAll = function(done) {
+    var files = ['stats', 'cores', 'current', 'conf', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.WOTB_FILE];
+    var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
+    return resetFiles(files, dirs, done);
   };
 
   this.resetData = function(done) {
-    return that.dal.resetData(done);
+    return co(function*(){
+      var files = ['stats', 'cores', 'current', directory.UCOIN_DB_NAME, directory.UCOIN_DB_NAME + '.db', directory.UCOIN_DB_NAME + '.log', directory.WOTB_FILE];
+      var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
+      yield resetFiles(files, dirs, done);
+    });
+  };
+
+  this.resetConf = function(done) {
+    var files = ['conf'];
+    var dirs  = [];
+    return resetFiles(files, dirs, done);
   };
 
   this.resetStats = function(done) {
-    return that.dal.resetStats(done);
+    var files = ['stats'];
+    var dirs  = ['ud_history'];
+    return resetFiles(files, dirs, done);
   };
 
   this.resetPeers = function(done) {
     return that.dal.resetPeers(done);
   };
 
-  this.resetTxs = function(done) {
-    that.dal.resetTransactions(done);
-  };
+  this.cleanDBData = () => co(function *() {
+    yield _.values(that.dal.newDals).map((dal) => dal.cleanData && dal.cleanData());
+    that.dal.wotb.resetWoT();
+    var files = ['stats', 'cores', 'current'];
+    var dirs  = ['blocks', 'ud_history', 'branches', 'certs', 'txs', 'cores', 'sources', 'links', 'ms', 'identities', 'peers', 'indicators', 'leveldb'];
+    return resetFiles(files, dirs);
+  });
 
-  this.resetConf = function(done) {
-    return that.dal.resetConf(done);
-  };
+  function resetFiles(files, dirs, done) {
+    return co(function *() {
+      let params = yield paramsP;
+      let myFS = params.fs;
+      let rootPath = params.home;
+      for (let i = 0, len = files.length; i < len; i++) {
+        let fName = files[i];
+        // JSON file?
+        let existsJSON = yield myFS.exists(rootPath + '/' + fName + '.json');
+        if (existsJSON) {
+          yield myFS.remove(rootPath + '/' + fName + '.json');
+        } else {
+          // Normal file?
+          let normalFile = path.join(rootPath, fName);
+          let existsFile = yield myFS.exists(normalFile);
+          if (existsFile) {
+            yield myFS.remove(normalFile);
+          }
+        }
+      }
+      for (let i = 0, len = dirs.length; i < len; i++) {
+        let dirName = dirs[i];
+        let existsDir = yield myFS.exists(rootPath + '/' + dirName);
+        if (existsDir) {
+          yield myFS.removeTree(rootPath + '/' + dirName);
+        }
+      }
+      done && done();
+    })
+        .catch((err) => {
+          done && done(err);
+          throw err;
+        });
+  }
 
   this.disconnect = function() {
     return that.dal && that.dal.close();
@@ -344,6 +410,64 @@ function Server (dbConf, overrideConf) {
     return theRouter;
   };
 
+  /**
+   * Synchronize the server with another server.
+   *
+   * If local server's blockchain is empty, process a fast sync: **no block is verified in such a case**, unless
+   * you force value `askedCautious` to true.
+   *
+   * @param onHost Syncs on given host.
+   * @param onPort Syncs on given port.
+   * @param upTo Sync up to this number, if `upTo` value is a positive integer.
+   * @param chunkLength Length of each chunk of blocks to download. Kind of buffer size.
+   * @param interactive Tell if the loading bars should be used for console output.
+   * @param askedCautious If true, force the verification of each downloaded block. This is the right way to have a valid blockchain for sure.
+   * @param nopeers If true, sync will omit to retrieve peer documents.
+   */
+  this.synchronize = (onHost, onPort, upTo, chunkLength, interactive, askedCautious, nopeers) => {
+    let remote = new Synchroniser(that, onHost, onPort, that.conf, interactive === true);
+    let syncPromise = remote.sync(upTo, chunkLength, askedCautious, nopeers);
+    return {
+      flow: remote,
+      syncPromise: syncPromise
+    };
+  };
+
+  /**
+   * Enable routing features:
+   *   - The server will try to send documents to the network
+   *   - The server will eventually be notified of network failures
+   */
+  this.routing = () => {
+    // The router asks for multicasting of documents
+    this.pipe(this.router())
+      // The documents get sent to peers
+      .pipe(multicaster(this.conf))
+      // The multicaster may answer 'unreachable peer'
+      .pipe(this.router());
+  };
+
+  this.upnp = () => co(function *() {
+    let upnpAPI = yield upnp(that.conf.port, that.conf.remoteport);
+    that.upnpAPI = upnpAPI;
+    return upnpAPI;
+  });
+  
+  this.listenToTheWeb = (showLogs) => co(function *() {
+    let bmapi = yield bma(that, [{
+      ip: that.conf.ipv4,
+      port: that.conf.port
+    }], showLogs);
+    return bmapi.openConnections();
+  });
+
+  this.rawer = rawer;
+
+  this.writeRaw = (raw, type) => co(function *() {
+    let parser = documentsMapping[type] && documentsMapping[type].parser;
+    let obj = parser.syncWrite(raw);
+    return yield that.singleWritePromise(obj);
+  });
 }
 
 util.inherits(Server, stream.Duplex);
diff --git a/test/fast/tx_format.js b/test/fast/tx_format.js
new file mode 100644
index 0000000000000000000000000000000000000000..95c346820f09fb3ec40f6a7184b04d31a023fea8
--- /dev/null
+++ b/test/fast/tx_format.js
@@ -0,0 +1,28 @@
+"use strict";
+var async   = require('async');
+var should  = require('should');
+var parsers = require('../../app/lib/streams/parsers/doc');
+
+var raw = "Version: 2\n" +
+    "Type: Transaction\n" +
+    "Currency: test_net\n" +
+    "Locktime: 0\n" +
+    "Issuers:\n" +
+    "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\n" +
+    "Inputs:\n" +
+    "D:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:3428\n" +
+    "Unlocks:\n" +
+    "0:SIG(0)\n" +
+    "Outputs:\n" +
+    "1000:0:SIG(yGKRRB18B4eaZQdksWBZubea4VJKFSSpii2okemP7x1)\n" +
+    "99000:0:SIG(HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk)\n" +
+    "Comment: reessai\n" +
+    "P6MxJ/2SdkvNDyIyWuOkTz3MUwsgsfo70j+rpWeQWcm6GdvKQsbplB8482Ar1HMz2q0h5V3tfMqjCuAeWVQ+Ag==\n";
+
+describe("Transaction format", function(){
+
+    var parser = parsers.parseTransaction;
+
+    it('a valid block should be well formatted', () => parser.syncWrite(raw));
+
+});
diff --git a/test/integration/branches.js b/test/integration/branches.js
index 88a178b8b1f701c14c1eee61ba27ce67ef799be1..8a07e5acfbc0cd8f7b356e54ff5a5ce9860dd7ba 100644
--- a/test/integration/branches.js
+++ b/test/integration/branches.js
@@ -39,7 +39,7 @@ describe("Branches", 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.20.0a57');
+        res.should.have.property('duniter').property('version').equal('0.20.0a84');
         res.should.have.property('duniter').property('forkWindowSize').equal(3);
       });
     });
diff --git a/test/integration/lookup.js b/test/integration/lookup.js
new file mode 100644
index 0000000000000000000000000000000000000000..18fe6ee3f1f97b7ace927f4f1e6329a964adf0f2
--- /dev/null
+++ b/test/integration/lookup.js
@@ -0,0 +1,72 @@
+"use strict";
+
+var _         = require('underscore');
+var co        = require('co');
+var ucoin     = require('./../../index');
+var bma       = require('./../../app/lib/streams/bma');
+var user      = require('./tools/user');
+var rp        = require('request-promise');
+var httpTest  = require('./tools/http');
+
+var MEMORY_MODE = true;
+var commonConf = {
+  ipv4: '127.0.0.1',
+  currency: 'bb'
+};
+
+var s1 = ucoin({
+  memory: MEMORY_MODE,
+  name: 'bb12'
+}, _.extend({
+  port: '4452',
+  pair: {
+    pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+    sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+  }
+}, commonConf));
+
+var cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+var tic1 = user('tic1', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+var tic2 = user('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+
+describe("Lookup identity grouping", () => {
+
+  before(() => co(function *() {
+    // Server initialization
+    yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
+
+    // cat is publishing its identity, no problem
+    yield cat.selfCertPromise();
+
+    // tic1 is publishing its identity
+    yield tic1.selfCertPromise();
+
+    // tic2 is publishing its identity, but he has **the same pubkey as tic1**.
+    // This is OK on the protocol side, but the lookup should group the 2 identities
+    // under the same pubkey
+    yield tic2.selfCertPromise();
+  }));
+
+  it('cat should have only 1 identity in 1 pubkey', () => httpTest.expectAnswer(rp('http://127.0.0.1:4452/wot/lookup/cat', { json: true }), (res) => {
+    res.should.have.property('results').length(1);
+    // cat pubkey
+    res.results[0].should.have.property('pubkey').equal('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd');
+    // only 1 UID
+    res.results[0].should.have.property('uids').length(1);
+    // which is cat
+    res.results[0].uids[0].should.have.property('uid').equal('cat');
+  }));
+
+  it.skip('tic should have only 2 identities in 1 pubkey', () => httpTest.expectAnswer(rp('http://127.0.0.1:4452/wot/lookup/tic', { json: true }), (res) => {
+    // We want to have only 1 result for the 2 identities
+    res.should.have.property('results').length(1);
+    // because they share the same pubkey
+    res.results[0].should.have.property('pubkey').equal('DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV');
+    // but got 2 UIDs
+    res.results[0].should.have.property('uids').length(2);
+    // which are tic1
+    res.results[0].uids[0].should.have.property('uid').equal('tic1');
+    // which are tic2
+    res.results[0].uids[1].should.have.property('uid').equal('tic2');
+  }));
+});
diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js
index 78642ac8055e0a242aec070f36a471728108ffc9..4078037671f2cc5336a81721e14ee592b63f4036 100644
--- a/test/integration/tools/node.js
+++ b/test/integration/tools/node.js
@@ -7,7 +7,6 @@ var async  = require('async');
 var request  = require('request');
 var vucoin = require('vucoin');
 var ucoin  = require('../../../index');
-var bma    = require('../../../app/lib/streams/bma');
 var multicaster = require('../../../app/lib/streams/multicaster');
 var Configuration = require('../../../app/lib/entity/configuration');
 var Peer          = require('../../../app/lib/entity/peer');
@@ -221,13 +220,7 @@ function Node (dbName, options) {
         done && done(err);
       });
     })
-      .then(function(server){
-        return bma(server, [{
-          ip: server.conf.ipv4,
-          port: server.conf.port
-        }])
-          .then((bmapi) => bmapi.openConnections());
-      });
+      .then((server) => server.listenToTheWeb());
   };
 
   function service(callback) {
diff --git a/ui/notice.md b/ui/notice.md
deleted file mode 100644
index bb3eea8a56b43f85ad4ce7ece1dd53d9c9d4e1c0..0000000000000000000000000000000000000000
--- a/ui/notice.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# About this directory
-
-It aims at containing ucoin-ui, a web graphical interface which is deployed on NPM and is installed through `npm install`
\ No newline at end of file
diff --git a/web-ui b/web-ui
new file mode 160000
index 0000000000000000000000000000000000000000..da54b8f0c948b18fbadb9fe7e21513186917323e
--- /dev/null
+++ b/web-ui
@@ -0,0 +1 @@
+Subproject commit da54b8f0c948b18fbadb9fe7e21513186917323e