diff --git a/app/lib/rawer.js b/app/lib/rawer.js
index 8b96806b0e2fbba04523ba3de8c1546aeedf08bd..d197bfc3ddc3ad927b71aa98e504b80334cbe7e8 100644
--- a/app/lib/rawer.js
+++ b/app/lib/rawer.js
@@ -145,6 +145,9 @@ module.exports = new function() {
     return unix2dos(signed(that.getMembershipWithoutSignature(json), json));
   };
 
+  var KEYBLOCK_PUBK_PREFIX = "#####-----"
+  var KEYBLOCK_PUBK_SUFFIX = "-----#####"
+
   this.getKeyblockWithoutSignature = function (json) {
     var raw = "";
     raw += "Version: " + json.version + "\n";
@@ -165,8 +168,11 @@ module.exports = new function() {
     }
     raw += "PublicKeys:\n";
     for(var i = 0; i < json.publicKeys.length; i++){
-      raw += '#' + json.publicKeys[i].fingerprint + '\n';
-      raw += json.publicKeys[i].packets;
+      var packets = json.publicKeys[i].packets;
+      raw += KEYBLOCK_PUBK_PREFIX + json.publicKeys[i].fingerprint + KEYBLOCK_PUBK_SUFFIX + '\n';
+      raw += packets;
+      if (!packets.match(/\n$/))
+        raw += '\n';
     }
     raw += "Memberships:\n";
     for(var i = 0; i < json.memberships.length; i++){
@@ -174,8 +180,11 @@ module.exports = new function() {
     }
     raw += "MembershipsSignatures:\n";
     for(var i = 0; i < json.membershipsSigs.length; i++){
-      raw += '#' + json.membershipsSigs[i].fingerprint + '\n';
-      raw += json.membershipsSigs[i].packets;
+      var packets = json.membershipsSigs[i].packets;
+      raw += KEYBLOCK_PUBK_PREFIX + json.membershipsSigs[i].fingerprint + KEYBLOCK_PUBK_SUFFIX + '\n';
+      raw += packets;
+      if (!packets.match(/\n$/))
+        raw += '\n';
     }
     return unix2dos(raw);
   };
diff --git a/app/lib/signature.js b/app/lib/signature.js
new file mode 100644
index 0000000000000000000000000000000000000000..58198f38dd7874eef6b33c0f319a6a9a9c8ea025
--- /dev/null
+++ b/app/lib/signature.js
@@ -0,0 +1,42 @@
+var async     = require('async');
+var openpgp   = require('openpgp');
+var jpgp      = require('./jpgp');
+var logger    = require('./logger')('peerserver');
+
+module.exports = function (armoredPrivateKey, password, withOpenPGPJS, done) {
+  var privateKey = openpgp.key.readArmored(armoredPrivateKey).keys[0];
+  var fingerprint = privateKey.getKeyPacket().getFingerprint().toUpperCase();
+  async.waterfall([
+    function (next) {
+      if (withOpenPGPJS) {
+        var pgp = jpgp();
+        privateKey.decrypt(password);
+        var signingFunc = async.apply(pgp.sign.bind(pgp.sign), privateKey);
+        next(null, function (message, done) {
+          jpgp().sign(message, privateKey, done);
+        });
+      } else {
+        var asciiPrivateKey = armoredPrivateKey;
+        var keyring = '~/.gnupg/ucoin_' + fingerprint;
+        logger.debug("Keyring = %s", keyring);
+        var gnupg = new (require('./gnupg'))(asciiPrivateKey, password, fingerprint, keyring);
+        gnupg.init(function (err) {
+          next(err, function (message, done) {
+            gnupg.sign(message, done);
+          });
+        });
+      }
+    },
+    function (signFunc, next){
+      try{
+        signFunc("some test\nwith line return", function (err) {
+          next(err, signFunc);
+        });
+      } catch(ex){
+        next("Wrong private key password.");
+      }
+    },
+  ], function (err, signFunc) {
+    done(err, signFunc);
+  });
+};
diff --git a/app/lib/streams/multicaster.js b/app/lib/streams/multicaster.js
index 37110b609c00c7d8b81d3c9b312ad475beeb77d9..fa757ee34819726c49f2404ab99a5dc1157176f5 100644
--- a/app/lib/streams/multicaster.js
+++ b/app/lib/streams/multicaster.js
@@ -130,6 +130,7 @@ function Multicaster () {
   });
 
   this.sendPubkey = sendPubkey;
