diff --git a/app/lib/common-libs/constants.ts b/app/lib/common-libs/constants.ts
index b9176d89a3cf765caf0f1f31924c7c7abd0e01e5..67e5f12c053daa6058dee571296c45aa5e18d076 100644
--- a/app/lib/common-libs/constants.ts
+++ b/app/lib/common-libs/constants.ts
@@ -7,16 +7,20 @@ const SIGNATURE    = "[A-Za-z0-9+\\/=]{87,88}"
 const USER_ID      = "[A-Za-z0-9_-]{2,100}"
 const INTEGER      = "(0|[1-9]\\d{0,18})"
 const FINGERPRINT  = "[A-F0-9]{64}"
-const BLOCK_UID    = INTEGER + "-" + FINGERPRINT
 const BLOCK_VERSION = "(10)"
 const TX_VERSION   = "(10)"
 const DIVIDEND     = "[1-9][0-9]{0,5}"
 const ZERO_OR_POSITIVE_INT = "0|[1-9][0-9]{0,18}"
+const BLOCK_UID    = "(" + ZERO_OR_POSITIVE_INT + ")-" + FINGERPRINT
 const RELATIVE_INTEGER = "(0|-?[1-9]\\d{0,18})"
 const FLOAT        = "\\d+\.\\d+"
 const POSITIVE_INT = "[1-9][0-9]{0,18}"
 const TIMESTAMP    = "[1-9][0-9]{0,18}"
 const BOOLEAN      = "[01]"
+const WS2PID       = "[0-9a-f]{8}"
+const SOFTWARE     = "[a-z0-9]{2,15}"
+const SOFT_VERSION = "[0-9a-z.-_]{2,15}"
+const POW_PREFIX   = "([1-9]|[1-9][0-9]|[1-8][0-9][0-9])" // 1-899
 const SPECIAL_BLOCK = '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'
 const META_TS      = "META:TS:" + BLOCK_UID
 const COMMENT      = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}"
@@ -68,7 +72,12 @@ export const CommonConstants = {
     INTEGER,
     BLOCKSTAMP: BLOCK_UID,
     FINGERPRINT,
-    TIMESTAMP
+    TIMESTAMP,
+    WS2PID,
+    SOFTWARE,
+    SOFT_VERSION,
+    POW_PREFIX,
+    ZERO_OR_POSITIVE_INT
   },
 
   BLOCK_GENERATED_VERSION: 10,
