diff --git a/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java b/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java
index 2c033bea52db11eed0baa83bcc6fc490b8729e3f..e329116dd279e3f1cfd3a8d91c33b8f4cecdf487 100644
--- a/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java
+++ b/duniter4j-client/src/main/java/org/duniter/client/actions/NetworkAction.java
@@ -175,7 +175,7 @@ public class NetworkAction extends AbstractAction {
                     peer.getStats().getStatus().name(),
                     isUp ? formatApi(peer) : "",
                     isUp ? peer.getStats().getVersion() : "",
-                    (isUp && peer.getStats().getHardshipLevel() != null) ? peer.getStats().getHardshipLevel() : I18n.t("duniter4j.client.network.mirror"),
+                    (isUp && peer.getStats().getHardshipLevel() != null) ? peer.getStats().getHardshipLevel() : (peer.getStats().getUid() == null ? I18n.t("duniter4j.client.network.mirror") : ""),
                     isUp ? formatBuid(peer.getStats()) : ""
             };
         })
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkWs2pHeads.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkWs2pHeads.java
new file mode 100644
index 0000000000000000000000000000000000000000..948d313f7fc6d2dc316ae7e2e34a386c04638202
--- /dev/null
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/NetworkWs2pHeads.java
@@ -0,0 +1,97 @@
+package org.duniter.core.client.model.bma;
+
+/*
+ * #%L
+ * Duniter4j :: Core Client API
+ * %%
+ * Copyright (C) 2014 - 2017 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import java.io.Serializable;
+
+/**
+ * Created by blavenie on 22/01/19.
+ */
+public class NetworkWs2pHeads {
+
+    public NetworkWs2pHeads.Head[] heads;
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        for(NetworkWs2pHeads.Head head : heads) {
+            sb.append(head.toString()).append("\n");
+        }
+        return sb.toString();
+    }
+
+    public static class Head implements Serializable {
+        public Ws2pHead message;
+        public String sig;
+        public String messageV2;
+        public String sigV2;
+        public Integer step;
+
+        public Ws2pHead getMessage() {
+            return message;
+        }
+
+        public void setMessage(Ws2pHead message) {
+            this.message = message;
+        }
+
+        public String getSig() {
+            return sig;
+        }
+
+        public void setSig(String sig) {
+            this.sig = sig;
+        }
+
+        public String getMessageV2() {
+            return messageV2;
+        }
+
+        public void setMessageV2(String messageV2) {
+            this.messageV2 = messageV2;
+        }
+
+        public String getSigV2() {
+            return sigV2;
+        }
+
+        public void setSigV2(String sigV2) {
+            this.sigV2 = sigV2;
+        }
+
+        public Integer getStep() {
+            return step;
+        }
+
+        public void setStep(Integer step) {
+            this.step = step;
+        }
+
+        @Override
+        public String toString() {
+            String s = "message=" + message + "\n" +
+                    "sig=" + sig+ "\n" +
+                    "step=" + step;
+            return s;
+        }
+    }
+}
\ No newline at end of file
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHead.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHead.java
new file mode 100644
index 0000000000000000000000000000000000000000..0813f9327129efe1a71c1e6c221dc6aff7bc7225
--- /dev/null
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHead.java
@@ -0,0 +1,199 @@
+package org.duniter.core.client.model.bma;
+
+/*
+ * #%L
+ * Duniter4j :: Core Client API
+ * %%
+ * Copyright (C) 2014 - 2017 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.common.base.Joiner;
+
+import java.io.Serializable;
+
+/**
+ * Created by blavenie on 22/01/19.
+ */
+public class Ws2pHead implements Serializable {
+
+
+    public class AccessConfig {
+        public boolean useTor;
+        private String mode;
+
+        public boolean isUseTor() {
+            return useTor;
+        }
+
+        public void setUseTor(boolean useTor) {
+            this.useTor = useTor;
+        }
+
+        public String getMode() {
+            return mode;
+        }
+
+        public void setMode(String mode) {
+            this.mode = mode;
+        }
+    }
+
+    public Integer version;
+    public String pubkey;
+    public String block;
+    public String ws2pid;
+    public String software;
+    public String softwareVersion;
+    public String powPrefix;
+
+    public String signature;
+
+    public AccessConfig privateConfig = new AccessConfig();
+    public AccessConfig publicConfig = new AccessConfig();
+
+    public Integer getVersion() {
+        return version;
+    }
+
+    public void setVersion(Integer version) {
+        this.version = version;
+    }
+
+    public String getPubkey() {
+        return pubkey;
+    }
+
+    public void setPubkey(String pubkey) {
+        this.pubkey = pubkey;
+    }
+
+    public String getBlock() {
+        return block;
+    }
+
+    public void setBlock(String block) {
+        this.block = block;
+    }
+
+    public String getWs2pid() {
+        return ws2pid;
+    }
+
+    public void setWs2pid(String ws2pid) {
+        this.ws2pid = ws2pid;
+    }
+
+    public String getSoftware() {
+        return software;
+    }
+
+    public void setSoftware(String software) {
+        this.software = software;
+    }
+
+    public String getSoftwareVersion() {
+        return softwareVersion;
+    }
+
+    public void setSoftwareVersion(String softwareVersion) {
+        this.softwareVersion = softwareVersion;
+    }
+
+    public String getPowPrefix() {
+        return powPrefix;
+    }
+
+    public void setPowPrefix(String powPrefix) {
+        this.powPrefix = powPrefix;
+    }
+
+    public AccessConfig getPrivateConfig() {
+        return privateConfig;
+    }
+
+    public void setPrivateConfig(AccessConfig privateConfig) {
+        this.privateConfig = privateConfig;
+    }
+
+    public AccessConfig getPublicConfig() {
+        return publicConfig;
+    }
+
+    public void setPublicConfig(AccessConfig publicConfig) {
+        this.publicConfig = publicConfig;
+    }
+
+    public String getSignature() {
+        return signature;
+    }
+
+    public void setSignature(String signature) {
+        this.signature = signature;
+    }
+
+    @Override
+    public String toString() {
+        return Joiner.on(':').skipNulls().join(new Object[]{
+                getPrefix(), "HEAD", version, pubkey, block, ws2pid, software, softwareVersion, powPrefix
+        });
+    }
+
+    @JsonIgnore
+    protected String getPrefix() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("WS2P");
+
+        // Private access
+        if (getPrivateConfig() != null) {
+            sb.append("O");
+            if (getPrivateConfig().isUseTor()) {
+                sb.append("T");
+            } else {
+                sb.append("C");
+            }
+
+            if (getPrivateConfig().getMode() != null) {
+                switch (getPrivateConfig().getMode()) {
+                    case "all":
+                        sb.append("A");
+                        break;
+                    case "mixed":
+                        sb.append("M");
+                        break;
+                    case "strict":
+                        sb.append("S");
+                        break;
+                }
+            }
+        }
+
+        // Public access
+        if (getPublicConfig() != null) {
+            sb.append("I");
+
+            if (getPublicConfig().isUseTor()) {
+                sb.append("T");
+            }
+            else {
+                sb.append("C");
+            }
+        }
+        return sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHeads.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHeads.java
new file mode 100644
index 0000000000000000000000000000000000000000..ebb722a1b1b0aea064219573109de5dd642c723c
--- /dev/null
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/Ws2pHeads.java
@@ -0,0 +1,111 @@
+package org.duniter.core.client.model.bma;
+
+import org.duniter.core.client.model.bma.jackson.Ws2pHeadDeserializer;
+import org.duniter.core.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Ws2pHeads {
+
+    private static final Logger log = LoggerFactory.getLogger(Ws2pHeads.class);
+
+    public static final String WS2P_PREFIX = "^WS2P(?:O([CT][SAM]))?(?:I([CT]))?$";
+
+    public static final Pattern WS2P_PREFIX_PATTERN = Pattern.compile(WS2P_PREFIX);
+
+    private Ws2pHeads() {
+        // helper class
+    }
+
+    public static Ws2pHead parse(String message) throws IOException {
+
+        try {
+            String[] parts = message.split(":");
+            if (parts.length < 3 || !parts[0].startsWith("WS2P")) {
+                throw new IOException("Invalid WS2P message format: " + message);
+            }
+            // Head message
+            if ("HEAD".equals(parts[1])) {
+                if (parts.length < 4) {
+                    throw new IllegalArgumentException("Invalid WS2P message format: " + message);
+                }
+
+                // Duniter version < 1.6.9
+                if (parts.length == 4) {
+                    Ws2pHead result = new Ws2pHead();
+                    result.setPubkey(parts[2]);
+                    result.setBlock(parts[3]);
+                } else {
+                    int version = Integer.parseInt(parts[2]);
+                    if (version >= 1) {
+                        Ws2pHead result = new Ws2pHead();
+                        String prefix = parts[0];
+
+                        // Private/public options
+                        if (prefix.length() > 4) {
+
+                            Matcher matches = WS2P_PREFIX_PATTERN.matcher(prefix);
+                            if (!matches.matches()) {
+                                throw new IllegalArgumentException("Invalid WS2P message format: " + message);
+                            }
+
+                            // Private options
+                            String privateOptions = matches.group(1);
+                            if (StringUtils.isNotBlank(privateOptions)) {
+                                Ws2pHead.AccessConfig privateConfig = result.getPrivateConfig();
+                                privateConfig.setUseTor(privateOptions.startsWith("T"));
+                                String mode = privateOptions.substring(1);
+                                switch (mode) {
+                                    case "A":
+                                        privateConfig.setMode("all");
+                                        break;
+                                    case "M":
+                                        privateConfig.setMode("mixed");
+                                        break;
+                                    case "S":
+                                        privateConfig.setMode("strict");
+                                        break;
+                                }
+                            }
+
+                            // Public options
+                            String publicOptions = matches.group(2);
+                            if (StringUtils.isNotBlank(publicOptions)) {
+                                Ws2pHead.AccessConfig publicConfig = result.getPrivateConfig();
+                                publicConfig.setUseTor(publicOptions.startsWith("T"));
+                                publicConfig.setMode("all");
+                            }
+
+                            // For DEBUG only:
+                            log.debug(String.format("Parsing WS2P prefix {%s} into: private %s, public %s",
+                                     prefix,
+                                      ((result.getPrivateConfig().isUseTor() ? "TOR " : "" ) + (result.getPrivateConfig().getMode())),
+                                      ((result.getPublicConfig().isUseTor() ? "TOR " : "" ) + (result.getPublicConfig().getMode()))
+                            ));
+                        }
+
+                        result.setVersion(version);
+                        result.setPubkey(parts[3]);
+                        result.setBlock(parts[4]);
+                        result.setWs2pid(parts[5]);
+                        result.setSoftware(parts[6]);
+                        result.setSoftwareVersion(parts[7]);
+                        result.setPowPrefix(parts[8]);
+
+                        return result;
+                    }
+                }
+
+            }
+
+            return null;
+        }
+        catch(Exception e) {
+            throw new IOException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/JacksonUtils.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/JacksonUtils.java
index 0309e327bc5361c3c8ef578e20346f5541b0f6de..73714a8db4742abb754cc0f1a2e63c6dade9a5a2 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/JacksonUtils.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/JacksonUtils.java
@@ -28,6 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.module.SimpleModule;
 import org.duniter.core.client.model.bma.BlockchainBlock;
 import org.duniter.core.client.model.bma.NetworkPeering;
+import org.duniter.core.client.model.bma.NetworkWs2pHeads;
+import org.duniter.core.client.model.bma.Ws2pHead;
 
 /**
  * Created by blavenie on 07/12/16.
@@ -62,6 +64,8 @@ public abstract class JacksonUtils extends SimpleModule {
         // Network
         module.addDeserializer(NetworkPeering.Endpoint.class, new EndpointDeserializer());
         module.addSerializer(NetworkPeering.Endpoint.class, new EndpointSerializer());
+        module.addDeserializer(Ws2pHead.class, new Ws2pHeadDeserializer());
+        module.addSerializer(Ws2pHead.class, new Ws2pHeadSerializer());
 
         objectMapper.registerModule(module);
 
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadDeserializer.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..537797ce21bddd69befa1a1f7015438e80bca579
--- /dev/null
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadDeserializer.java
@@ -0,0 +1,68 @@
+package org.duniter.core.client.model.bma.jackson;
+
+/*
+ * #%L
+ * Duniter4j :: Core Client API
+ * %%
+ * Copyright (C) 2014 - 2017 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import org.duniter.core.client.model.bma.Endpoints;
+import org.duniter.core.client.model.bma.NetworkPeering;
+import org.duniter.core.client.model.bma.Ws2pHead;
+import org.duniter.core.client.model.bma.Ws2pHeads;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Created by blavenie on 07/12/16.
+ */
+public class Ws2pHeadDeserializer extends JsonDeserializer<Ws2pHead> {
+
+    private static final Logger log = LoggerFactory.getLogger(Ws2pHeadDeserializer.class);
+
+    private boolean debug;
+
+    public Ws2pHeadDeserializer() {
+        this.debug = log.isDebugEnabled();
+    }
+
+    @Override
+    public Ws2pHead deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
+
+        String ept = jp.getText();
+
+        try {
+            return Ws2pHeads.parse(ept);
+        } catch(IOException e) {
+            // Unable to parse endpoint: continue (will skip this endpoint)
+            if (debug) {
+                log.warn(e.getMessage(), e); // link the exception
+            }
+            else {
+                log.debug(e.getMessage());
+            }
+            return null;
+        }
+    }
+}
\ No newline at end of file
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadSerializer.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadSerializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..971c2b861a9fa978e24e662a69211b97c667476a
--- /dev/null
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/bma/jackson/Ws2pHeadSerializer.java
@@ -0,0 +1,64 @@
+package org.duniter.core.client.model.bma.jackson;
+
+/*
+ * #%L
+ * Duniter4j :: Core Client API
+ * %%
+ * Copyright (C) 2014 - 2017 EIS
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the 
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public 
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/gpl-3.0.html>.
+ * #L%
+ */
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import org.duniter.core.client.model.bma.NetworkPeering;
+import org.duniter.core.client.model.bma.NetworkWs2pHeads;
+import org.duniter.core.client.model.bma.Ws2pHead;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Created by blavenie on 17/10/18.
+ */
+public class Ws2pHeadSerializer extends JsonSerializer<Ws2pHead> {
+
+    private static final Logger log = LoggerFactory.getLogger(Ws2pHeadSerializer.class);
+
+    private boolean debug;
+
+    public Ws2pHeadSerializer() {
+        this.debug = log.isDebugEnabled();
+    }
+
+    @Override
+    public void serialize(Ws2pHead head, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
+
+        try {
+            jsonGenerator.writeString(head.toString());
+        } catch(IOException e) {
+            // Unable to parse endpoint: continue (will skip this endpoint)
+            if (debug) {
+                log.warn(e.getMessage(), e); // link the exception
+            }
+            else {
+                log.debug(e.getMessage());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peers.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peers.java
index f7e5e06f4c583563a996ba489ea89bc8db8f7427..3648143ab417e00d83a80bef6e420971ef703b7c 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peers.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/model/local/Peers.java
@@ -104,7 +104,20 @@ public final class Peers {
             result.setCurrency(firstEp.getCurrency());
             result.setPubkey(firstEp.getPubkey());
 
-            result.setBlock(getBlockStamp(firstEp));
+            if (firstEp.getPeering() != null) {
+                result.setBlock(getPeeringBlockStamp(firstEp));
+                result.setSignature(firstEp.getPeering().getSignature());
+                result.setVersion(firstEp.getPeering().getVersion());
+            }
+            else {
+                result.setVersion(Protocol.VERSION);
+                result.setBlock(getStatsBlockStamp(firstEp));
+                result.setSignature(null);
+            }
+
+            // Default values (not stored yet)
+            // TODO check if still used by clients
+            result.setStatusTS(0L);
 
             // Compute status (=UP is at least one endpoint is UP)
             String status = endpoints.stream()
@@ -114,10 +127,6 @@ public final class Peers {
                     .orElse(Peer.PeerStatus.DOWN).name();
             result.setStatus(status);
 
-            // Default values (not stored yet)
-            result.setVersion(Protocol.VERSION); // TODO: get it from the storage (DB, ES, etc.) ?
-            result.setStatusTS(0L); // TODO make sure this is used by clients ?
-
             // Compute endpoints list
             List<NetworkPeering.Endpoint> bmaEps = endpoints.stream()
                     .map(Peers::toBmaEndpoint)
@@ -152,7 +161,14 @@ public final class Peers {
     }
 
 
-    public static String getBlockStamp(final Peer peer) {
+    public static String getPeeringBlockStamp(final Peer peer) {
+        return peer.getPeering() != null &&
+                peer.getPeering().getBlockNumber() != null &&
+                peer.getPeering().getBlockHash() != null
+                ? (peer.getPeering().getBlockNumber() + "-" + peer.getPeering().getBlockHash()) : null;
+    }
+
+    public static String getStatsBlockStamp(final Peer peer) {
         return peer.getStats() != null &&
                 peer.getStats().getBlockNumber() != null &&
                 peer.getStats().getBlockHash() != null
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java
index 7b6c5e6c3861e2b698f599223692c9bce7844f6f..bf7956eb283affe2a68da3c3220d510978f617c7 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteService.java
@@ -23,9 +23,7 @@ package org.duniter.core.client.service.bma;
  */
 
 import org.duniter.core.beans.Service;
-import org.duniter.core.client.model.bma.EndpointApi;
-import org.duniter.core.client.model.bma.NetworkPeering;
-import org.duniter.core.client.model.bma.NetworkPeers;
+import org.duniter.core.client.model.bma.*;
 import org.duniter.core.client.model.local.Peer;
 import org.duniter.core.util.websocket.WebsocketClientEndpoint;
 
@@ -44,6 +42,8 @@ public interface NetworkRemoteService extends Service {
 
     NetworkPeers.Peer getPeerLeaf(Peer peer, String leaf);
 
+    List<Ws2pHead> getWs2pHeads(Peer peer);
+
     List<Peer> findPeers(Peer peer, String status, EndpointApi endpointApi, Integer currentBlockNumber, String currentBlockHash);
 
     WebsocketClientEndpoint addPeerListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect);
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java
index 12242217c95d0ea92560eebc7492343f90642085..a7c0e63eb3f24853a511e50919242f8beb4aac26 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/bma/NetworkRemoteServiceImpl.java
@@ -30,13 +30,12 @@ import java.util.*;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.Lists;
 import org.apache.http.NameValuePair;
 import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.message.BasicNameValuePair;
-import org.duniter.core.client.model.bma.EndpointApi;
-import org.duniter.core.client.model.bma.NetworkPeering;
-import org.duniter.core.client.model.bma.NetworkPeers;
+import org.duniter.core.client.model.bma.*;
 import org.duniter.core.client.model.bma.jackson.JacksonUtils;
 import org.duniter.core.client.model.local.Peer;
 import org.duniter.core.client.model.local.Wallet;
@@ -68,6 +67,10 @@ public class NetworkRemoteServiceImpl extends BaseRemoteServiceImpl implements N
 
     public static final String URL_WS_PEER = "/ws/peer";
 
+    public static final String URL_WS2P = URL_BASE + "/ws2p";
+
+    public static final String URL_WS2P_HEADS = URL_WS2P + "/heads";
+
     public NetworkRemoteServiceImpl() {
         super();
     }
@@ -154,6 +157,26 @@ public class NetworkRemoteServiceImpl extends BaseRemoteServiceImpl implements N
         return result;
     }
 
+    @Override
+    public List<Ws2pHead> getWs2pHeads(Peer peer) {
+        Preconditions.checkNotNull(peer);
+
+        NetworkWs2pHeads remoteResult = httpService.executeRequest(peer, URL_WS2P_HEADS, NetworkWs2pHeads.class);
+
+        List<Ws2pHead> result = Lists.newArrayList();
+
+        for (NetworkWs2pHeads.Head remoteWs2pHead: remoteResult.heads) {
+
+            Ws2pHead head = remoteWs2pHead.getMessage();
+            if (head != null) {
+                head.setSignature(remoteWs2pHead.getSig());
+
+                result.add(head);
+            }
+        }
+
+        return result;
+    }
 
     @Override
     public WebsocketClientEndpoint addPeerListener(String currencyId, WebsocketClientEndpoint.MessageListener listener, boolean autoReconnect) {
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkService.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkService.java
index bf8651dbde8836a3680bc06d33d67201814d6989..c470273e9f62383a9c3199e72dcb17cef1f56445 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkService.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkService.java
@@ -91,7 +91,7 @@ public interface NetworkService extends Service {
                                 final Filter filter, final Sort sort, final boolean autoreconnect,
                                 final ExecutorService executor);
 
-    CompletableFuture<List<Peer>> asyncRefreshPeers(final Peer mainPeer, final List<Peer> peers, final ExecutorService pool);
+    CompletableFuture<List<Peer>> refreshPeersAsync(final Peer mainPeer, final List<Peer> peers, final ExecutorService pool);
 
     String getVersion(final Peer peer);
 }
diff --git a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java
index f31cc200c5087a6df956ab414859c63e5f669bc4..d61105851966400ec56f3b8cd9c52c9db4701da7 100644
--- a/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java
+++ b/duniter4j-core-client/src/main/java/org/duniter/core/client/service/local/NetworkServiceImpl.java
@@ -155,44 +155,50 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
 
         log.debug("Loading network peers...");
         final ExecutorService pool = (executor != null) ? executor : ForkJoinPool.commonPool();
-        CompletableFuture<List<Peer>> peersFuture = CompletableFuture.supplyAsync(() -> loadPeerLeafs(mainPeer, filterEndpoints), pool);
-        CompletableFuture<Map<String, String>> memberUidsFuture = CompletableFuture.supplyAsync(() -> wotRemoteService.getMembersUids(mainPeer), pool);
 
-        return CompletableFuture.allOf(
-                new CompletableFuture[] {peersFuture, memberUidsFuture})
-                .thenComposeAsync(v -> {
-                    final Map<String, String> memberUids = memberUidsFuture.join();
-                    List<Peer> peers = peersFuture.join();
-
-                    List<CompletableFuture<Peer>> list = peers.stream()
-                            .map(peer -> {
-                                // For if same as main peer,
-                                if (mainPeer.getUrl().equals(peer.getUrl())) {
-                                    // Update properties
-                                    mainPeer.setPubkey(peer.getPubkey());
-                                    mainPeer.setHash(peer.getHash());
-                                    mainPeer.setCurrency(peer.getCurrency());
-                                    // reuse instance
-                                    peer = mainPeer;
-                                }
-
-                                // Exclude peer with only a local IPv4 address (or localhost)
-                                else if (InetAddressUtils.isLocalAddress(peer.getHost())) {
-                                    return CompletableFuture.<Peer>completedFuture(null);
-                                }
-
-                                return asyncRefreshPeer(peer, memberUids, pool);
-                            })
-                            .collect(Collectors.toList());
-                    return CompletableFutures.allOfToList(list);
-                });
+        return CompletableFuture.supplyAsync(() -> loadPeerLeafs(mainPeer, filterEndpoints), pool)
+                .thenApply(peers ->
+                    peers.stream()
+                    .map(peer -> {
+                        // For if same as main peer,
+                        if (mainPeer.getUrl().equals(peer.getUrl())) {
+                            // Update properties
+                            mainPeer.setPubkey(peer.getPubkey());
+                            mainPeer.setHash(peer.getHash());
+                            mainPeer.setCurrency(peer.getCurrency());
+                            // reuse instance
+                            peer = mainPeer;
+                        }
+
+                        // Exclude peer with only a local IPv4 address (or localhost)
+                        else if (InetAddressUtils.isLocalAddress(peer.getHost())) {
+                            return null;
+                        }
+
+                        return peer;
+                    })
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList())
+        )
+         .thenCompose(peers -> this.refreshPeersAsync(mainPeer, peers, pool));
     }
 
 
-    public CompletableFuture<Peer> asyncRefreshPeer(final Peer peer, final Map<String, String> memberUids, final ExecutorService pool) {
+    public CompletableFuture<Peer> refreshPeerAsync(final Peer peer,
+                                                    final Map<String, String> memberUids,
+                                                    final List<Ws2pHead> ws2pHeads,
+                                                    final ExecutorService pool) {
         if (log.isDebugEnabled()) log.debug(String.format("[%s] Refreshing peer status", peer.toString()));
 
-        return CompletableFuture.supplyAsync(() -> fillNodeSummary(peer), pool)
+        // WS2P: refresh using heads
+        if (Peers.hasWs2pEndpoint(peer)) {
+            return CompletableFuture.supplyAsync(() -> fillWs2pPeer(peer, memberUids, ws2pHeads), pool);
+        }
+
+        // BMA or ES_CORE
+        if (Peers.hasBmaEndpoint(peer) || Peers.hasEsCoreEndpoint(peer)) {
+
+            return CompletableFuture.supplyAsync(() -> fillNodeSummary(peer), pool)
                 .thenApplyAsync(this::fillCurrentBlock)
                 .exceptionally(throwable -> {
                     peer.getStats().setStatus(Peer.PeerStatus.DOWN);
@@ -225,18 +231,72 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
                     peer.getStats().setHardshipLevel(0);
                     return peer;
                 });
+        }
+
+        // Unknown API: just return the peer
+        return CompletableFuture.completedFuture(peer);
     }
 
+    public Peer fillWs2pPeer(final Peer peer, final Map<String, String> memberUids, List<Ws2pHead> ws2pHeads) {
+        if (log.isDebugEnabled()) log.debug(String.format("[%s] Refreshing WS2P peer status", peer.toString()));
 
-    public CompletableFuture<List<Peer>> asyncRefreshPeers(final Peer mainPeer, final List<Peer> peers, final ExecutorService pool) {
-        return CompletableFuture.supplyAsync(() -> wotRemoteService.getMembersUids(mainPeer), pool)
-                // Refresh all endpoints
-                .thenApply(memberUids ->
-                        peers.stream().map(peer ->
-                                asyncRefreshPeer(peer, memberUids, pool))
-                                .collect(Collectors.toList())
+        if (StringUtils.isBlank(peer.getPubkey()) || StringUtils.isBlank(peer.getEpId())) return peer;
+
+        Ws2pHead ws2pHead = ws2pHeads.stream().filter(head ->
+                peer.getPubkey().equals(head.getPubkey())
+                && peer.getEpId().equals(head.getWs2pid()
                 )
+        ).findFirst().orElse(null);
+
+        if (ws2pHead != null) {
+            if (ws2pHead.getBlock() != null) {
+                String[] blockParts = ws2pHead.getBlock().split("-");
+                if (blockParts.length == 2) {
+                    peer.getStats().setBlockNumber(Integer.parseInt(blockParts[0]));
+                    peer.getStats().setBlockHash(blockParts[1]);
+                }
+            }
+            peer.getStats().setSoftware(ws2pHead.getSoftware());
+            peer.getStats().setVersion(ws2pHead.getSoftwareVersion());
+        }
+        else {
+            peer.getStats().setStatus(Peer.PeerStatus.DOWN);
+        }
+
+        // Set uid
+        String uid = memberUids.get(peer.getPubkey());
+        peer.getStats().setUid(uid);
+
+        if (uid != null) {
+            // Could not known hardship, so fill 0 if member (=can compute)
+            peer.getStats().setHardshipLevel(0);
+        }
+        else {
+            peer.getStats().setHardshipLevel(null);
+        }
+
+        return peer;
+    }
+
+    public CompletableFuture<List<Peer>> refreshPeersAsync(final Peer mainPeer,final  List<Peer> peers, final ExecutorService pool) {
+
+        if (CollectionUtils.isEmpty(peers)) return CompletableFuture.completedFuture(null);
+
+        CompletableFuture<Map<String, String>> memberUidsFuture = CompletableFuture.supplyAsync(() -> wotRemoteService.getMembersUids(mainPeer), pool);
+        CompletableFuture<List<Ws2pHead>> ws2pHeadsFuture = CompletableFuture.supplyAsync(() -> networkRemoteService.getWs2pHeads(mainPeer), pool);
+
+        return CompletableFuture.allOf(memberUidsFuture, ws2pHeadsFuture)
+
+                // Refresh all endpoints
+                .thenApply(v -> {
+                    final Map<String, String> memberUids = memberUidsFuture.join();
+                    final List<Ws2pHead> ws2pHeads = ws2pHeadsFuture.join();
+                    return peers.stream().map(peer ->
+                            refreshPeerAsync(peer, memberUids, ws2pHeads, pool))
+                            .collect(Collectors.toList());
+                })
                 .thenCompose(CompletableFutures::allOfToList);
+
     }
 
     public List<Peer> fillPeerStatsConsensus(final List<Peer> peers) {
@@ -245,7 +305,7 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
         final Map<String,Long> peerCountByBuid = peers.stream()
                 .filter(peer -> Peers.isReacheable(peer) && Peers.hasDuniterEndpoint(peer))
                 .map(Peers::buid)
-                .filter(b -> b != null)
+                .filter(Objects::nonNull)
                 .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
 
         // Compute main consensus buid
@@ -253,7 +313,7 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
                 .sorted(Comparator.comparing(Map.Entry::getValue, Comparator.reverseOrder()))
                 .findFirst();
 
-        final String mainBuid = maxPeerCountEntry.isPresent() ? maxPeerCountEntry.get().getKey() : null;;
+        final String mainBuid = maxPeerCountEntry.isPresent() ? maxPeerCountEntry.get().getKey() : null;
 
         // Compute total of UP peers
         final Long peersUpTotal = peerCountByBuid.values().stream().mapToLong(Long::longValue).sum();
@@ -372,17 +432,7 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
                     final List<Peer> newPeers = new ArrayList<>();
                     addEndpointsAsPeers(bmaPeer, newPeers, null, filter.filterEndpoints);
 
-                    CompletableFuture<List<CompletableFuture<Peer>>> jobs =
-                            CompletableFuture.supplyAsync(() -> wotRemoteService.getMembersUids(mainPeer), pool)
-
-                                    // Refresh all endpoints
-                                    .thenApply(memberUids ->
-                                            newPeers.stream().map(peer ->
-                                                    asyncRefreshPeer(peer, memberUids, pool))
-                                                    .collect(Collectors.toList())
-                                    );
-
-                    jobs.thenCompose(CompletableFutures::allOfToList)
+                    refreshPeersAsync(mainPeer, newPeers, executor)
                         .thenAccept(refreshedPeers -> {
                             if (CollectionUtils.isEmpty(refreshedPeers)) return;
 
@@ -749,4 +799,6 @@ public class NetworkServiceImpl extends BaseRemoteServiceImpl implements Network
             });
         }
     }
+
+
 }
diff --git a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/NetworkRemoteServiceTest.java b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/NetworkRemoteServiceTest.java
index 085f9550187e6f6ccfc40a3b9cde7b4a8a1dcd2a..de4c8891bff095c8896c695cf92965f4803a7f0d 100644
--- a/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/NetworkRemoteServiceTest.java
+++ b/duniter4j-core-client/src/test/java/org/duniter/core/client/service/bma/NetworkRemoteServiceTest.java
@@ -27,6 +27,7 @@ import org.duniter.core.client.TestResource;
 import org.duniter.core.client.config.Configuration;
 import org.duniter.core.client.model.bma.EndpointApi;
 import org.duniter.core.client.model.bma.NetworkPeering;
+import org.duniter.core.client.model.bma.Ws2pHead;
 import org.duniter.core.client.model.local.Peer;
 import org.duniter.core.client.service.ServiceLocator;
 import org.junit.Assert;
@@ -84,6 +85,20 @@ public class NetworkRemoteServiceTest {
 		Assert.assertTrue(result.size() > 0);
 	}
 
+	@Test
+	public void getWs2pHeads() throws Exception {
+
+		List<Ws2pHead> result = service.getWs2pHeads(peer);
+
+		Assert.assertNotNull(result);
+		Assert.assertTrue(result.size() > 0);
+
+		// log
+		//result.stream().forEach(head ->
+		//		System.out.println(head.toString())
+		//);
+	}
+
 	/* -- internal methods */
 
     protected Peer createTestPeer() {