+  this.sendKeyblock = sendKeyblock;
 }
 
 util.inherits(Multicaster, stream.Transform);
@@ -143,6 +144,15 @@ function sendPubkey(peer, pubkey, done) {
   }, done);
 }
 
+function sendKeyblock(peer, keyblock, done) {
+  var keyID = peer.keyID();
+  logger.info('POST keyblock to %s', keyID.match(/\?/) ? peer.getURL() : keyID);
+  post(peer, '/keychain/keyblock', {
+    "keyblock": keyblock.getRaw(),
+    "signature": keyblock.signature
+  }, done);
+}
+
 function sendTransaction(peer, transaction, done) {
   logger.info('POST transaction to %s', peer.keyID());
   post(peer, '/hdc/transactions/process', {
diff --git a/app/lib/streams/parsers/doc/keyblock.js b/app/lib/streams/parsers/doc/keyblock.js
index 465c377a7edf83e1e5410c8b39b82fb01b04eaa8..f37d45857db1fd6c40a7fc70f68af5fa43846114 100644
--- a/app/lib/streams/parsers/doc/keyblock.js
+++ b/app/lib/streams/parsers/doc/keyblock.js
@@ -114,11 +114,11 @@ function extractFingerprintSeparatedPackets(raw) {
   var lines = raw.split(/\n/);
   var nbKeys = 0;
   lines.forEach(function(line){
-    if (line.match(/^#[A-Z0-9]{40}$/)) {
+    if (line.match(/^#####-----[A-Z0-9]{40}-----#####$/)) {
       // New key block
       packetsByFPR.push({
         number: nbKeys++,
-        fingerprint: line.substring(1),
+        fingerprint: line.substring(10, 50),
         packets: ""
       });
     } else if (line.match(/^[A-Za-z0-9\/+=]{1,64}$/) && nbKeys > 0) {
diff --git a/app/models/keyblock.js b/app/models/keyblock.js
index f5874ab1796bad1598229433a3089827b1a3c177..bc438e5b8e5aac2ffe7a2f3a584740437f1cf7a7 100644
--- a/app/models/keyblock.js
+++ b/app/models/keyblock.js
@@ -258,6 +258,10 @@ KeyBlockSchema.methods = {
   },
 
   getRaw: function() {
+    return require('../lib/rawer').getKeyblockWithoutSignature(this);
+  },
+
+  getRawSigned: function() {
     return require('../lib/rawer').getKeyblock(this);
   },
 
diff --git a/app/models/peer.js b/app/models/peer.js
index 18d1d60ad13a30596e3fbcad17560b43e440bb31..56f8b59e5fb8d2580533a8fd1cfbac2cd2086792 100644
--- a/app/models/peer.js
+++ b/app/models/peer.js
@@ -15,7 +15,7 @@ var STATUS = {
   DOWN: "DOWN",
   NOTHING: "NOTHING"
 };
-var BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]+))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
+var BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
 
 var PeerSchema = new Schema({
   version: String,
diff --git a/app/service/KeychainService.js b/app/service/KeychainService.js
index 4d1437572251a27b9c5ab9109bbf0a41fccb5c9a..7fb2c3d6b140c88173a7e33ff9d41bec7b6b8d9d 100644
--- a/app/service/KeychainService.js
+++ b/app/service/KeychainService.js
@@ -2,10 +2,13 @@ var jpgp     = require('../lib/jpgp');
 var async    = require('async');
 var _        = require('underscore');
 var openpgp  = require('openpgp');
+var merkle   = require('merkle');
 var base64   = require('../lib/base64');
 var unix2dos = require('../lib/unix2dos');
+var dos2unix = require('../lib/dos2unix');
 var parsers  = require('../lib/streams/parsers/doc');
-var mlogger = require('../lib/logger')('membership');
+var logger   = require('../lib/logger')('membership');
+var moment   = require('moment');
 
 module.exports.get = function (conn, conf, PublicKeyService) {
   return new KeyService(conn, conf, PublicKeyService);
@@ -34,7 +37,7 @@ function KeyService (conn, conf, PublicKeyService) {
     var entry = new Membership(ms);
     async.waterfall([
       function (next){
-        mlogger.debug('⬇ %s %s', entry.issuer, entry.membership);
+        logger.debug('⬇ %s %s', entry.issuer, entry.membership);
         // Get already existing Membership with same parameters
         Membership.getForHashAndIssuer(entry.hash, entry.issuer, next);
       },
@@ -51,7 +54,7 @@ function KeyService (conn, conf, PublicKeyService) {
         });
       },
       function (next){
-        mlogger.debug('✔ %s %s', entry.issuer, entry.membership);
+        logger.debug('✔ %s %s', entry.issuer, entry.membership);
         next(null, entry);
       }
     ], done);
@@ -581,6 +584,37 @@ function KeyService (conn, conf, PublicKeyService) {
       return certifs;
     };
 
+    this.getPurePubkey = function () {
+      return new openpgp.key.Key(this.getPurePackets());
+    };
+
+    // Get signatories' certification packet of the userid (not checked yet)
+    this.getPurePackets = function () {
+      var purePackets = [];
+      var packets = this.packets.filterByTag(
+        openpgp.enums.packet.publicKey,
+        openpgp.enums.packet.publicSubkey,
+        openpgp.enums.packet.userid,
+        openpgp.enums.packet.signature);
+      packets.forEach(function(packet){
+        var signaturesToKeep = [
+          openpgp.enums.signature.cert_generic,
+          openpgp.enums.signature.cert_persona,
+          openpgp.enums.signature.cert_casual,
+          openpgp.enums.signature.cert_positive,
+          openpgp.enums.signature.subkey_binding
+        ];
+        if (~signaturesToKeep.indexOf(packet.signatureType)) {
+          var issuerKeyId = packet.issuerKeyId.toHex().toUpperCase();
+          var isSelfSig = fingerprint.match(new RegExp(issuerKeyId + '$'));
+          if (isSelfSig) {
+            purePackets.push(packet);
+          }
+        }
+      });
+      return purePackets;
+    };
+
     this.getPubKey = function () {
       return new openpgp.key.Key(this.packets);
     };
@@ -595,4 +629,127 @@ function KeyService (conn, conf, PublicKeyService) {
       return armor;
     }
   }
+
+  this.current = function (done) {
+    KeyBlock.current(function (err, kb) {
+      done(err, kb || null);
+    })
+  }
+
+  this.generateRoot = function (uids, done) {
+    var joinData = {};
+    var fingerprints = [];
+    async.forEach(uids, function(uid, callback){
+      var join = { pubkey: null, ms: null };
+      async.waterfall([
+        function (next){
+          Membership.find({ userid: uid }, next);
+        },
+        function (mss, next){
+          if (mss.length == 0) {
+            next('Membership not found?!')
+            return;
+          }
+          else if (mss.length > 1) {
+            next('Multiple membership found! Stopping.')
+            return;
+          }
+          else {
+            join.ms = mss[0];
+            fingerprints.push(join.ms.issuer);
+            PublicKey.getTheOne(join.ms.issuer, next);
+          }
+        },
+        function (pubk, next){
+          join.pubkey = pubk;
+          joinData[join.pubkey.fingerprint] = join;
+          next();
+        },
+      ], callback);
+    }, function(err){
+      var block = new KeyBlock();
+      block.version = 1;
+      block.currency = joinData[fingerprints[0]].ms.currency;
+      block.number = 0;
+      // Members merkle
+      fingerprints.sort();
+      var tree = merkle(fingerprints, 'sha1').process();
+      block.membersCount = fingerprints.length;
+      block.membersRoot = tree.root();
+      block.membersChanges = [];
+      fingerprints.forEach(function(fpr){
+        block.membersChanges.push('+' + fpr);
+      });
+      // Public keys
+      block.publicKeys = [];
+      _(joinData).values().forEach(function(join){
+        var key = openpgp.key.readArmored(join.pubkey.raw).keys[0];
+        var pkData = {
+          fingerprint: join.pubkey.fingerprint,
+          packets: base64.encode(key.toPacketlist().write())
+        };
+        block.publicKeys.push(pkData);
+      });
+      // Memberships
+      block.memberships = [];
+      _(joinData).values().forEach(function(join){
+        var ms = join.ms;
+        var shortMS = [1, join.pubkey.fingerprint, 'IN', ms.date.timestamp(), ms.userid].join(':');
+        block.memberships.push(shortMS);
+      });
+      // Memberships signatures
+      block.membershipsSigs = [];
+      _(joinData).values().forEach(function(join){
+        var ms = join.ms;
+        var splits = dos2unix(ms.signature).split('\n');
+        var signature = "";
+        var keep = false;
+        splits.forEach(function(line){
+          if (keep && !line.match('-----END PGP') && line != '') signature += line + '\n';
+          if (line == "") keep = true;
+        });
+        block.membershipsSigs.push({
+          fingerprint: join.pubkey.fingerprint,
+          packets: signature
+        });
+      });
+      done(null, block);
+    });
+  };
+
+  this.prove = function (block, sigFunc, nbZeros, done) {
+    var powRegexp = new RegExp('^0{' + nbZeros + '}');
+    var pow = "", sig = "", raw = "";
+    var start = new Date().timestamp();
+    var testsCount = 0;
+    logger.debug('Generating proof-of-work...');
+    async.whilst(
+      function(){ return !pow.match(powRegexp); },
+      function (next) {
+        var newTS = new Date().timestamp();
+        if (newTS == block.timestamp) {
+          block.nonce++;
+        } else {
+          block.nonce = 0;
+          block.timestamp = newTS;
+        }
+        raw = block.getRaw();
+        sigFunc(raw, function (err, sigResult) {
+          sig = unix2dos(sigResult);
+          var full = raw + sig;
+          pow = full.hash();
+          testsCount++;
+          if (testsCount % 100 == 0) process.stdout.write('.');
+          next();
+        });
+      }, function (err) {
+        console.log(raw);
+        block.signature = sig;
+        var end = new Date().timestamp();
+        var duration = moment.duration((end - start)) + 's';
+        var testsPerSecond = (testsCount / (end - start)).toFixed(2);
+        logger.debug('Done: ' + pow + ' in ' + duration + ' (~' + testsPerSecond + ' tests/s)');
+        done(err, block);
+      });
+  };
 }
diff --git a/bin/ucoind b/bin/ucoind
index 3d6727c7ebc6eb5a4bc4ce1a1cb48d2e5b50e9a2..b0bfd864b8ec9ebdf39ec0d138b1168f489c6fe8 100755
--- a/bin/ucoind
+++ b/bin/ucoind
@@ -13,6 +13,7 @@ var wizard      = require('../app/lib/wizard');
 var router      = require('../app/lib/streams/router');
 var multicaster = require('../app/lib/streams/multicaster');
 var logger      = require('../app/lib/logger')('ucoind');
+var signature   = require('../app/lib/signature');
 var ucoin       = require('./..');
 
 function keys (val) {
@@ -220,6 +221,86 @@ function handleKey (server, key, isManaged, message) {
   });
 }
 
+program
+  .command('gen-root [host] [port] [difficulty]')
+  .description('Tries to generate the root keyblock of the keychain using already received keys & memberships')
+  .action(service(DO_NOT_LISTEN_HTTP, ucoin.createWOTServer, function (host, port, difficulty, server, conf) {
+    var Membership = server.conn.model('Membership');
+    var KeychainService = server.KeychainService;
+    async.waterfall([
+      function (next){
+        if (!host || !port) {
+          next('usage: gen-root [host] [port]');
+          return;
+        }
+        KeychainService.current(function (err, current) {
+          if (current) {
+            next('Local keychain is already started.');
+            return;
+          }
+          else next();
+        })
+      },
+      function (next){
+        Membership.find({}, next);
+      },
+      function (mss, next){
+        var uids = [];
+        mss.forEach(function(ms){
+          uids.push(ms.userid);
+        });
+        inquirer.prompt([{
+          type: "checkbox",
+          name: "uids",
+          message: "Initial members of the Web of Trust",
+          choices: uids,
+          default: uids[0]
+        }], function (answers) {
+          next(null, answers.uids);
+        });
+      },
+      function (uids, next){
+        if (uids.length == 0) {
+          next('You must select at least 1 user');
+          return;
+        }
+        KeychainService.generateRoot(uids, next);
+      },
+      function (root, next){
+        var wiz = wizard(server);
+        async.waterfall([
+          function (next){
+            wiz.configOpenpgp(conf, next);
+          },
+          function (next){
+            wiz.configKey(conf, next);
+          },
+          function (next){
+            signature(conf.pgpkey, conf.pgppasswd, conf.openpgpjs, next);
+          },
+          function (sigFunc, next){
+            KeychainService.prove(root, sigFunc, difficulty, next);
+          },
+          function (block, next){
+            var Peer = server.conn.model('Peer');
+            var peer = new Peer({
+              endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
+            });
+            // console.log(block.getRaw());
+            // console.log(block.signature);
+            multicaster().sendKeyblock(peer, block, next);
+          },
+        ], next);
+      },
+    ], function (err) {
+      if (err) {
+        logger.error(err);
+      }
+      server.disconnect();
+      process.exit();
+    });
+  }));
+
 program
   .command('check-config')
   .description('Checks the node\'s configuration')
diff --git a/peerserver.js b/peerserver.js
index 0b755b3cb56e9322bd5bc448cf6929c42531f7e5..9593a65b528a1c321cc528997876b5a78a666c04 100644
--- a/peerserver.js
+++ b/peerserver.js
@@ -8,7 +8,8 @@ var plogger   = require('./app/lib/logger')('peer');
 var flogger   = require('./app/lib/logger')('forward');
 var slogger   = require('./app/lib/logger')('status');
 var wlogger   = require('./app/lib/logger')('wallet');
-var WOT = require('./wotserver');
+var WOT       = require('./wotserver');
+var signature = require('./app/lib/signature');
 var parsers   = require('./app/lib/streams/parsers/doc');
 
 function PeerServer (dbConf, overrideConf, interceptors, onInit) {
@@ -178,37 +179,8 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) {
   };
 
   this.createSignFunction = function (conf, done) {
-    async.waterfall([
-      function (next) {
-        if (conf.openpgpjs) {
-          var pgp = jpgp();
-          var privateKey = openpgp.key.readArmored(conf.pgpkey).keys[0];
-          privateKey.decrypt(conf.pgppasswd);
-          var signingFunc = async.apply(pgp.sign.bind(pgp.sign), privateKey);
-          next(null, function (message, done) {
-            jpgp().sign(message, privateKey, done);
-          });
-        } else {
-          var asciiPrivateKey = conf.pgpkey;
-          var keyring = '~/.gnupg/ucoin_' + that.PeeringService.cert.fingerprint;
-          logger.debug("Keyring = %s", keyring);
-          var gnupg = new (require('./app/lib/gnupg'))(asciiPrivateKey, conf.pgppasswd, that.PeeringService.cert.fingerprint, keyring);
-          gnupg.init(function (err) {
-            next(err, function (message, done) {
-              gnupg.sign(message, done);
-            });
-          });
-        }
-      },
-      function (signFunc, next){
-        that.sign = signFunc;
-        try{
-          that.sign("some test\nwith line return", next);
-        } catch(ex){
-          next("Wrong private key password.");
-        }
-      },
-    ], function (err) {
+    signature(conf.pgpkey, conf.pgppasswd, conf.openpgpjs, function (err, sigFunc) {
+      that.sign = sigFunc;
       done(err);
     });
   }