diff --git a/Cargo.lock b/Cargo.lock
index eb3b85a8743437a0d1ad4b4a303740cd8d6bb887..36fad37cd74fe9c995a48b042434abf12d114f38 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -389,6 +389,7 @@ dependencies = [
  "dup-crypto 0.6.0",
  "durs-common-tools 0.1.0",
  "durs-module 0.1.0-a0.1",
+ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "rpassword 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -418,6 +419,7 @@ dependencies = [
  "simplelog 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unwrap 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
diff --git a/doc/en/dev/developing-a-durs-module.md b/doc/en/dev/developing-a-durs-module.md
index c1dc07ddd5fe097dbd55e73d983b09649821d886..4454ef412f39c54ceb8236ee2a8567ec4590135b 100644
--- a/doc/en/dev/developing-a-durs-module.md
+++ b/doc/en/dev/developing-a-durs-module.md
@@ -216,8 +216,9 @@ If you do have a subcommand, simply return `true`.
         soft_meta_datas: &SoftwareMetaDatas<DC>,
         keys: RequiredKeysContent,
         module_conf: Self::ModuleConf,
+        module_user_conf: Option<Self::ModuleUserConf>,
         subcommand_args: Self::ModuleOpt,
-    ) {
+    ) -> Option<Self::ModuleUserConf> {
     }
 ```
 
diff --git a/doc/fr/developpeurs/developper-un-module-durs.md b/doc/fr/developpeurs/developper-un-module-durs.md
index 9d2d50ca41360c8a915c936b754c9fc9f2498f9c..2292390308b3c71941ae1d48fcc15cc665e40351 100644
--- a/doc/fr/developpeurs/developper-un-module-durs.md
+++ b/doc/fr/developpeurs/developper-un-module-durs.md
@@ -225,8 +225,9 @@ Déclaration :
         soft_meta_datas: &SoftwareMetaDatas<DC>,
         keys: RequiredKeysContent,
         module_conf: Self::ModuleConf,
+        module_user_conf: Option<Self::ModuleUserConf>,
         subcommand_args: Self::ModuleOpt,
-    ) {
+    ) -> Option<Self::ModuleUserConf> {
     }
 ```
 
diff --git a/lib/core/conf/Cargo.toml b/lib/core/conf/Cargo.toml
index 49eb83631465a2cd9206c35c1e07f4887471218d..9094937b9d3b69d1d6c03b6a63087bccc9ff5d55 100644
--- a/lib/core/conf/Cargo.toml
+++ b/lib/core/conf/Cargo.toml
@@ -15,6 +15,7 @@ dup-crypto = { path = "../../tools/crypto" }
 dubp-documents= { path = "../../tools/documents" }
 durs-module = { path = "../module" }
 durs-common-tools = { path = "../../tools/common-tools" }
+failure = "0.1.5"
 log = "0.4.*"
 rand = "0.4.*"
 serde = "1.0.*"
diff --git a/lib/core/conf/src/lib.rs b/lib/core/conf/src/lib.rs
index c60668367f79b56b592ba78ce9f4d28a73b7b331..85910f6ed466662ba2d1a0b918319f1ff4814291 100644
--- a/lib/core/conf/src/lib.rs
+++ b/lib/core/conf/src/lib.rs
@@ -41,6 +41,7 @@ use durs_common_tools::fatal_error;
 use durs_module::{
     DursConfTrait, DursGlobalConfTrait, ModuleName, RequiredKeys, RequiredKeysContent,
 };
+use failure::Fail;
 use rand::Rng;
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 use std::collections::HashSet;
@@ -503,9 +504,9 @@ pub fn keypairs_filepath(profiles_path: &Option<PathBuf>, profile: &str) -> Path
 pub fn load_conf(
     mut profile_path: PathBuf,
     keypairs_file_path: &Option<PathBuf>,
-) -> (DuRsConf, DuniterKeyPairs) {
+) -> Result<(DuRsConf, DuniterKeyPairs), DursConfFileError> {
     // Load conf
-    let (conf, keypairs) = load_conf_at_path(profile_path.clone(), keypairs_file_path);
+    let (conf, keypairs) = load_conf_at_path(profile_path.clone(), keypairs_file_path)?;
 
     // Create currency dir
     profile_path.push(conf.currency().to_string());
@@ -514,7 +515,21 @@ pub fn load_conf(
     }
 
     // Return conf and keypairs
-    (conf, keypairs)
+    Ok((conf, keypairs))
+}
+
+/// Error with configuration file
+#[derive(Debug, Fail)]
+pub enum DursConfFileError {
+    /// Read error
+    #[fail(display = "fail to read configuration file: {}", _0)]
+    ReadError(std::io::Error),
+    /// Parse error
+    #[fail(display = "fail to parse configuration file: {}", _0)]
+    ParseError(serde_json::Error),
+    /// Write error
+    #[fail(display = "fail to write configuration file: {}", _0)]
+    WriteError(std::io::Error),
 }
 
 /// Load configuration. at specified path
