diff --git a/network/Cargo.toml b/network/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..15c8ac50e1bf844ad486dceb5bd93c463007c041
--- /dev/null
+++ b/network/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "duniter-network"
+version = "0.1.0"
+authors = ["librelois <elois@duniter.org>"]
+description = "network model for the Duniter project."
+license = "AGPL-3.0"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+duniter-crypto = { path = "../crypto" }
+duniter-documents = { path = "../documents" }
+duniter-module = { path = "../module" }
+lazy_static = "1.0.0"
+regex = "0.2.6"
+rust-crypto = "0.2.36"
+serde = "1.0.24"
+serde_derive = "1.0.24"
+serde_json = "1.0.9"
\ No newline at end of file
diff --git a/network/lib.rs b/network/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e20366615812c1322992196a843c3e1ac6461d62
--- /dev/null
+++ b/network/lib.rs
@@ -0,0 +1,338 @@
+//  Copyright (C) 2018  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Defined all aspects of the inter-node network that concern all modules and are therefore independent of one implementation or another of this network layer.
+
+#![cfg_attr(feature = "strict", deny(warnings))]
+#![deny(
+    missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
+    trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
+    unused_qualifications
+)]
+
+#[macro_use]
+extern crate lazy_static;
+
+extern crate crypto;
+extern crate duniter_crypto;
+extern crate duniter_documents;
+extern crate duniter_module;
+extern crate serde;
+extern crate serde_json;
+
+pub mod network_endpoint;
+pub mod network_head;
+pub mod network_peer;
+
+use self::network_head::NetworkHead;
+use self::network_peer::NetworkPeer;
+use crypto::digest::Digest;
+use crypto::sha2::Sha256;
+use duniter_crypto::keys::{ed25519, PublicKey};
+use duniter_documents::blockchain::v10::documents::{
+    BlockDocument, CertificationDocument, IdentityDocument, MembershipDocument, RevocationDocument,
+    TransactionDocument,
+};
+use duniter_documents::blockchain::Document;
+use duniter_documents::{BlockHash, BlockId, Blockstamp, Hash};
+use duniter_module::{ModuleReqFullId, ModuleReqId};
+use std::fmt::{Debug, Display, Error, Formatter};
+use std::ops::Deref;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+/// Random identifier with which several Duniter nodes with the same network keypair can be differentiated
+pub struct NodeUUID(pub u32);
+
+impl Default for NodeUUID {
+    fn default() -> NodeUUID {
+        NodeUUID(0)
+    }
+}
+
+impl Display for NodeUUID {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+        write!(f, "{:x}", self.0)
+    }
+}
+
+impl<'a> From<&'a str> for NodeUUID {
+    fn from(source: &'a str) -> NodeUUID {
+        NodeUUID(u32::from_str_radix(source, 16).expect("Fail to parse NodeUUID"))
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+/// Complete identifier of a duniter node.
+pub struct NodeFullId(pub NodeUUID, pub ed25519::PublicKey);
+
+impl Default for NodeFullId {
+    fn default() -> NodeFullId {
+        NodeFullId(
+            NodeUUID::default(),
+            PublicKey::from_base58("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA").unwrap(),
+        )
+    }
+}
+
+impl Display for NodeFullId {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
+        write!(f, "{}-{}", self.0, self.1)
+    }
+}
+
+impl NodeFullId {
+    /// Compute sha256 hash
+    pub fn sha256(&self) -> Hash {
+        let mut sha256 = Sha256::new();
+        sha256.input_str(&format!("{}", self));
+        Hash::from_hex(&sha256.result_str()).unwrap()
+    }
+}
+
+/// Trait to be implemented by the configuration object of the module managing the inter-node network.
+pub trait NetworkConf: Debug + Copy + Clone + PartialEq {}
+
+#[derive(Debug, Clone)]
+/// Block v10 in network format (Some events require a blockchain access to reconstitute the corresponding document)
+pub struct NetworkBlockV10 {
+    /// Uncompleted block document
+    pub uncompleted_block_doc: BlockDocument,
+    /// Joiners
+    pub joiners: Vec<serde_json::Value>,
+    /// actives
+    pub actives: Vec<serde_json::Value>,
+    /// leavers
+    pub leavers: Vec<serde_json::Value>,
+    /// revoked
+    pub revoked: Vec<serde_json::Value>,
+    /// certifications
+    pub certifications: Vec<serde_json::Value>,
+}
+
+#[derive(Debug, Clone)]
+/// Block in network format (Some events require a blockchain access to reconstitute the corresponding document)
+pub enum NetworkBlock {
+    /// Block V10
+    V10(Box<NetworkBlockV10>),
+    /// Block V11
+    V11(),
+}
+
+impl NetworkBlock {
+    /// Return blockstamp
+    pub fn blockstamp(&self) -> Blockstamp {
+        match self {
+            &NetworkBlock::V10(ref network_block_v10) => {
+                network_block_v10.deref().uncompleted_block_doc.blockstamp()
+            }
+            _ => panic!("Block version not supported !"),
+        }
+    }
+    /// Return previous blockstamp
+    pub fn previous_blockstamp(&self) -> Blockstamp {
+        match self {
+            &NetworkBlock::V10(ref network_block_v10) => Blockstamp {
+                id: BlockId(network_block_v10.deref().uncompleted_block_doc.number.0 - 1),
+                hash: BlockHash(
+                    network_block_v10
+                        .deref()
+                        .uncompleted_block_doc
+                        .previous_hash,
+                ),
+            },
+            _ => panic!("Block version not supported !"),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Network Document
+pub enum NetworkDocument {
+    /// Network Block
+    Block(NetworkBlock),
+    /// Identity Document
+    Identity(IdentityDocument),
+    /// Membership Document
+    Membership(MembershipDocument),
+    /// Certification Document
+    Certification(CertificationDocument),
+    /// Revocation Document
+    Revocation(RevocationDocument),
+    /// Transaction Document
+    Transaction(TransactionDocument),
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+/// Type returned when the network module fails to determine the current network consensus
+pub enum NetworkConsensusError {
+    /// The network module does not have enough data to determine consensus
+    InsufficientData(usize),
+    /// The network module does not determine consensus, there is most likely a fork
+    Fork(),
+}
+
+#[derive(Debug, Copy, Clone)]
+/// Type containing a request addressed to the network module
+pub enum NetworkRequest {
+    /// Get a current block of a specific node
+    GetCurrent(ModuleReqFullId, NodeFullId),
+    //GetBlock(ModuleReqFullId, NodeFullId, u64),
+    /// Get a blocks chunk from specified node
+    GetBlocks(ModuleReqFullId, NodeFullId, u32, u32),
+    /// Get pending wot documents from specified node
+    GetRequirementsPending(ModuleReqFullId, NodeFullId, u32),
+    /// Obtain the current network consensus
+    GetConsensus(ModuleReqFullId),
+    /// Getting the heads cache
+    GetHeadsCache(ModuleReqFullId),
+    /// Get a list of known endpoints
+    GetEndpoints(ModuleReqFullId),
+}
+
+impl NetworkRequest {
+    /// Get request full identitifier
+    pub fn get_req_full_id(&self) -> ModuleReqFullId {
+        match *self {
+            NetworkRequest::GetCurrent(ref req_id, _)
+            | NetworkRequest::GetBlocks(ref req_id, _, _, _)
+            | NetworkRequest::GetRequirementsPending(ref req_id, _, _)
+            | NetworkRequest::GetConsensus(ref req_id)
+            | NetworkRequest::GetHeadsCache(ref req_id)
+            | NetworkRequest::GetEndpoints(ref req_id) => req_id.clone(),
+        }
+    }
+    /// Get request identitifier
+    pub fn get_req_id(&self) -> ModuleReqId {
+        self.get_req_full_id().1
+    }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+/// Type returned when the network module does not get a satisfying answer to a request
+pub enum NetworkRequestError {
+    /// Receiving an invalid format response
+    WrongFormat(),
+    /// Unknow error
+    UnknowError(),
+    /// No response received
+    NoResponse(),
+    /// Unable to reach the target node
+    ReceiverUnreachable(),
+}
+
+#[derive(Debug, Clone)]
+/// Type containing the response to a network request
+pub enum NetworkResponse {
+    /// CurrentBlock
+    CurrentBlock(ModuleReqFullId, NodeFullId, Box<NetworkBlock>),
+    /// Block
+    Block(ModuleReqFullId, NodeFullId, Box<NetworkBlock>),
+    /// Chunk
+    Chunk(ModuleReqFullId, NodeFullId, Vec<Box<NetworkBlock>>),
+    /// PendingDocuments
+    PendingDocuments(ModuleReqFullId, Vec<NetworkDocument>),
+    /// Consensus
+    Consensus(ModuleReqFullId, Result<Blockstamp, NetworkConsensusError>),
+    /// HeadsCache
+    HeadsCache(ModuleReqFullId, Box<NetworkHead>),
+}
+
+impl NetworkResponse {
+    /// Get request full identifier
+    pub fn get_req_full_id(&self) -> ModuleReqFullId {
+        match *self {
+            NetworkResponse::CurrentBlock(ref req_id, _, _)
+            | NetworkResponse::Block(ref req_id, _, _)
+            | NetworkResponse::Chunk(ref req_id, _, _)
+            | NetworkResponse::PendingDocuments(ref req_id, _)
+            | NetworkResponse::Consensus(ref req_id, _)
+            | NetworkResponse::HeadsCache(ref req_id, _) => req_id.clone(),
+        }
+    }
+    /// Get request identifier
+    pub fn get_req_id(&self) -> ModuleReqId {
+        self.get_req_full_id().1
+    }
+}
+
+#[derive(Debug, Clone)]
+/// Type containing a network event, each time a network event occurs it's relayed to all modules
+pub enum NetworkEvent {
+    /// Receiving a response to a network request
+    ReqResponse(Box<NetworkResponse>),
+    /// A connection has changed state(`u32` is the new state, `Option<String>` est l'uid du noeud)
+    ConnectionStateChange(NodeFullId, u32, Option<String>),
+    /// Receiving Pending Documents
+    ReceiveDocuments(Vec<NetworkDocument>),
+    /// Receipt of peer cards
+    ReceivePeers(Vec<NetworkPeer>),
+    /// Receiving heads
+    ReceiveHeads(Vec<NetworkHead>),
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::network_endpoint::*;
+    use super::*;
+
+    #[test]
+    fn parse_endpoint() {
+        let issuer =
+            PublicKey::from_base58("D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx").unwrap();
+        let node_id = NodeUUID(u32::from_str_radix("c1c39a0a", 16).unwrap());
+        let full_id = NodeFullId(node_id, issuer);
+        assert_eq!(
+            NetworkEndpoint::parse_from_raw("WS2P c1c39a0a i3.ifee.fr 80 /ws2p", issuer, 0, 0),
+            Some(NetworkEndpoint::V1(NetworkEndpointV1 {
+                version: 1,
+                issuer,
+                api: NetworkEndpointApi(String::from("WS2P")),
+                node_id: Some(node_id),
+                hash_full_id: Some(full_id.sha256()),
+                host: String::from("i3.ifee.fr"),
+                port: 80,
+                path: Some(String::from("ws2p")),
+                raw_endpoint: String::from("WS2P c1c39a0a i3.ifee.fr 80 /ws2p"),
+                last_check: 0,
+                status: 0,
+            },))
+        );
+    }
+
+    #[test]
+    fn parse_endpoint2() {
+        let issuer =
+            PublicKey::from_base58("5gJYnQp8v7bWwk7EWRoL8vCLof1r3y9c6VDdnGSM1GLv").unwrap();
+        let node_id = NodeUUID(u32::from_str_radix("cb06a19b", 16).unwrap());
+        let full_id = NodeFullId(node_id, issuer);
+        assert_eq!(
+            NetworkEndpoint::parse_from_raw("WS2P cb06a19b g1.imirhil.fr 53012 /", issuer, 0, 0),
+            Some(NetworkEndpoint::V1(NetworkEndpointV1 {
+                version: 1,
+                issuer,
+                api: NetworkEndpointApi(String::from("WS2P")),
+                node_id: Some(node_id),
+                hash_full_id: Some(full_id.sha256()),
+                host: String::from("g1.imirhil.fr"),
+                port: 53012,
+                path: None,
+                raw_endpoint: String::from("WS2P cb06a19b g1.imirhil.fr 53012 /"),
+                last_check: 0,
+                status: 0,
+            },))
+        );
+    }
+}
diff --git a/network/network_endpoint.rs b/network/network_endpoint.rs
new file mode 100644
index 0000000000000000000000000000000000000000..554148207e109966750ca6ba2fe4bff53593d209
--- /dev/null
+++ b/network/network_endpoint.rs
@@ -0,0 +1,213 @@
+//  Copyright (C) 2017  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Module defining the format of network endpoints and how to handle them.
+
+extern crate crypto;
+extern crate duniter_crypto;
+extern crate duniter_documents;
+extern crate duniter_module;
+extern crate regex;
+extern crate serde;
+extern crate serde_json;
+
+use self::regex::Regex;
+use super::{NodeFullId, NodeUUID};
+use duniter_crypto::keys::ed25519;
+use duniter_documents::Hash;
+
+lazy_static! {
+    #[derive(Debug)]
+    /// Regex match all endpoint in V1 format (works for all api)
+    pub static ref ENDPOINT_V1_REGEX: Regex = Regex::new(
+        r"^(?P<api>[A-Z0-9_]+) (?P<version>[1-9][0-9]*)? ?(?P<uuid>[a-f0-9]{6,8})? ?(?P<host>[a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) (?P<port>[0-9]+)(?: /?(?P<path>.+)?)? *$"
+    ).unwrap();
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Identifies the API of an endpoint
+pub struct NetworkEndpointApi(pub String);
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Endpoint v1
+pub struct NetworkEndpointV1 {
+    /// API version
+    pub version: usize,
+    /// API Name
+    pub api: NetworkEndpointApi,
+    /// Node unique identifier
+    pub node_id: Option<NodeUUID>,
+    /// Public key of the node declaring this endpoint
+    pub issuer: ed25519::PublicKey,
+    /// NodeFullID hash
+    pub hash_full_id: Option<Hash>,
+    /// hostname
+    pub host: String,
+    /// port number
+    pub port: usize,
+    /// Optional path
+    pub path: Option<String>,
+    /// Endpoint in raw format (as it appears on the peer card)
+    pub raw_endpoint: String,
+    /// Accessibility status of this endpoint  (updated regularly)
+    pub status: u32,
+    /// Timestamp of the last connection attempt to this endpoint
+    pub last_check: u64,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Endpoint
+pub enum NetworkEndpoint {
+    /// Endpoint v1
+    V1(NetworkEndpointV1),
+    /// Endpoint v2
+    V2(),
+}
+
+impl ToString for NetworkEndpoint {
+    fn to_string(&self) -> String {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.raw_endpoint.clone(),
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+}
+
+impl NetworkEndpoint {
+    /// Accessors providing API name
+    pub fn api(&self) -> NetworkEndpointApi {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.api.clone(),
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Accessors providing node unique identifier
+    pub fn node_uuid(&self) -> Option<NodeUUID> {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.node_id,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Accessors providing node public key
+    pub fn pubkey(&self) -> ed25519::PublicKey {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.issuer,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Accessors providing node full identifier
+    pub fn node_full_id(&self) -> Option<NodeFullId> {
+        match self.node_uuid() {
+            Some(node_id) => Some(NodeFullId(node_id, self.pubkey())),
+            None => None,
+        }
+    }
+    /// Accessors providing port number
+    pub fn port(&self) -> usize {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.port,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Accessors providing raw format
+    pub fn raw(&self) -> String {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.raw_endpoint.clone(),
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Accessors providing endpoint accessibility status
+    pub fn status(&self) -> u32 {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => ep.status,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Set status
+    pub fn set_status(&mut self, new_status: u32) {
+        match *self {
+            NetworkEndpoint::V1(ref mut ep) => ep.status = new_status,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Set last_check
+    pub fn set_last_check(&mut self, new_last_check: u64) {
+        match *self {
+            NetworkEndpoint::V1(ref mut ep) => ep.last_check = new_last_check,
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Generate endpoint url
+    pub fn get_url(&self) -> String {
+        match *self {
+            NetworkEndpoint::V1(ref ep) => {
+                let protocol = match &ep.api.0[..] {
+                    "WS2P" | "WS2PTOR" => "ws",
+                    _ => "http",
+                };
+                let tls = match ep.port {
+                    443 => "s",
+                    _ => "",
+                };
+                let path = match ep.path {
+                    Some(ref path_string) => path_string.clone(),
+                    None => String::new(),
+                };
+                format!("{}{}://{}:{}/{}", protocol, tls, ep.host, ep.port, path)
+            }
+            _ => panic!("Endpoint version is not supported !"),
+        }
+    }
+    /// Parse Endpoint from rax format
+    pub fn parse_from_raw(
+        raw_endpoint: &str,
+        issuer: ed25519::PublicKey,
+        status: u32,
+        last_check: u64,
+    ) -> Option<NetworkEndpoint> {
+        match ENDPOINT_V1_REGEX.captures(raw_endpoint) {
+            Some(caps) => {
+                let node_id = match caps.name("uuid") {
+                    Some(caps_node_id) => match u32::from_str_radix(caps_node_id.as_str(), 16) {
+                        Ok(node_id) => Some(NodeUUID(node_id)),
+                        Err(_) => None,
+                    },
+                    None => None,
+                };
+                let hash_full_id = match node_id {
+                    Some(node_id_) => Some(NodeFullId(node_id_, issuer).sha256()),
+                    None => None,
+                };
+                Some(NetworkEndpoint::V1(NetworkEndpointV1 {
+                    version: 1,
+                    issuer,
+                    api: NetworkEndpointApi(String::from(&caps["api"])),
+                    node_id,
+                    hash_full_id,
+                    host: String::from(&caps["host"]),
+                    port: caps["port"].parse().unwrap_or(80),
+                    path: match caps.name("path") {
+                        Some(m) => Some(m.as_str().to_string()),
+                        None => None,
+                    },
+                    raw_endpoint: String::from(raw_endpoint),
+                    status,
+                    last_check,
+                }))
+            }
+            None => None,
+        }
+    }
+}
diff --git a/network/network_head.rs b/network/network_head.rs
new file mode 100644
index 0000000000000000000000000000000000000000..8559dbf3734e29f405cbb19645c422b244d62aa3
--- /dev/null
+++ b/network/network_head.rs
@@ -0,0 +1,429 @@
+//  Copyright (C) 2017  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Module defining the format of network heads and how to handle them.
+
+extern crate duniter_crypto;
+extern crate duniter_documents;
+extern crate serde_json;
+
+use super::{NodeFullId, NodeUUID};
+use duniter_crypto::keys::{ed25519, PublicKey, Signature};
+use duniter_documents::Blockstamp;
+use std::cmp::Ordering;
+use std::collections::HashMap;
+use std::ops::Deref;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Head Message V2
+pub struct NetworkHeadMessageV2 {
+    /// API details
+    pub api: String,
+    /// Head version
+    pub version: usize,
+    /// Head pubkey
+    pub pubkey: ed25519::PublicKey,
+    /// Head blockstamp
+    pub blockstamp: Blockstamp,
+    /// Head node id
+    pub node_uuid: NodeUUID,
+    /// Issuer node software
+    pub software: String,
+    /// Issuer node soft version
+    pub soft_version: String,
+    /// Issuer node prefix
+    pub prefix: usize,
+    /// Issuer node free member room
+    pub free_member_room: Option<usize>,
+    /// Issuer node free mirror room
+    pub free_mirror_room: Option<usize>,
+}
+
+impl PartialOrd for NetworkHeadMessageV2 {
+    fn partial_cmp(&self, other: &NetworkHeadMessageV2) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for NetworkHeadMessageV2 {
+    fn cmp(&self, other: &NetworkHeadMessageV2) -> Ordering {
+        self.blockstamp.cmp(&other.blockstamp)
+    }
+}
+
+impl NetworkHeadMessageV2 {
+    /// To human readable string
+    pub fn to_human_string(&self, max_len: usize, uid: Option<String>) -> String {
+        let short_api = &self.api[4..];
+
+        if max_len > 80 && uid.is_some() {
+            format!(
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} {pre:3} [{api:5}]  {mer:02}:{mir:02} {uid}",
+                node_id = self.node_uuid.to_string(),
+                pubkey = self.pubkey.to_string(),
+                blockstamp = self.blockstamp.to_string(),
+                soft = self.software,
+                ver = self.soft_version,
+                pre = self.prefix,
+                api = short_api,
+                mer = self.free_member_room.unwrap_or(0),
+                mir = self.free_mirror_room.unwrap_or(0),
+                uid = uid.unwrap(),
+            )
+        } else if max_len > 67 {
+            format!(
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} {pre:3} [{api:5}]  {mer:02}:{mir:02}",
+                node_id = self.node_uuid.to_string(),
+                pubkey = self.pubkey.to_string(),
+                blockstamp = self.blockstamp.to_string(),
+                soft = self.software,
+                ver = self.soft_version,
+                pre = self.prefix,
+                api = short_api,
+                mer = self.free_member_room.unwrap_or(0),
+                mir = self.free_mirror_room.unwrap_or(0),
+            )
+        } else if max_len > 63 {
+            format!(
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:7} [{api:5}]  {mer:02}:{mir:02}",
+                node_id = self.node_uuid.to_string(),
+                pubkey = self.pubkey.to_string(),
+                blockstamp = self.blockstamp.to_string(),
+                soft = self.software,
+                ver = self.soft_version,
+                api = short_api,
+                mer = self.free_member_room.unwrap_or(0),
+                mir = self.free_mirror_room.unwrap_or(0),
+            )
+        } else if max_len > 47 {
+            format!(
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} [{api:5}]  {mer:02}:{mir:02}",
+                node_id = self.node_uuid.to_string(),
+                pubkey = self.pubkey.to_string(),
+                blockstamp = self.blockstamp.to_string(),
+                api = short_api,
+                mer = self.free_member_room.unwrap_or(0),
+                mir = self.free_mirror_room.unwrap_or(0),
+            )
+        } else if max_len > 41 {
+            format!(
+                "{node_id:8}-{pubkey:.8} {blockstamp:.16} [{api:5}]",
+                node_id = self.node_uuid.to_string(),
+                pubkey = self.pubkey.to_string(),
+                blockstamp = self.blockstamp.to_string(),
+                api = short_api,
+            )
+        } else {
+            format!("Term width insufficient")
+        }
+    }
+}
+
+#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)]
+/// Head Message
+pub enum NetworkHeadMessage {
+    /// Head Message V2
+    V2(NetworkHeadMessageV2),
+    /// Head Message V3
+    V3(),
+}
+
+impl NetworkHeadMessage {
+    /// To human readable string
+    pub fn to_human_string(&self, max_len: usize, uid: Option<String>) -> String {
+        match *self {
+            NetworkHeadMessage::V2(ref mess_v2) => mess_v2.deref().to_human_string(max_len, uid),
+            _ => panic!("NetworkHead version not supported !"),
+        }
+    }
+    /// Parse head from string
+    fn from_str(source: &str) -> Option<NetworkHeadMessage> {
+        let source_array: Vec<&str> = source.split(':').collect();
+        if let Ok(pubkey) = PublicKey::from_base58(&source_array[3].to_string()) {
+            Some(NetworkHeadMessage::V2(NetworkHeadMessageV2 {
+                api: source_array[0].to_string(),
+                version: source_array[2].parse().unwrap(),
+                pubkey,
+                blockstamp: Blockstamp::from_string(source_array[4]).unwrap(),
+                node_uuid: NodeUUID(u32::from_str_radix(source_array[5], 16).unwrap()),
+                software: source_array[6].to_string(),
+                soft_version: source_array[7].to_string(),
+                prefix: source_array[8].parse().unwrap(),
+                free_member_room: if let Some(field) = source_array.get(9) {
+                    Some(field.parse().unwrap())
+                } else {
+                    None
+                },
+                free_mirror_room: if let Some(field) = source_array.get(10) {
+                    Some(field.parse().unwrap())
+                } else {
+                    None
+                },
+            }))
+        } else {
+            None
+        }
+    }
+    /// Get head blockcstamp
+    fn blockstamp(&self) -> Blockstamp {
+        match *self {
+            NetworkHeadMessage::V2(ref head_message_v2) => head_message_v2.blockstamp,
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Get head node id
+    fn node_uuid(&self) -> NodeUUID {
+        match *self {
+            NetworkHeadMessage::V2(ref head_message_v2) => head_message_v2.node_uuid,
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Get head issuer public key
+    fn _pubkey(&self) -> ed25519::PublicKey {
+        match *self {
+            NetworkHeadMessage::V2(ref head_message_v2) => head_message_v2.pubkey,
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+}
+
+impl ToString for NetworkHeadMessageV2 {
+    fn to_string(&self) -> String {
+        match self.version {
+            1 => format!(
+                "{}:HEAD:1:{}:{}:{}:{}:{}:{}",
+                self.api,
+                self.pubkey,
+                self.blockstamp,
+                self.node_uuid,
+                self.software,
+                self.soft_version,
+                self.prefix
+            ),
+            2 => format!(
+                "{}:HEAD:2:{}:{}:{}:{}:{}:{}:{}:{}",
+                self.api,
+                self.pubkey,
+                self.blockstamp,
+                self.node_uuid,
+                self.software,
+                self.soft_version,
+                self.prefix,
+                self.free_member_room.unwrap(),
+                self.free_mirror_room.unwrap()
+            ),
+            _ => panic!("NetworkHeadMessage is wrongly parsed !"),
+        }
+    }
+}
+
+impl ToString for NetworkHeadMessage {
+    fn to_string(&self) -> String {
+        match *self {
+            NetworkHeadMessage::V2(ref head_message) => head_message.to_string(),
+            _ => panic!("This HEADMessage version is not supported !"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+/// Head V2
+pub struct NetworkHeadV2 {
+    /// Head V1 Message
+    pub message: NetworkHeadMessage,
+    /// signature of V1 Message
+    pub sig: ed25519::Signature,
+    /// Head V2 Message
+    pub message_v2: NetworkHeadMessage,
+    /// signature of V2 Message
+    pub sig_v2: ed25519::Signature,
+    /// Head step
+    pub step: u32,
+    /// Head issuer uid
+    pub uid: Option<String>,
+}
+
+impl ToString for NetworkHeadV2 {
+    fn to_string(&self) -> String {
+        self.message_v2.to_string()
+    }
+}
+
+impl PartialOrd for NetworkHeadV2 {
+    fn partial_cmp(&self, other: &NetworkHeadV2) -> Option<Ordering> {
+        Some(self.cmp(other))
+    }
+}
+
+impl Ord for NetworkHeadV2 {
+    fn cmp(&self, other: &NetworkHeadV2) -> Ordering {
+        self.message.cmp(&other.message)
+    }
+}
+
+impl NetworkHeadV2 {
+    /// To human readable string
+    pub fn to_human_string(&self, max_len: usize) -> String {
+        if max_len > 2 {
+            format!(
+                "{} {}",
+                self.step,
+                self.message_v2
+                    .to_human_string(max_len - 2, self.uid.clone())
+            )
+        } else {
+            format!(".")
+        }
+    }
+    /// Get uid of head issuer
+    pub fn uid(&self) -> Option<String> {
+        self.uid.clone()
+    }
+}
+
+#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq)]
+/// Network Head : Set of information on the current state of a node, the central information being the blockstamp of its current block (= the head of its blockchain).
+pub enum NetworkHead {
+    /// Head V2
+    V2(Box<NetworkHeadV2>),
+    /// head V3
+    V3(),
+}
+
+impl ToString for NetworkHead {
+    fn to_string(&self) -> String {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.deref().to_string(),
+            _ => panic!("NetworkHead version not supported !"),
+        }
+    }
+}
+
+impl NetworkHead {
+    /// Get HEAD version
+    pub fn version(&self) -> u32 {
+        match *self {
+            NetworkHead::V2(_) => 2,
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Get HEAD blockstamp
+    pub fn blockstamp(&self) -> Blockstamp {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.message_v2.blockstamp(),
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Get pubkey of head issuer
+    pub fn pubkey(&self) -> ed25519::PublicKey {
+        match *self {
+            NetworkHead::V2(ref head_v2) => match head_v2.message_v2 {
+                NetworkHeadMessage::V2(ref head_message_v2) => head_message_v2.pubkey,
+                _ => panic!("This HEAD message version is not supported !"),
+            },
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Get uid of head issuer
+    pub fn uid(&self) -> Option<String> {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.uid(),
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Change uid of head issuer
+    pub fn set_uid(&mut self, uid: &str) {
+        match *self {
+            NetworkHead::V2(ref mut head_v2) => head_v2.uid = Some(String::from(uid)),
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// return the HEAD Step
+    pub fn step(&self) -> u32 {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.step,
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Checks the validity of all head signatures
+    pub fn verify(&self) -> bool {
+        match *self {
+            NetworkHead::V2(ref head_v2) => {
+                let pubkey: ed25519::PublicKey =
+                    PublicKey::from_base58(&self.pubkey().to_string()).unwrap();
+                pubkey.verify(head_v2.message.to_string().as_bytes(), &head_v2.sig)
+                    && pubkey.verify(head_v2.message_v2.to_string().as_bytes(), &head_v2.sig_v2)
+            }
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Returns issuer node id
+    pub fn node_uuid(&self) -> NodeUUID {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.message_v2.node_uuid(),
+            _ => panic!("This HEAD version is not supported !"),
+        }
+    }
+    /// Returns issuer node full identifier
+    pub fn node_full_id(&self) -> NodeFullId {
+        NodeFullId(self.node_uuid(), self.pubkey())
+    }
+    /// Returns true only if this head is to replace the old head of the same issuer in the head cache (or if it's the 1st head of this issuer)
+    pub fn apply(&self, heads_cache: &mut HashMap<NodeFullId, NetworkHead>) -> bool {
+        let heads_cache_copy = heads_cache.clone();
+        if let Some(head) = heads_cache_copy.get(&self.node_full_id()) {
+            if self.blockstamp().id.0 > head.blockstamp().id.0
+                || (self.blockstamp().id.0 == head.blockstamp().id.0
+                    && (self.version() >= head.version() || self.step() <= head.step()))
+            {
+                if let Some(head_mut) = heads_cache.get_mut(&self.node_full_id()) {
+                    *head_mut = self.clone();
+                    true
+                } else {
+                    false
+                }
+            } else {
+                false
+            }
+        } else {
+            heads_cache.insert(self.node_full_id(), self.clone());
+            true
+        }
+    }
+    /// Parse Json Head
+    pub fn from_json_value(source: &serde_json::Value) -> Option<NetworkHead> {
+        let message = NetworkHeadMessage::from_str(source.get("message")?.as_str().unwrap())?;
+        match message {
+            NetworkHeadMessage::V2(_) => Some(NetworkHead::V2(Box::new(NetworkHeadV2 {
+                message,
+                sig: Signature::from_base64(source.get("sig")?.as_str().unwrap()).unwrap(),
+                message_v2: NetworkHeadMessage::from_str(
+                    source.get("messageV2")?.as_str().unwrap(),
+                )?,
+                sig_v2: Signature::from_base64(source.get("sigV2")?.as_str().unwrap()).unwrap(),
+                step: source.get("step")?.as_u64().unwrap() as u32,
+                uid: None,
+            }))),
+            _ => None,
+        }
+    }
+    /// To human readable string
+    pub fn to_human_string(&self, max_len: usize) -> String {
+        match *self {
+            NetworkHead::V2(ref head_v2) => head_v2.deref().to_human_string(max_len),
+            _ => panic!("NetworkHead version not supported !"),
+        }
+    }
+}
diff --git a/network/network_peer.rs b/network/network_peer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5efe5ed188f3e4e939ac08ffdc67eb681c3d97b7
--- /dev/null
+++ b/network/network_peer.rs
@@ -0,0 +1,79 @@
+//  Copyright (C) 2017  The Duniter Project Developers.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero 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 Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+//! Module defining the format of network peer cards and how to handle them.
+
+extern crate crypto;
+extern crate duniter_crypto;
+extern crate duniter_documents;
+extern crate duniter_module;
+extern crate serde;
+extern crate serde_json;
+
+use super::network_endpoint::NetworkEndpoint;
+use duniter_crypto::keys::ed25519;
+use duniter_documents::Blockstamp;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Peer card V10
+pub struct NetworkPeerV10 {
+    /// Peer card Blockstamp
+    pub blockstamp: Blockstamp,
+    /// Peer card issuer
+    pub issuer: ed25519::PublicKey,
+    /// Peer card endpoints list
+    pub endpoints: Vec<NetworkEndpoint>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+/// Peer card
+pub enum NetworkPeer {
+    /// Peer card V10
+    V10(NetworkPeerV10),
+    /// Peer card V11
+    V11(),
+}
+
+impl NetworkPeer {
+    /// Get peer card version
+    pub fn version(&self) -> u32 {
+        match *self {
+            NetworkPeer::V10(ref _peer_v10) => 10,
+            _ => panic!("Peer version is not supported !"),
+        }
+    }
+    /// Get peer card blockstamp
+    pub fn blockstamp(&self) -> Blockstamp {
+        match *self {
+            NetworkPeer::V10(ref peer_v10) => peer_v10.blockstamp,
+            _ => panic!("Peer version is not supported !"),
+        }
+    }
+    /// Get peer card issuer
+    pub fn issuer(&self) -> ed25519::PublicKey {
+        match *self {
+            NetworkPeer::V10(ref peer_v10) => peer_v10.issuer,
+            _ => panic!("Peer version is not supported !"),
+        }
+    }
+    /// Verify validity of peer card signature
+    pub fn verify(&self) -> bool {
+        false
+    }
+    /// Get peer card endpoint
+    pub fn get_endpoints(&self) -> Vec<NetworkEndpoint> {
+        Vec::with_capacity(0)
+    }
+}