diff --git a/app/lib/common/package.ts b/app/lib/common/package.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6f2b557374efed2c239a580a1e2723797562750e
--- /dev/null
+++ b/app/lib/common/package.ts
@@ -0,0 +1,22 @@
+
+export class Package {
+
+  private json:{ version:string }
+
+  private constructor() {
+    this.json = require('../../../package.json')
+  }
+
+  get version() {
+    return this.json.version
+  }
+
+  private static instance:Package
+
+  static getInstance() {
+    if (!Package.instance) {
+      Package.instance = new Package()
+    }
+    return Package.instance
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts
index a860036fd6d388157ebdeff39505e7558e9fea29..5543a640bb62425a8da21202ff419a30355418ac 100644
--- a/app/modules/ws2p/lib/WS2PCluster.ts
+++ b/app/modules/ws2p/lib/WS2PCluster.ts
@@ -14,6 +14,8 @@ import {Key, verify} from "../../../lib/common-libs/crypto/keyring"
 import {WS2PServerMessageHandler} from "./interface/WS2PServerMessageHandler"
 import {WS2PMessageHandler} from "./impl/WS2PMessageHandler"
 import { CommonConstants } from "../../../lib/common-libs/constants";
+import { Package } from "../../../lib/common/package";
+import { Constants } from "../../prover/lib/constants";
 
 const es = require('event-stream')
 const nuuid = require('node-uuid')
@@ -57,7 +59,7 @@ export class WS2PCluster {
   private memberkeysCache:{ [k:string]: number } = {}
 
   // A cache of the current HEAD for a given pubkey
-  private headsCache:{ [pubkey:string]: { blockstamp:string, message:string, sig:string } } = {}
+  private headsCache:{ [ws2pFullId:string]: { blockstamp:string, message:string, sig:string } } = {}
 
   // A buffer of "to be sent" heads
   private newHeads:{ message:string, sig:string }[] = []
@@ -75,19 +77,21 @@ export class WS2PCluster {
 
   async getKnownHeads(): Promise<WS2PHead[]> {
     const heads:WS2PHead[] = []
+    const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '000000'
     const localPub = this.server.conf.pair.pub
-    if (!this.headsCache[localPub]) {
+    const fullId = [localPub, ws2pId].join('-')
+    if (!this.headsCache[fullId]) {
       const current = await this.server.dal.getCurrentBlockOrNull()
       if (current) {
         const { sig, message } = this.sayHeadChangedTo(current.number, current.hash)
         const blockstamp = [current.number, current.hash].join('-')
-        this.headsCache[localPub] = { blockstamp, message, sig }
+        this.headsCache[fullId] = { blockstamp, message, sig }
       }
     }
-    for (const pubkey of Object.keys(this.headsCache)) {
+    for (const ws2pFullId of Object.keys(this.headsCache)) {
       heads.push({
-        message: this.headsCache[pubkey].message,
-        sig: this.headsCache[pubkey].sig
+        message: this.headsCache[ws2pFullId].message,
+        sig: this.headsCache[ws2pFullId].sig
       })
     }
     return heads
@@ -101,20 +105,58 @@ export class WS2PCluster {
       if (!message) {
         throw "EMPTY_MESSAGE_FOR_HEAD"
       }
-      if (message.match(WS2PConstants.HEAD_REGEXP)) {
+      if (message.match(WS2PConstants.HEAD_V0_REGEXP)) {
         const [,, pub, blockstamp]:string[] = message.split(':')
+        const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '000000'
+        const fullId = [pub, ws2pId].join('-')
         const sigOK = verify(message, sig, pub)
         if (sigOK) {
           // Already known?
-          if (!this.headsCache[pub] || this.headsCache[pub].blockstamp !== blockstamp) {
+          if (!this.headsCache[fullId] || this.headsCache[fullId].blockstamp !== blockstamp) {
             // More recent?
-            if (!this.headsCache[pub] || parseInt(this.headsCache[pub].blockstamp) < parseInt(blockstamp)) {
+            if (!this.headsCache[fullId] || parseInt(this.headsCache[fullId].blockstamp) < parseInt(blockstamp)) {
               // Check that issuer is a member and that the block exists
               const memberKey = await this.isMemberKey(pub)
               if (memberKey) {
                 const exists = await this.existsBlock(blockstamp)
                 if (exists) {
-                  this.headsCache[pub] = { blockstamp, message, sig }
+                  this.headsCache[fullId] = { blockstamp, message, sig }
+                  this.newHeads.push({message, sig})
+                  added.push({message, sig})
+                  // Cancel a pending "heads" to be spread
+                  if (this.headsTimeout) {
+                    clearTimeout(this.headsTimeout)
+                  }
+                  // Reprogram it a few moments later
+                  this.headsTimeout = setTimeout(async () => {
+                    const heads = this.newHeads.splice(0, this.newHeads.length)
+                    if (heads.length) {
+                      await this.spreadNewHeads(heads)
+                    }
+                  }, WS2PConstants.HEADS_SPREAD_TIMEOUT)
+                }
+              }
+            }
+          }
+        } else {
+          throw "HEAD_MESSAGE_WRONGLY_SIGNED"
+        }
+      }
+      else if (message.match(WS2PConstants.HEAD_V1_REGEXP)) {
+        const [,,, pub, blockstamp, software, ws2pId, softVersion, prefix]:string[] = message.split(':')
+        const sigOK = verify(message, sig, pub)
+        const fullId = [pub, ws2pId].join('-')
+        if (sigOK) {
+          // Already known?
+          if (!this.headsCache[fullId] || this.headsCache[fullId].blockstamp !== blockstamp) {
+            // More recent?
+            if (!this.headsCache[fullId] || parseInt(this.headsCache[fullId].blockstamp) < parseInt(blockstamp)) {
+              // Check that issuer is a member and that the block exists
+              const memberKey = await this.isMemberKey(pub)
+              if (memberKey) {
+                const exists = await this.existsBlock(blockstamp)
+                if (exists) {
+                  this.headsCache[fullId] = { blockstamp, message, sig }
                   this.newHeads.push({message, sig})
                   added.push({message, sig})
                   // Cancel a pending "heads" to be spread
@@ -352,7 +394,11 @@ export class WS2PCluster {
   private sayHeadChangedTo(number:number, hash:string) {
     const key = new Key(this.server.conf.pair.pub, this.server.conf.pair.sec)
     const pub = key.publicKey
-    const message = `WS2P:HEAD:${pub}:${number}-${hash}`
+    const software = 'duniter'
+    const softVersion = Package.getInstance().version
+    const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '00000000'
+    const prefix = this.server.conf.prefix || Constants.DEFAULT_PEER_ID
+    const message = `WS2P:HEAD:1:${pub}:${number}-${hash}:${ws2pId}:${software}:${softVersion}:${prefix}`
     const sig = key.signSync(message)
     return { sig, message, pub }
   }
diff --git a/app/modules/ws2p/lib/constants.ts b/app/modules/ws2p/lib/constants.ts
index 8fd9b790b34ff9845dbccd5b7c198b4a057d6e99..f196b27d36e5dc33e40f7290c41653132fb6c8af 100644
--- a/app/modules/ws2p/lib/constants.ts
+++ b/app/modules/ws2p/lib/constants.ts
@@ -19,7 +19,19 @@ export const WS2PConstants = {
   BAN_DURATION_IN_SECONDS: 120,
   ERROR_RECALL_DURATION_IN_SECONDS: 60,
 
-  HEAD_REGEXP: new RegExp('^WS2P:HEAD:' + CommonConstants.FORMATS.PUBKEY + ':' + CommonConstants.FORMATS.BLOCKSTAMP + '$'),
+  HEAD_V0_REGEXP: new RegExp('^WS2P:HEAD:'
+    + CommonConstants.FORMATS.PUBKEY + ':'
+    + CommonConstants.FORMATS.BLOCKSTAMP
+    + '$'),
+
+  HEAD_V1_REGEXP: new RegExp('^WS2P:HEAD:1:'
+  + '(' + CommonConstants.FORMATS.PUBKEY + '):'
+  + '(' + CommonConstants.FORMATS.BLOCKSTAMP + '):'
+  + '(' + CommonConstants.FORMATS.WS2PID + '):'
+  + '(' + CommonConstants.FORMATS.SOFTWARE + '):'
+  + '(' + CommonConstants.FORMATS.SOFT_VERSION + '):'
+  + '(' + CommonConstants.FORMATS.POW_PREFIX + ')'
+  + '$'),
 
   HEADS_SPREAD_TIMEOUT: 100 // Wait 100ms before sending a bunch of signed heads
 }
\ No newline at end of file
diff --git a/test/fast/modules/ws2p/ws2p_regexp.ts b/test/fast/modules/ws2p/ws2p_regexp.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1dff5363df765c0139d0f36e1e46e55d0bb502c7
--- /dev/null
+++ b/test/fast/modules/ws2p/ws2p_regexp.ts
@@ -0,0 +1,47 @@
+import * as assert from 'assert'
+import { WS2PConstants } from '../../../../app/modules/ws2p/lib/constants';
+
+describe('WS2P Regexp', () => {
+  
+  it('should match correctly HEADv0 regexps', () => {
+    assert.deepEqual('WRONG_VALUE'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957#00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:00-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP) || { length: 0 }),
+      [
+        'WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E',
+        '0'
+      ]
+    )
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP) || { length: 0 }),
+      ['WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E', '63957']
+    )
+  })
+  
+  it('should match correctly HEADv1 regexps', () => {
+    assert.deepEqual('WRONG_VALUE'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957#00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:00-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:1:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E:abcdef01:duniter:1.6.8:899'.match(WS2PConstants.HEAD_V1_REGEXP) || { length: 0 }),
+      [
+        'WS2P:HEAD:1:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E:abcdef01:duniter:1.6.8:899',
+        '3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj',
+        '63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E',
+        '63957',
+        'abcdef01',
+        'duniter',
+        '1.6.8',
+        '899',
+        '899'
+      ]
+    )
+  })
+})