@@ -522,7 +537,7 @@ pub fn load_conf(
 pub fn load_conf_at_path(
     profile_path: PathBuf,
     keypairs_file_path: &Option<PathBuf>,
-) -> (DuRsConf, DuniterKeyPairs) {
+) -> Result<(DuRsConf, DuniterKeyPairs), DursConfFileError> {
     // Get KeyPairs
     let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path {
         keypairs_file_path.clone()
@@ -614,26 +629,24 @@ pub fn load_conf_at_path(
     let mut conf_path = profile_path;
     conf_path.push(constants::CONF_FILENAME);
     let conf = if conf_path.as_path().exists() {
-        if let Ok(mut f) = File::open(conf_path.as_path()) {
-            let mut contents = String::new();
-            if f.read_to_string(&mut contents).is_ok() {
+        match File::open(conf_path.as_path()) {
+            Ok(mut f) => {
+                let mut contents = String::new();
+                f.read_to_string(&mut contents)
+                    .map_err(DursConfFileError::ReadError)?;
                 // Parse conf file
                 let conf: DuRsConf =
-                    serde_json::from_str(&contents).expect("Conf: Fail to parse conf file !");
+                    serde_json::from_str(&contents).map_err(DursConfFileError::ParseError)?;
                 // Upgrade conf to latest version
                 let (conf, upgraded) = conf.upgrade();
                 // If conf is upgraded, rewrite conf file
                 if upgraded {
-                    write_conf_file(conf_path.as_path(), &conf).unwrap_or_else(|_| {
-                        panic!(dbg!("Fatal error : fail to write conf file !"))
-                    });
+                    write_conf_file(conf_path.as_path(), &conf)
+                        .map_err(DursConfFileError::WriteError)?;
                 }
                 conf
-            } else {
-                panic!("Fail to read conf file !");
             }
-        } else {
-            panic!("Fail to open conf file !");
+            Err(e) => return Err(DursConfFileError::ReadError(e)),
         }
     } else {
         // Create conf file with default conf
@@ -644,7 +657,7 @@ pub fn load_conf_at_path(
     };
 
     // Return conf and keypairs
-    (conf, keypairs)
+    Ok((conf, keypairs))
 }
 
 /// Save keypairs in profile folder
@@ -663,6 +676,19 @@ pub fn write_keypairs_file(
     Ok(())
 }
 
+/// Write new module conf
+pub fn write_new_module_conf<DC: DursConfTrait>(
+    conf: &mut DC,
+    profile_path: PathBuf,
+    module_name: ModuleName,
+    new_module_conf: serde_json::Value,
+) {
+    conf.set_module_conf(module_name, new_module_conf);
+    let mut conf_path = profile_path;
+    conf_path.push(crate::constants::CONF_FILENAME);
+    write_conf_file(conf_path.as_path(), conf).expect("Fail to write new conf file ! ");
+}
+
 /// Save configuration in profile folder
 pub fn write_conf_file<DC: DursConfTrait>(
     conf_path: &Path,
@@ -742,10 +768,11 @@ mod tests {
     }
 
     #[test]
-    fn load_conf_file_v1() -> std::io::Result<()> {
+    fn load_conf_file_v1() -> Result<(), DursConfFileError> {
         let profile_path = PathBuf::from("./test/v1/");
-        save_old_conf(PathBuf::from(profile_path.clone()))?;
-        let (conf, _keys) = load_conf_at_path(profile_path.clone(), &None);
+        save_old_conf(PathBuf::from(profile_path.clone()))
+            .map_err(DursConfFileError::WriteError)?;
+        let (conf, _keys) = load_conf_at_path(profile_path.clone(), &None)?;
         assert_eq!(
             conf.modules()
                 .get("ws2p")
@@ -768,15 +795,16 @@ mod tests {
                 ]
             })
         );
-        restore_old_conf_and_save_upgraded_conf(profile_path)?;
+        restore_old_conf_and_save_upgraded_conf(profile_path)
+            .map_err(DursConfFileError::WriteError)?;
 
         Ok(())
     }
 
     #[test]
-    fn load_conf_file_v2() {
+    fn load_conf_file_v2() -> Result<(), DursConfFileError> {
         let profile_path = PathBuf::from("./test/v2/");
-        let (conf, _keys) = load_conf_at_path(profile_path, &None);
+        let (conf, _keys) = load_conf_at_path(profile_path, &None)?;
         assert_eq!(
             conf.modules()
                 .get("ws2p")
@@ -799,5 +827,6 @@ mod tests {
                 ]
             })
         );
+        Ok(())
     }
 }
diff --git a/lib/core/core/Cargo.toml b/lib/core/core/Cargo.toml
index 708e843b3918347f0a61220427227a2346d1ec74..a158b7590306e4e420f47c093ce95c59aa3ed85b 100644
--- a/lib/core/core/Cargo.toml
+++ b/lib/core/core/Cargo.toml
@@ -27,5 +27,6 @@ serde_json = "1.0.*"
 simplelog = "0.5.*"
 structopt= "0.2.*"
 threadpool = "1.7.*"
+unwrap = "1.2.1"
 
 [features]
diff --git a/lib/core/core/src/errors.rs b/lib/core/core/src/errors.rs
index a7f541452aea638c304e31e6e5c790c34c5d1fa7..0aac0cdf1a2bf9789d21a76db3dfdecb6909d322 100644
--- a/lib/core/core/src/errors.rs
+++ b/lib/core/core/src/errors.rs
@@ -22,6 +22,9 @@ use failure::Fail;
 #[derive(Debug, Fail)]
 /// Durs server error
 pub enum DursCoreError {
+    /// Error with configuration file
+    #[fail(display = "Error with configuration file: {}", _0)]
+    ConfFileError(durs_conf::DursConfFileError),
     /// Fail to remove configuration file
     #[fail(display = "Fail to remove configuration file: {}", _0)]
     FailRemoveConfFile(std::io::Error),
diff --git a/lib/core/core/src/lib.rs b/lib/core/core/src/lib.rs
index ec5a683ba6a6fc5c9e09f5f03558ed1176076170..113b4e44ca646405cf2cb37945fc9d179474585e 100644
--- a/lib/core/core/src/lib.rs
+++ b/lib/core/core/src/lib.rs
@@ -55,6 +55,7 @@ use std::collections::HashMap;
 use std::path::PathBuf;
 use std::sync::mpsc;
 use std::thread;
+use unwrap::unwrap;
 
 #[macro_export]
 /// Plug modules in durs core
@@ -111,8 +112,7 @@ impl DursCore<DuRsConf> {
         soft_name: &'static str,
         soft_version: &'static str,
     ) -> Result<(), DursCoreError> {
-        let durs_core = DursCore::<DuRsConf>::init(soft_name, soft_version, durs_core_opts, 0)?;
-
+        let mut durs_core = DursCore::<DuRsConf>::init(soft_name, soft_version, durs_core_opts, 0)?;
         // Load module conf and keys
         let module_conf_json = durs_core
             .soft_meta_datas
@@ -122,7 +122,7 @@ impl DursCore<DuRsConf> {
             .get(&M::name().to_string().as_str())
             .cloned();
 
-        let (module_conf, required_keys) = get_module_conf_and_keys::<M>(
+        let ((module_conf, module_user_conf), required_keys) = get_module_conf_and_keys::<M>(
             &durs_core.soft_meta_datas.conf.get_global_conf(),
             module_conf_json,
             durs_core.keypairs,
@@ -131,15 +131,22 @@ impl DursCore<DuRsConf> {
             module_name: M::name(),
             error: e.into(),
         })?;
-
         // Execute module subcommand
-        M::exec_subcommand(
+        let new_module_conf = M::exec_subcommand(
             &durs_core.soft_meta_datas,
             required_keys,
             module_conf,
+            module_user_conf,
             module_command,
         );
 
+        durs_conf::write_new_module_conf(
+            &mut durs_core.soft_meta_datas.conf,
+            durs_core.soft_meta_datas.profile_path.clone(),
+            M::name().into(),
+            unwrap!(serde_json::value::to_value(new_module_conf)),
+        );
+
         Ok(())
     }
 
@@ -227,7 +234,8 @@ impl DursCore<DuRsConf> {
 
         // Load global conf
         let (conf, keypairs) =
-            durs_conf::load_conf(profile_path.clone(), &durs_core_opts.keypairs_file);
+            durs_conf::load_conf(profile_path.clone(), &durs_core_opts.keypairs_file)
+                .map_err(DursCoreError::ConfFileError)?;
         info!("Success to load global conf.");
 
         // Instanciate durs core
@@ -355,7 +363,7 @@ impl DursCore<DuRsConf> {
                 let keypairs = self.keypairs;
 
                 // Load module conf and keys
-                let (module_conf, required_keys) = get_module_conf_and_keys::<NM>(
+                let ((module_conf, _), required_keys) = get_module_conf_and_keys::<NM>(
                     &soft_meta_datas.conf.get_global_conf(),
                     module_conf_json,
                     keypairs,
@@ -429,7 +437,7 @@ impl DursCore<DuRsConf> {
                     .cloned();
                 let keypairs = self.keypairs;
                 // Load module conf and keys
-                let (module_conf, required_keys) = get_module_conf_and_keys::<M>(
+                let ((module_conf, _), required_keys) = get_module_conf_and_keys::<M>(
                     &soft_meta_datas.conf.get_global_conf(),
                     module_conf_json,
                     keypairs,
@@ -477,12 +485,21 @@ impl DursCore<DuRsConf> {
     }
 }
 
+/// Module configurations and required keys
+pub type ModuleConfsAndKeys<M> = (
+    (
+        <M as DursModule<DuRsConf, DursMsg>>::ModuleConf,
+        Option<<M as DursModule<DuRsConf, DursMsg>>::ModuleUserConf>,
+    ),
+    RequiredKeysContent,
+);
+
 /// Get module conf and keys
 pub fn get_module_conf_and_keys<M: DursModule<DuRsConf, DursMsg>>(
     global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
     module_conf_json: Option<serde_json::Value>,
     keypairs: DuniterKeyPairs,
-) -> Result<(M::ModuleConf, RequiredKeysContent), ModuleConfError> {
+) -> Result<ModuleConfsAndKeys<M>, ModuleConfError> {
     Ok((
         get_module_conf::<M>(global_conf, module_conf_json)?,
         DuniterKeyPairs::get_required_keys_content(M::ask_required_keys(), keypairs),
@@ -493,11 +510,11 @@ pub fn get_module_conf_and_keys<M: DursModule<DuRsConf, DursMsg>>(
 pub fn get_module_conf<M: DursModule<DuRsConf, DursMsg>>(
     global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
     module_conf_json: Option<serde_json::Value>,
-) -> Result<M::ModuleConf, ModuleConfError> {
+) -> Result<(M::ModuleConf, Option<M::ModuleUserConf>), ModuleConfError> {
     if let Some(module_conf_json) = module_conf_json {
-        let module_user_conf: M::ModuleUserConf =
+        let module_user_conf: Option<M::ModuleUserConf> =
             serde_json::from_str(module_conf_json.to_string().as_str())?;
-        M::generate_module_conf(global_conf, Some(module_user_conf))
+        M::generate_module_conf(global_conf, module_user_conf)
     } else {
         M::generate_module_conf(global_conf, None)
     }
diff --git a/lib/core/module/src/lib.rs b/lib/core/module/src/lib.rs
index 8db64900bb52b48294cf3fabcddf19397e4762e6..eaa7e56de816994a51882cb3fed3260a19c5d834 100644
--- a/lib/core/module/src/lib.rs
+++ b/lib/core/module/src/lib.rs
@@ -343,15 +343,23 @@ pub fn module_valid_filters<
 #[derive(Debug, Fail)]
 /// Error when generating the configuration of a module
 pub enum ModuleConfError {
-    /// Parse error
-    #[fail(display = "{}", _0)]
-    ParseError(serde_json::Error),
     /// Combination forbidden
     #[fail(display = "Forbidden configuration: {}", cause)]
     CombinationForbidden {
         /// Cause
         cause: String,
     },
+    /// Invalid field
+    #[fail(display = "Field '{}' is invalid: {}", field_name, cause)]
+    InvalidField {
+        /// Field name
+        field_name: &'static str,
+        /// Cause
+        cause: String,
+    },
+    /// Parse error
+    #[fail(display = "{}", _0)]
+    ParseError(serde_json::Error),
 }
 
 impl From<serde_json::Error> for ModuleConfError {
@@ -388,7 +396,7 @@ impl From<ModuleConfError> for PlugModuleError {
 /// All Duniter-rs modules must implement this trait.
 pub trait DursModule<DC: DursConfTrait, M: ModuleMessage> {
     ///Module user configuration (configuration provided by the user)
-    type ModuleUserConf: Clone + Debug + DeserializeOwned + Send + Serialize + Sync;
+    type ModuleUserConf: Clone + Debug + Default + DeserializeOwned + Send + Serialize + Sync;
     /// Module real configuration (configuration calculated from the configuration provided by the user and the global configuration)
     type ModuleConf: 'static + Clone + Debug + Default + Send + Sync;
     /// Module subcommand options
@@ -404,7 +412,7 @@ pub trait DursModule<DC: DursConfTrait, M: ModuleMessage> {
     fn generate_module_conf(
         global_conf: &DC::GlobalConf,
         module_user_conf: Option<Self::ModuleUserConf>,
-    ) -> Result<Self::ModuleConf, ModuleConfError>;
+    ) -> Result<(Self::ModuleConf, Option<Self::ModuleUserConf>), ModuleConfError>;
     /// Define if module have a cli subcommand
     fn have_subcommand() -> bool {
         false
@@ -414,8 +422,10 @@ pub trait DursModule<DC: DursConfTrait, M: ModuleMessage> {
         _soft_meta_datas: &SoftwareMetaDatas<DC>,
         _keys: RequiredKeysContent,
         _module_conf: Self::ModuleConf,
+        _module_user_conf: Option<Self::ModuleUserConf>,
         _subcommand_args: Self::ModuleOpt,
-    ) {
+    ) -> Option<Self::ModuleUserConf> {
+        None
     }
     /// Launch the module
     fn start(
diff --git a/lib/modules/skeleton/lib.rs b/lib/modules/skeleton/lib.rs
index f983f75177f15f3e37261ed6b2d11facc8b91a00..3405246db33909828c764b206c192889071fd5d5 100644
--- a/lib/modules/skeleton/lib.rs
+++ b/lib/modules/skeleton/lib.rs
@@ -62,7 +62,7 @@ impl Default for SkeletonConf {
     }
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// Skeleton Module Configuration
 pub struct SkeletonUserConf {
     test_fake_conf_field: Option<String>,
@@ -132,40 +132,32 @@ impl DursModule<DuRsConf, DursMsg> for SkeletonModule {
     fn generate_module_conf(
         _global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
         module_user_conf: Option<Self::ModuleUserConf>,
-    ) -> Result<Self::ModuleConf, ModuleConfError> {
+    ) -> Result<(Self::ModuleConf, Option<Self::ModuleUserConf>), ModuleConfError> {
         let mut conf = SkeletonConf::default();
 
-        if let Some(module_user_conf) = module_user_conf {
-            if let Some(test_fake_conf_field) = module_user_conf.test_fake_conf_field {
-                conf.test_fake_conf_field = test_fake_conf_field;
+        if let Some(ref module_user_conf) = module_user_conf {
+            if let Some(ref test_fake_conf_field) = module_user_conf.test_fake_conf_field {
+                conf.test_fake_conf_field = test_fake_conf_field.to_owned();
             }
         }
 
-        Ok(conf)
+        Ok((conf, module_user_conf))
     }
     fn exec_subcommand(
-        soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
+        _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
         _keys: RequiredKeysContent,
         module_conf: Self::ModuleConf,
+        _module_user_conf: Option<Self::ModuleUserConf>,
         subcommand_args: Self::ModuleOpt,
-    ) {
-        let mut conf = soft_meta_datas.conf.clone();
-        let new_skeleton_conf = SkeletonConf {
-            test_fake_conf_field: subcommand_args.new_conf_field.clone(),
+    ) -> Option<Self::ModuleUserConf> {
+        let new_skeleton_conf = SkeletonUserConf {
+            test_fake_conf_field: Some(subcommand_args.new_conf_field.to_owned()),
         };
-        conf.set_module_conf(
-            ModuleName(MODULE_NAME.to_owned()),
-            serde_json::value::to_value(new_skeleton_conf)
-                .expect("Fail to jsonifie SkeletonConf !"),
-        );
-        let mut conf_path = soft_meta_datas.profile_path.clone();
-        conf_path.push(durs_conf::constants::CONF_FILENAME);
-        durs_conf::write_conf_file(conf_path.as_path(), &conf)
-            .expect("Fail to write new conf file ! ");
         println!(
             "Succesfully exec skeleton subcommand whit terminal name : {} and conf={:?}!",
             subcommand_args.new_conf_field, module_conf
-        )
+        );
+        Some(new_skeleton_conf)
     }
     fn start(
         _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
diff --git a/lib/modules/tui/lib.rs b/lib/modules/tui/lib.rs
index b065e35db36813187192937b52759937e69e6eb7..e0aa018b723a004376d821e0c7e2b2ed3c8778e6 100644
--- a/lib/modules/tui/lib.rs
+++ b/lib/modules/tui/lib.rs
@@ -390,8 +390,8 @@ impl DursModule<DuRsConf, DursMsg> for TuiModule {
     fn generate_module_conf(
         _global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
         _module_user_conf: Option<Self::ModuleUserConf>,
-    ) -> Result<Self::ModuleConf, ModuleConfError> {
-        Ok(TuiConf {})
+    ) -> Result<(Self::ModuleConf, Option<Self::ModuleUserConf>), ModuleConfError> {
+        Ok((TuiConf {}, None))
     }
     fn start(
         _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
diff --git a/lib/modules/ws2p-v1-legacy/src/lib.rs b/lib/modules/ws2p-v1-legacy/src/lib.rs
index 292d44f2f5fa6622632e30bd704430c987b4a5f5..f70e5df0bb577b067b0e3e0588cf286af45d124b 100644
--- a/lib/modules/ws2p-v1-legacy/src/lib.rs
+++ b/lib/modules/ws2p-v1-legacy/src/lib.rs
@@ -43,6 +43,7 @@ pub mod parsers;
 mod requests;
 mod responses;
 pub mod serializer;
+mod subcommands;
 pub mod ws2p_db;
 pub mod ws_connections;
 
@@ -52,6 +53,7 @@ use crate::constants::*;
 use crate::ok_message::WS2POkMessageV1;
 use crate::parsers::blocks::parse_json_block;
 use crate::requests::sent::send_dal_request;
+use crate::subcommands::WS2PSubCommands;
 use crate::ws2p_db::DbEndpoint;
 use crate::ws_connections::messages::WS2PConnectionMessage;
 use crate::ws_connections::states::WS2PConnectionState;
@@ -74,10 +76,11 @@ use durs_network_documents::network_endpoint::*;
 use durs_network_documents::network_head::*;
 use durs_network_documents::*;
 use serde::{Deserialize, Serialize};
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::fs;
 use std::ops::Deref;
 use std::path::PathBuf;
+use std::str::FromStr;
 use std::sync::mpsc;
 use std::thread;
 use std::time::{Duration, SystemTime, UNIX_EPOCH};
@@ -95,28 +98,33 @@ pub fn ssl() -> bool {
     true
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
 /// WS2P Configuration
-pub struct WS2PConf {
+pub struct WS2PUserConf {
     /// Limit of outcoming connections
-    pub outcoming_quota: usize,
+    pub outcoming_quota: Option<usize>,
+    /// List of prefered public keys
+    pub prefered_pubkeys: Option<HashSet<String>>,
     /// Default WS2P endpoints provides by configuration file
-    pub sync_endpoints: Vec<EndpointV1>,
+    pub sync_endpoints: Option<Vec<EndpointV1>>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 /// WS2P Configuration
-pub struct WS2PUserConf {
+pub struct WS2PConf {
     /// Limit of outcoming connections
-    pub outcoming_quota: Option<usize>,
+    pub outcoming_quota: usize,
+    /// List of prefered public keys
+    pub prefered_pubkeys: HashSet<PubKey>,
     /// Default WS2P endpoints provides by configuration file
-    pub sync_endpoints: Option<Vec<EndpointV1>>,
+    pub sync_endpoints: Vec<EndpointV1>,
 }
 
 impl Default for WS2PConf {
     fn default() -> Self {
         WS2PConf {
             outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,
+            prefered_pubkeys: HashSet::new(),
             sync_endpoints: vec![
                 unwrap!(EndpointV1::parse_from_raw(
                     "WS2P c1c39a0a ts.g1.librelois.fr 443 /ws2p",
@@ -318,13 +326,25 @@ impl NetworkModule<DuRsConf, DursMsg> for WS2PModule {
     }
 }
 
-#[derive(StructOpt, Debug, Copy, Clone)]
+#[derive(Clone, Debug, StructOpt)]
 #[structopt(
     name = "ws2p",
     raw(setting = "structopt::clap::AppSettings::ColoredHelp")
 )]
-/// WS2Pv1 subcommand options
-pub struct WS2POpt {}
+/// WS2P1 subcommand options
+pub struct WS2POpt {
+    /// Ws2p1 subcommands
+    #[structopt(subcommand)]
+    pub subcommand: WS2PSubCommands,
+}
+
+macro_rules! fields_overload {
+    ($struct:ident; $option_struct:ident; [$($field:ident),+]) => {{
+        $(if let Some($field) = $option_struct.$field {
+            $struct.$field = $field;
+        })+
+    }};
+}
 
 impl DursModule<DuRsConf, DursMsg> for WS2PModule {
     type ModuleUserConf = WS2PUserConf;
@@ -343,10 +363,11 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule {
     fn have_subcommand() -> bool {
         true
     }
+
     fn generate_module_conf(
         global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
         module_user_conf: Option<Self::ModuleUserConf>,
-    ) -> Result<Self::ModuleConf, ModuleConfError> {
+    ) -> Result<(Self::ModuleConf, Option<Self::ModuleUserConf>), ModuleConfError> {
         let mut conf = WS2PConf::default();
 
         if global_conf.currency() == CurrencyName("g1-test".to_owned()) {
@@ -360,24 +381,49 @@ impl DursModule<DuRsConf, DursMsg> for WS2PModule {
             ))];
         }
 
-        if let Some(module_user_conf) = module_user_conf {
-            if let Some(outcoming_quota) = module_user_conf.outcoming_quota {
+        if let Some(module_user_conf) = module_user_conf.clone() {
+            /*if let Some(outcoming_quota) = module_user_conf.outcoming_quota {
                 conf.outcoming_quota = outcoming_quota;
             }
             if let Some(sync_endpoints) = module_user_conf.sync_endpoints {
                 conf.sync_endpoints = sync_endpoints;
+            }*/
+            if let Some(prefered_pubkeys) = module_user_conf.prefered_pubkeys {
+                conf.prefered_pubkeys = prefered_pubkeys
+                    .iter()
+                    .enumerate()
+                    .map(|(i, p)| {
+                        PubKey::from_str(p).map_err(|e| ModuleConfError::InvalidField {
+                            field_name: stringify!(prefered_pubkeys),
+                            cause: format!("pubkey n°{} is invalid: {}", i, e),
+                        })
+                    })
+                    .collect::<Result<HashSet<PubKey>, ModuleConfError>>()?;
             }
+            fields_overload!(
+                conf;
+                module_user_conf;
+                [
+                    outcoming_quota,
+                    sync_endpoints
+                ]
+            )
         }
 
-        Ok(conf)
+        Ok((conf, module_user_conf))
     }
     fn exec_subcommand(
         _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
         _keys: RequiredKeysContent,
         _module_conf: Self::ModuleConf,
-        _subcommand_args: WS2POpt,
-    ) {
-        println!("Succesfully exec ws2p subcommand !")
+        module_user_conf: Option<Self::ModuleUserConf>,
+        opts: WS2POpt,
+    ) -> Option<Self::ModuleUserConf> {
+        match opts.subcommand {
+            WS2PSubCommands::Prefered {
+                subcommand: prefered_subcommand,
+            } => prefered_subcommand.execute(module_user_conf),
+        }
     }
     fn start(
         soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
diff --git a/lib/modules/ws2p-v1-legacy/src/subcommands/mod.rs b/lib/modules/ws2p-v1-legacy/src/subcommands/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..cab62bf96eeec9ec4310244e08433c73d2f6bfd8
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/subcommands/mod.rs
@@ -0,0 +1,34 @@
+//  Copyright (C) 2018  The Durs 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/>.
+
+//! WS2P1 module subcommands
+
+pub mod prefered;
+
+use prefered::Ws2pPreferedSubCommands;
+
+#[derive(Clone, Debug, StructOpt)]
+/// Ws2p1 subcommands
+pub enum WS2PSubCommands {
+    /// Prefered keys
+    #[structopt(
+        name = "prefered",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    Prefered {
+        #[structopt(subcommand)]
+        subcommand: Ws2pPreferedSubCommands,
+    },
+}
diff --git a/lib/modules/ws2p-v1-legacy/src/subcommands/prefered.rs b/lib/modules/ws2p-v1-legacy/src/subcommands/prefered.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a442661cae09acede1d61c523c97c394f15f29ec
--- /dev/null
+++ b/lib/modules/ws2p-v1-legacy/src/subcommands/prefered.rs
@@ -0,0 +1,174 @@
+//  Copyright (C) 2018  The Durs 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/>.
+
+//! WS2P1 module subcommand prefered
+
+use dup_crypto::keys::PubKey;
+use std::collections::HashSet;
+use std::fs;
+use std::io::BufRead;
+use std::path::PathBuf;
+use std::str::FromStr;
+
+#[derive(Clone, Debug, StructOpt)]
+/// Ws2p1 prefered subcommands
+pub enum Ws2pPreferedSubCommands {
+    /// Add prefered pubkey
+    #[structopt(
+        name = "add",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    Add {
+        /// Public key to add
+        public_keys: Vec<PubKey>,
+    },
+    /// Add prefered pubkeys from file (one pubkey per line)
+    #[structopt(
+        name = "add-file",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    AddFromFile {
+        /// File path
+        #[structopt(parse(from_os_str))]
+        file_path: PathBuf,
+    },
+    /// Clear prefered pubkeys
+    #[structopt(
+        name = "clear",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    Clear,
+    /// Remove prefered pubkey
+    #[structopt(
+        name = "rem",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    Rem {
+        /// Public key to remove
+        public_keys: Vec<PubKey>,
+    },
+    /// Show prefered pubkeys
+    #[structopt(
+        name = "show",
+        raw(setting = "structopt::clap::AppSettings::ColoredHelp")
+    )]
+    Show,
+}
+
+impl Ws2pPreferedSubCommands {
+    pub fn execute(
+        self,
+        module_user_conf: Option<crate::WS2PUserConf>,
+    ) -> Option<crate::WS2PUserConf> {
+        {
+            let mut prefered_pubkeys = if let Some(ref module_user_conf) = module_user_conf {
+                module_user_conf
+                    .prefered_pubkeys
+                    .clone()
+                    .unwrap_or_else(HashSet::new)
+            } else {
+                HashSet::new()
+            };
+
+            match self {
+                Ws2pPreferedSubCommands::Add { public_keys } => {
+                    for pubkey in public_keys {
+                        prefered_pubkeys.insert(pubkey.to_string());
+                        println!(
+                            "Pubkey '{}' successfully added to the list of preferred keys.",
+                            pubkey
+                        );
+                    }
+                    let mut new_user_conf = module_user_conf.unwrap_or_default();
+                    new_user_conf.prefered_pubkeys = Some(prefered_pubkeys);
+                    Some(new_user_conf)
+                }
+                Ws2pPreferedSubCommands::AddFromFile { file_path } => {
+                    if file_path.as_path().exists() {
+                        match fs::File::open(file_path.as_path()) {
+                            Ok(file) => {
+                                let mut new_prefered_pubkeys = HashSet::new();
+                                for (i, line) in std::io::BufReader::new(file).lines().enumerate() {
+                                    match line {
+                                        Ok(line) => match PubKey::from_str(&line) {
+                                            Ok(pubkey) => {
+                                                new_prefered_pubkeys.insert(pubkey.to_string());
+                                                println!(
+                                                            "Pubkey '{}' successfully added to the list of preferred keys.",
+                                                            pubkey
+                                                        );
+                                            }
+                                            Err(e) => {
+                                                println!("Line n°{} is invalid: {}", i + 1, e);
+                                            }
+                                        },
+                                        Err(e) => {
+                                            println!("Fail to read line n°{}: {}", i + 1, e);
+                                            return module_user_conf;
+                                        }
+                                    }
+                                }
+                                let mut new_user_conf = module_user_conf.unwrap_or_default();
+                                if let Some(ref mut prefered_pubkeys) =
+                                    new_user_conf.prefered_pubkeys
+                                {
+                                    prefered_pubkeys.extend(new_prefered_pubkeys.into_iter());
+                                } else {
+                                    new_user_conf.prefered_pubkeys = Some(new_prefered_pubkeys);
+                                }
+                                Some(new_user_conf)
+                            }
+                            Err(e) => {
+                                println!("Fail to open file: {}", e);
+                                module_user_conf
+                            }
+                        }
+                    } else {
+                        println!("Error: file note exist !");
+                        module_user_conf
+                    }
+                }
+                Ws2pPreferedSubCommands::Clear => {
+                    if let Some(mut module_user_conf) = module_user_conf {
+                        module_user_conf.prefered_pubkeys = None;
+                        println!("All preferred keys removed !");
+                        Some(module_user_conf)
+                    } else {
+                        module_user_conf
+                    }
+                }
+                Ws2pPreferedSubCommands::Rem { public_keys } => {
+                    for pubkey in public_keys {
+                        prefered_pubkeys.remove(&pubkey.to_string());
+                        println!(
+                            "Pubkey '{}' successfully removed from the list of preferred keys",
+                            pubkey
+                        );
+                    }
+                    let mut new_user_conf = module_user_conf.unwrap_or_default();
+                    new_user_conf.prefered_pubkeys = Some(prefered_pubkeys);
+                    Some(new_user_conf)
+                }
+                Ws2pPreferedSubCommands::Show => {
+                    println!("{} preferred keys: ", prefered_pubkeys.len());
+                    for pubkey in &prefered_pubkeys {
+                        println!("{}", pubkey);
+                    }
+                    module_user_conf
+                }
+            }
+        }
+    }
+}
diff --git a/lib/modules/ws2p/ws2p/src/lib.rs b/lib/modules/ws2p/ws2p/src/lib.rs
index 04f96f2a91e1218d1d1ed619ddab2b6ad1b84205..653fd29a8246fa68a750aa32e5120ed622b627ec 100644
--- a/lib/modules/ws2p/ws2p/src/lib.rs
+++ b/lib/modules/ws2p/ws2p/src/lib.rs
@@ -56,7 +56,7 @@ pub struct WS2PConf {
     pub sync_endpoints: Vec<EndpointEnum>,
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
 /// WS2P Configuration
 pub struct WS2PUserConf {
     /// Limit of outcoming connections
@@ -160,10 +160,10 @@ impl DursModule<DuRsConf, DursMsg> for WS2Pv2Module {
     fn generate_module_conf(
         _global_conf: &<DuRsConf as DursConfTrait>::GlobalConf,
         module_user_conf: Option<Self::ModuleUserConf>,
-    ) -> Result<Self::ModuleConf, ModuleConfError> {
+    ) -> Result<(Self::ModuleConf, Option<Self::ModuleUserConf>), ModuleConfError> {
         let mut conf = WS2PConf::default();
 
-        if let Some(module_user_conf) = module_user_conf {
+        if let Some(module_user_conf) = module_user_conf.clone() {
             if let Some(outcoming_quota) = module_user_conf.outcoming_quota {
                 conf.outcoming_quota = outcoming_quota;
             }
@@ -172,15 +172,17 @@ impl DursModule<DuRsConf, DursMsg> for WS2Pv2Module {
             }
         }
 
-        Ok(conf)
+        Ok((conf, module_user_conf))
     }
     fn exec_subcommand(
         _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
         _keys: RequiredKeysContent,
         _module_conf: Self::ModuleConf,
+        _module_user_conf: Option<Self::ModuleUserConf>,
         _subcommand_args: WS2POpt,
-    ) {
-        println!("Succesfully exec ws2p subcommand !")
+    ) -> Option<Self::ModuleUserConf> {
+        println!("Succesfully exec ws2p subcommand !");
+        None
     }
     fn start(
         _soft_meta_datas: &SoftwareMetaDatas<DuRsConf>,
diff --git a/lib/tools/crypto/src/keys/mod.rs b/lib/tools/crypto/src/keys/mod.rs
index d464e60336e9c59b2add365eea21d39f90204588..9a660fec16dbedb48bcf5278c1263455bfadcfb5 100644
--- a/lib/tools/crypto/src/keys/mod.rs
+++ b/lib/tools/crypto/src/keys/mod.rs
@@ -55,6 +55,7 @@ use std::fmt::Display;
 use std::fmt::Error;
 use std::fmt::Formatter;
 use std::hash::Hash;
+use std::str::FromStr;
 
 pub mod bin_signable;
 pub mod ed25519;
@@ -270,6 +271,14 @@ impl Display for PubKey {
     }
 }
 
+impl FromStr for PubKey {
+    type Err = BaseConvertionError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        ed25519::PublicKey::from_base58(s).map(PubKey::Ed25519)
+    }
+}
+
 impl PublicKey for PubKey {
     type Signature = Sig;