diff --git a/Cargo.toml b/Cargo.toml
index efc3bc36781382788b8702ebdeaa2825494a6417..650d3911048c356e6ee1378a53391c7bc1da5c9b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ members = [
     "lib/tools/documents",
     "lib/tools/json-pest-parser",
     "lib/tools/network-documents",
+    "lib/tools/rules-engine",
     "lib/tests-tools/crypto-tests-tools",
     "lib/tests-tools/documents-tests-tools",
     "lib/tests-tools/rust-tests-tools",
diff --git a/lib/tools/rules-engine/Cargo.toml b/lib/tools/rules-engine/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..a206cdd485eff0a5813395e21cea783bb0671235
--- /dev/null
+++ b/lib/tools/rules-engine/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "rules-engine"
+version = "0.1.0"
+authors = ["librelois <elois@ifee.fr>"]
+description = "Rules engine"
+license = "AGPL-3.0"
+edition = "2018"
+
+[lib]
+path = "src/lib.rs"
+
+[dependencies]
+failure = "0.1.5"
+rayon = "1.0.3"
+
+[dev-dependencies]
+maplit = "1.0.1"
diff --git a/lib/tools/rules-engine/src/lib.rs b/lib/tools/rules-engine/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..800711925a897ea2a5577ee33aa3c1d93240cae9
--- /dev/null
+++ b/lib/tools/rules-engine/src/lib.rs
@@ -0,0 +1,479 @@
+//  Copyright (C) 2019  Éloïs SANCHEZ
+//
+// 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/>.
+
+//! Rules engine
+
+#![deny(
+    missing_copy_implementations,
+    trivial_casts,
+    trivial_numeric_casts,
+    unsafe_code,
+    unstable_features,
+    unused_import_braces
+)]
+
+pub mod rule;
+
+use failure::Fail;
+use rayon::prelude::*;
+use rule::{Rule, RuleError, RuleNumber};
+use std::collections::BTreeMap;
+use std::fmt::Debug;
+
+#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]
+pub struct ProtocolVersion(pub usize);
+
+impl std::fmt::Display for ProtocolVersion {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ProtocolRules(pub Vec<RulesGroup>);
+
+impl From<Vec<usize>> for ProtocolRules {
+    fn from(rules_numbers: Vec<usize>) -> Self {
+        ProtocolRules(vec![RulesGroup::Ser(
+            rules_numbers.into_iter().map(RuleNumber).collect(),
+        )])
+    }
+}
+
+impl From<Vec<RulesGroup>> for ProtocolRules {
+    fn from(rules_groups: Vec<RulesGroup>) -> Self {
+        ProtocolRules(rules_groups)
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+/// Protocol
+pub struct Protocol(BTreeMap<ProtocolVersion, ProtocolRules>);
+
+impl Protocol {
+    /// Create new protocol
+    /// protocol_versions: Dictionary of rules to be applied for each version of the protocol (rules will be applied in the order provided)
+    pub fn new(protocol_versions: BTreeMap<ProtocolVersion, ProtocolRules>) -> Self {
+        Protocol(protocol_versions)
+    }
+
+    /// Get specific protocol version
+    pub fn get(&self, protocol_version: ProtocolVersion) -> Option<&ProtocolRules> {
+        self.0.get(&protocol_version)
+    }
+}
+
+/// Rules groups
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum RulesGroup {
+    /// In serial
+    Ser(Vec<RuleNumber>),
+    /// In parallel
+    Par(Vec<RulesGroup>),
+}
+
+impl RulesGroup {
+    #[inline]
+    /// Create singleton rules group
+    pub fn s1(rule_number: usize) -> Self {
+        RulesGroup::Ser(vec![RuleNumber(rule_number)])
+    }
+    #[inline]
+    /// Create parallel set of rules
+    pub fn pr(rules_numbers: Vec<usize>) -> Self {
+        RulesGroup::Par(rules_numbers.into_iter().map(RulesGroup::s1).collect())
+    }
+}
+
+/// Rules engine
+pub struct RulesEngine<D: Debug + Sync, E: Eq + Fail + PartialEq> {
+    /// All rules
+    all_rules: BTreeMap<RuleNumber, Rule<D, E>>,
+}
+
+impl<D: Debug + Sync, E: Eq + Fail + PartialEq> RulesEngine<D, E> {
+    /// Create new rules engine
+    pub fn new(all_rules: BTreeMap<RuleNumber, Rule<D, E>>) -> Self {
+        RulesEngine { all_rules }
+    }
+
+    fn apply_rules_group_ref(
+        &self,
+        protocol_version: ProtocolVersion,
+        rules_group: RulesGroup,
+        rule_datas: &D,
+    ) -> Result<(), EngineError<E>> {
+        match rules_group {
+            RulesGroup::Ser(rules_numbers) => rules_numbers
+                .into_iter()
+                .map(|rule_number| self.apply_rule_ref(protocol_version, rule_number, rule_datas))
+                .collect(),
+            RulesGroup::Par(rules_group) => rules_group
+                .into_par_iter()
+                .map(|rg| self.apply_rules_group_ref(protocol_version, rg, rule_datas))
+                .collect(),
+        }
+    }
+
+    fn apply_rule_ref(
+        &self,
+        protocol_version: ProtocolVersion,
+        rule_number: RuleNumber,
+        rule_datas: &D,
+    ) -> Result<(), EngineError<E>> {
+        if let Some(rule) = self.all_rules.get(&rule_number) {
+            rule.execute(protocol_version, rule_number, rule_datas)
+        } else {
+            Err(EngineError::RuleNotExist {
+                rule_number,
+                protocol_version,
+            })
+        }
+    }
+
+    fn apply_rule_mut(
+        &self,
+        protocol_version: ProtocolVersion,
+        rule_number: RuleNumber,
+        rule_datas: &mut D,
+    ) -> Result<(), EngineError<E>> {
+        if let Some(rule) = self.all_rules.get(&rule_number) {
+            rule.execute_mut(protocol_version, rule_number, rule_datas)
+        } else {
+            Err(EngineError::RuleNotExist {
+                rule_number,
+                protocol_version,
+            })
+        }
+    }
+
+    /// Apply a specific version of the protocol
+    pub fn apply_protocol(
+        &self,
+        protocol: Protocol,
+        protocol_version: ProtocolVersion,
+        rule_datas: &mut D,
+    ) -> Result<(), EngineError<E>> {
+        if let Some(protocol_rules) = protocol.get(protocol_version) {
+            for rules_group in &protocol_rules.0 {
+                let result: Result<(), EngineError<E>> = match rules_group {
+                    RulesGroup::Ser(rules_numbers) => rules_numbers
+                        .iter()
+                        .map(|rule_number| {
+                            self.apply_rule_mut(protocol_version, *rule_number, rule_datas)
+                        })
+                        .collect(),
+                    RulesGroup::Par(rules_group) => rules_group
+                        .par_iter()
+                        .map(|rg| {
+                            self.apply_rules_group_ref(protocol_version, rg.clone(), rule_datas)
+                        })
+                        .collect(),
+                };
+                if let Err(err) = result {
+                    return Err(err);
+                }
+            }
+
+            Ok(())
+        } else {
+            Err(EngineError::ProtocolVersionNotExist { protocol_version })
+        }
+    }
+}
+
+/// Protocol error
+#[derive(Debug, Eq, Fail, PartialEq)]
+pub enum EngineError<E: Eq + Fail + PartialEq> {
+    #[fail(display = "{}", _0)]
+    /// Rule Error
+    RuleError(RuleError<E>),
+    #[fail(display = "protocol V{} not exist", protocol_version)]
+    /// The protocol version does not exist
+    ProtocolVersionNotExist {
+        /// Protocole version
+        protocol_version: ProtocolVersion,
+    },
+    #[fail(
+        display = "Rule n°{} not exist (require by protocol V{})",
+        rule_number, protocol_version
+    )]
+    /// A rule required by the protocol version does not exist
+    RuleNotExist {
+        /// Rule number
+        rule_number: RuleNumber,
+        /// Protocole version
+        protocol_version: ProtocolVersion,
+    },
+    #[fail(
+        display = "Rule n°{} is mutable and called in parallel in the V{} protocol, this is prohibited.
+    A rule can be mutable or called in parallel but not both at the same time.",
+        rule_number, protocol_version
+    )]
+    /// Calling a mutable rule in a part executed in parallel
+    MutRuleInPar {
+        /// Rule number
+        rule_number: RuleNumber,
+        /// Protocole version
+        protocol_version: ProtocolVersion,
+    },
+    #[fail(
+        display = "Rule n°{} does not exist in a version less than or equal to the protocol version (V{})",
+        rule_number, protocol_version
+    )]
+    /// Calling a rule too recent
+    RuleTooRecent {
+        /// Rule number
+        rule_number: RuleNumber,
+        /// Protocole version
+        protocol_version: ProtocolVersion,
+    },
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::rule::*;
+    use super::*;
+    use maplit::btreemap;
+
+    #[derive(Debug)]
+    struct Datas {
+        i: usize,
+    }
+
+    #[derive(Debug, Eq, Fail, PartialEq)]
+    #[fail(display = "")]
+    struct Error {}
+
+    fn r2_v1(datas: &mut Datas) -> Result<(), Error> {
+        if datas.i == 0 {
+            datas.i += 1;
+            Ok(())
+        } else {
+            Err(Error {})
+        }
+    }
+
+    fn r3_v2(datas: &Datas) -> Result<(), Error> {
+        if datas.i == 1 {
+            Ok(())
+        } else {
+            Err(Error {})
+        }
+    }
+
+    fn get_test_engine() -> RulesEngine<Datas, Error> {
+        let all_rules: BTreeMap<RuleNumber, Rule<Datas, Error>> = btreemap![
+            RuleNumber(2) => Rule::new(RuleNumber(2), btreemap![
+                ProtocolVersion(1) => RuleFn::RefMut(r2_v1),
+            ]).expect("Fail to create rule n°2"),
+            RuleNumber(3) => Rule::new(RuleNumber(3), btreemap![
+                ProtocolVersion(2) => RuleFn::Ref(r3_v2),
+            ]).expect("Fail to create rule n°2"),
+        ];
+
+        RulesEngine::new(all_rules)
+    }
+
+    #[test]
+    fn rule_without_impl() {
+        if let Err(err) = Rule::<Datas, Error>::new(RuleNumber(1), btreemap![]) {
+            assert_eq!(
+                RuleWithoutImpl {
+                    rule_number: RuleNumber(1),
+                },
+                err,
+            )
+        } else {
+            panic!("Rule creation must be fail")
+        }
+
+        println!("{}", ProtocolVersion(1));
+        println!("{}", RuleNumber(1));
+    }
+
+    #[test]
+    fn protocol_empty() -> Result<(), EngineError<Error>> {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol_empty: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => Vec::<usize>::with_capacity(0).into()
+        ]);
+
+        engine.apply_protocol(protocol_empty, ProtocolVersion(1), &mut datas)
+    }
+
+    #[test]
+    fn protocol_version_not_exist() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol_empty: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => Vec::<usize>::with_capacity(0).into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::ProtocolVersionNotExist {
+                protocol_version: ProtocolVersion(2),
+            }),
+            engine.apply_protocol(protocol_empty, ProtocolVersion(2), &mut datas)
+        )
+    }
+
+    #[test]
+    fn rule_not_exist() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => vec![1usize].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleNotExist {
+                rule_number: RuleNumber(1),
+                protocol_version: ProtocolVersion(1)
+            }),
+            engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
+        );
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol_par: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => vec![RulesGroup::pr(vec![1usize])].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleNotExist {
+                rule_number: RuleNumber(1),
+                protocol_version: ProtocolVersion(1)
+            }),
+            engine.apply_protocol(protocol_par, ProtocolVersion(1), &mut datas)
+        );
+    }
+
+    #[test]
+    fn rule_fail() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 1 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => vec![2usize].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleError(RuleError {
+                rule_number: RuleNumber(2),
+                cause: Error {},
+            })),
+            engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
+        )
+    }
+
+    #[test]
+    fn par_rule_fail() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(2) => vec![RulesGroup::pr(vec![3usize])].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleError(RuleError {
+                rule_number: RuleNumber(3),
+                cause: Error {},
+            })),
+            engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
+        )
+    }
+
+    #[test]
+    fn rule_too_recent() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => vec![2usize, 3].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleTooRecent {
+                protocol_version: ProtocolVersion(1),
+                rule_number: RuleNumber(3),
+            }),
+            engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
+        )
+    }
+
+    #[test]
+    fn par_rule_too_recent() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(1) => vec![RulesGroup::pr(vec![3])].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::RuleTooRecent {
+                protocol_version: ProtocolVersion(1),
+                rule_number: RuleNumber(3),
+            }),
+            engine.apply_protocol(protocol, ProtocolVersion(1), &mut datas)
+        )
+    }
+
+    #[test]
+    fn mut_rule_in_par_protocol() {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 1 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(2) => vec![RulesGroup::pr(vec![2usize, 3])].into()
+        ]);
+
+        assert_eq!(
+            Err(EngineError::MutRuleInPar {
+                protocol_version: ProtocolVersion(2),
+                rule_number: RuleNumber(2),
+            }),
+            engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
+        )
+    }
+
+    #[test]
+    fn protocol_success() -> Result<(), EngineError<Error>> {
+        let engine = get_test_engine();
+
+        let mut datas = Datas { i: 0 };
+
+        let protocol: Protocol = Protocol::new(btreemap![
+            ProtocolVersion(2) => vec![2usize, 3].into()
+        ]);
+
+        engine.apply_protocol(protocol, ProtocolVersion(2), &mut datas)
+    }
+}
diff --git a/lib/tools/rules-engine/src/rule.rs b/lib/tools/rules-engine/src/rule.rs
new file mode 100644
index 0000000000000000000000000000000000000000..62dfd220f3ad79c159a707da72c3de51fe84267f
--- /dev/null
+++ b/lib/tools/rules-engine/src/rule.rs
@@ -0,0 +1,138 @@
+//  Copyright (C) 2019  Éloïs SANCHEZ
+//
+// 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/>.
+
+//! Rules engine : rules
+
+use crate::{EngineError, ProtocolVersion};
+use failure::Fail;
+use std::collections::BTreeMap;
+use std::fmt::Debug;
+
+#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]
+/// Rule number
+pub struct RuleNumber(pub usize);
+
+impl std::fmt::Display for RuleNumber {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(f, "{}", self.0)
+    }
+}
+
+/// Rule error
+#[derive(Debug, Eq, Fail, PartialEq)]
+#[fail(display = "An error occurred with rule n°{} : {}", rule_number, cause)]
+pub struct RuleError<E: Eq + Fail + PartialEq> {
+    /// Rule number
+    pub rule_number: RuleNumber,
+    /// Cause of the error
+    pub cause: E,
+}
+
+/// Rule immutable execution function
+pub type RuleFnRef<D, E> = fn(&D) -> Result<(), E>;
+
+/// Rule mutable execution function
+pub type RuleFnRefMut<D, E> = fn(&mut D) -> Result<(), E>;
+
+/// Rule execution function
+pub enum RuleFn<D, E> {
+    Ref(RuleFnRef<D, E>),
+    RefMut(RuleFnRefMut<D, E>),
+}
+
+#[derive(Debug, Copy, Clone, Eq, Fail, PartialEq)]
+#[fail(
+    display = "Fatal error: rules-engine: try to create rule n°{} without implementation !",
+    rule_number
+)]
+pub struct RuleWithoutImpl {
+    pub rule_number: RuleNumber,
+}
+
+/// Rule
+pub struct Rule<D: Debug, E: Eq + Fail + PartialEq> {
+    /// Dictionary of the different versions of the rule execution function
+    rule_versions: BTreeMap<ProtocolVersion, RuleFn<D, E>>,
+}
+
+impl<D: Debug, E: Eq + Fail + PartialEq> Rule<D, E> {
+    /// Create new rule
+    pub fn new(
+        rule_number: RuleNumber,
+        rule_versions: BTreeMap<ProtocolVersion, RuleFn<D, E>>,
+    ) -> Result<Self, RuleWithoutImpl> {
+        if rule_versions.is_empty() {
+            Err(RuleWithoutImpl { rule_number })
+        } else {
+            Ok(Rule { rule_versions })
+        }
+    }
+    /// Executes the correct version of the rule
+    pub fn execute(
+        &self,
+        protocol_version: ProtocolVersion,
+        rule_number: RuleNumber,
+        rule_datas: &D,
+    ) -> Result<(), EngineError<E>> {
+        let rule_opt: Option<(&ProtocolVersion, &RuleFn<D, E>)> =
+            self.rule_versions.range(..=protocol_version).last();
+        if let Some((_, rule_fn)) = rule_opt {
+            match rule_fn {
+                RuleFn::Ref(rule_fn_ref) => rule_fn_ref(rule_datas).map_err(|err| {
+                    EngineError::RuleError(RuleError {
+                        rule_number,
+                        cause: err,
+                    })
+                }),
+                RuleFn::RefMut(_) => Err(EngineError::MutRuleInPar {
+                    rule_number,
+                    protocol_version,
+                }),
+            }
+        } else {
+            Err(EngineError::RuleTooRecent {
+                rule_number,
+                protocol_version,
+            })
+        }
+    }
+    /// Executes the correct version of the rule
+    pub fn execute_mut(
+        &self,
+        protocol_version: ProtocolVersion,
+        rule_number: RuleNumber,
+        rule_datas: &mut D,
+    ) -> Result<(), EngineError<E>> {
+        let rule_opt: Option<(&ProtocolVersion, &RuleFn<D, E>)> =
+            self.rule_versions.range(..=protocol_version).last();
+        if let Some((_, rule_fn)) = rule_opt {
+            match rule_fn {
+                RuleFn::Ref(rule_fn_ref) => rule_fn_ref(rule_datas),
+                RuleFn::RefMut(rule_fn_ref_mut) => rule_fn_ref_mut(rule_datas),
+            }
+            .map_err(|err| {
+                EngineError::RuleError(RuleError {
+                    rule_number,
+                    cause: err,
+                })
+            })
+        } else {
+            Err(EngineError::RuleTooRecent {
+                rule_number,
+                protocol_version,
+            })
+        }
+    }
+}