diff --git a/.gitignore b/.gitignore
index 0b3cb79fd9fca0b6513ffc7fa9100a6c7f6b0a50..b8629cee13886923757d31c1aad1554a0f3af992 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,7 @@ native/artifacts.json
 **/*~
 **/node_modules
 **/.DS_Store
+
+*.bk
+
+*.wot
diff --git a/lib/index.js b/lib/index.js
index ab3e2715bb150865b1f8e276e66cc6cd2e9f5da6..6de2283e675f720373fd0c066458c04924f7d237 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,3 +1,20 @@
-var addon = require('../native');
+let WebOfTrust = require('../native').WebOfTrust;
 
-console.log(addon.hello());
+{
+    let wot = new WebOfTrust(3);
+    console.log(wot.getMaxCert())
+    wot.setMaxCert(4);
+    console.log(wot.getMaxCert())
+    console.log(wot.addNode());
+    console.log(wot.getWoTSize())
+}
+
+console.log("-----")
+
+{
+    let wot = new WebOfTrust("hey.wot");
+    console.log(wot.getMaxCert())
+    console.log(wot.addNode());
+    console.log(wot.getWoTSize());
+    console.log(wot.toFile("hey.wot"));
+}
diff --git a/native/src/lib.rs b/native/src/lib.rs
index 5b9929c08d926d106f800af00861c090731a3cdb..a09a07bb7689f653cf0f16c0efbc68255cee3d1e 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -1,14 +1,280 @@
+//! `duniter-rs-wotb-js` is a crate providing Javascript bindings of `duniter-rs-wotb`.
+
 #[macro_use]
 extern crate neon;
+extern crate duniter_rs_wotb;
+
+use neon::js::{Value, JsInteger, JsString, JsBoolean, JsFunction, JsArray, JsNumber, JsObject, Object};
+use neon::js::class::{Class, JsClass};
+use neon::mem::Handle;
+use neon::vm::{Throw, Lock};
+
+use duniter_rs_wotb::{WebOfTrust, NodeId, NewLinkResult, RemovedLinkResult};
+
+
+declare_types! {
+    /// JS class wrapping WebOfTrust struct.
+    pub class JsWebOfTrust for WebOfTrust {
+        init(call) {
+            let scope = call.scope;
+            let arg0 = try!(call.arguments.require(scope, 0));
+
+            if let Some(max_cert) = arg0.downcast::<JsInteger>() {
+                let max_cert = max_cert.value();
+
+                match max_cert > 0 {
+                    true => Ok(WebOfTrust::new(max_cert as usize)),
+                    false => Err(Throw),
+                }
+            } else if let Some(path) = arg0.downcast::<JsString>() {
+                let path = path.value();
+
+                match WebOfTrust::from_file(path.as_str()) {
+                    Some(wot) => Ok(wot),
+                    None => Err(Throw),
+                }
+            } else {
+                Err(Throw)
+            }
+        }
+
+        method toFile(call) {
+            let scope = call.scope;
+            let path = try!(try!(call.arguments.require(scope, 0)).to_string(scope)).value();
+            
+            let result = call.arguments.this(scope).grab(|wot| {
+                wot.to_file(path.as_str())
+            });
+
+            Ok(JsBoolean::new(scope, result).upcast())
+        }
+
+        method addNode(call) {
+            let scope = call.scope;
+            
+            let id = call.arguments.this(scope).grab(|wot| {
+                wot.add_node()
+            });
+
+            Ok(JsInteger::new(scope, *id as i32).upcast())
+        }
+
+        method removeNode(call) {
+            let scope = call.scope;
+
+            let id = call.arguments.this(scope).grab(|wot| {
+                match wot.remove_node() {
+                    Some(id) => *id as i32,
+                    None => -1,
+                }
+            });
+
+            Ok(JsInteger::new(scope, id).upcast())
+        }
+
+        method getSentries(call) {
+            let scope = call.scope;
+
+            let arg0 = try!(call.arguments.require(scope, 0));
+            let d_min = try!(arg0.check::<JsInteger>()).value() as usize;
+
+            let array = call.arguments.this(scope).grab(|wot| {
+                wot.get_sentries(d_min)
+            });
+
+            let jsarray: Handle<JsArray> = JsArray::new(scope, array.len() as u32);
+
+            for (index, &item) in array.iter().enumerate() {
+                try!(jsarray.set(index as u32, JsInteger::new(scope, *item as i32)));
+            }
+
+            Ok(jsarray.upcast())
+        }
+
+        method getNonSentries(call) {
+            let scope = call.scope;
+
+            let arg0 = try!(call.arguments.require(scope, 0));
+            let d_min = try!(arg0.check::<JsInteger>()).value() as usize;
+
+            let array = call.arguments.this(scope).grab(|wot| {
+                wot.get_non_sentries(d_min)
+            });
+
+            let jsarray: Handle<JsArray> = JsArray::new(scope, array.len() as u32);
+
+            for (index, &item) in array.iter().enumerate() {
+                try!(jsarray.set(index as u32, JsInteger::new(scope, *item as i32)));
+            }
+
+            Ok(jsarray.upcast())
+        }
+
+        method getDisabled(call) {
+            let scope = call.scope;
+
+            let array = call.arguments.this(scope).grab(|wot| {
+                wot.get_disabled()
+            });
+
+            let jsarray: Handle<JsArray> = JsArray::new(scope, array.len() as u32);
+
+            for (index, &item) in array.iter().enumerate() {
+                try!(jsarray.set(index as u32, JsInteger::new(scope, *item as i32)));
+            }
+
+            Ok(jsarray.upcast())
+        }
+
+        method getPaths(call) {
+            let scope = call.scope;
+
+            let from = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let to = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as usize;
+            let k_max = try!(try!(call.arguments.require(scope, 2)).check::<JsInteger>()).value() as u32;
+
+            let paths = call.arguments.this(scope).grab(|wot| {
+                wot.get_paths(NodeId(from), NodeId(to), k_max)
+            });
+
+            let jsarray: Handle<JsArray> = JsArray::new(scope, paths.len() as u32);
+
+            for(index, ref inner_array) in paths.iter().enumerate() {
+                let inner_jsarray: Handle<JsArray> = JsArray::new(scope, inner_array.len() as u32);
+
+                for(inner_index, &item) in inner_array.iter().enumerate() {
+                    try!(inner_jsarray.set(inner_index as u32, JsInteger::new(scope, *item as i32)));
+                }
+
+                try!(jsarray.set(index as u32, inner_jsarray));
+            }
+            
+            Ok(jsarray.upcast())
+        }
+
+        method getWoTSize(call) {
+            let scope = call.scope;
+
+            let size = call.arguments.this(scope).grab(|wot| {
+                wot.size()
+            });
+
+            Ok(JsInteger::new(scope, size as i32).upcast())
+        }
+
+        method isEnabled(call) {
+            let scope = call.scope;
+
+            let node = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+
+            match call.arguments.this(scope).grab(|wot| {
+                wot.is_enabled(NodeId(node)) 
+            }) {
+                Some(state) => Ok(JsBoolean::new(scope, state).upcast()),
+                None => Err(Throw),
+            }        
+        }
+
+        method setEnabled(call) {
+            let scope = call.scope;
+
+            let node = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let state = try!(try!(call.arguments.require(scope, 1)).check::<JsBoolean>()).value();
+
+            match call.arguments.this(scope).grab(|wot| {
+                wot.set_enabled(NodeId(node), state)
+            }) {
+                Some(state) => Ok(JsBoolean::new(scope, state).upcast()),
+                None => Err(Throw),
+            }  
+        }    
+
+        method addLink(call) {
+            let scope = call.scope;
+
+            let from = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let to = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as usize;
+
+            match call.arguments.this(scope).grab(|wot| {
+                wot.add_link(NodeId(from), NodeId(to))
+            }) {
+                NewLinkResult::Ok(count) |
+                NewLinkResult::AlreadyCertified(count) |
+                NewLinkResult::AllCertificationsUsed(count) => Ok(JsInteger::new(scope, count as i32).upcast()),
+                _ => Err(Throw),
+            } 
+        }    
+
+        method removeLink(call) {
+            let scope = call.scope;
+
+            let from = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let to = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as usize;
+
+            match call.arguments.this(scope).grab(|wot| {
+                wot.remove_link(NodeId(from), NodeId(to))
+            }) {
+                RemovedLinkResult::Removed(count) => Ok(JsInteger::new(scope, count as i32).upcast()),
+                _ => Err(Throw),
+            } 
+        }   
+
+        method existsLink(call) {
+            let scope = call.scope;
+
+            let from = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let to = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as usize;
+
+            let result = call.arguments.this(scope).grab(|wot| {
+                wot.exists_link(NodeId(from), NodeId(to))
+            });
+
+            Ok(JsBoolean::new(scope, result).upcast())
+        }   
+
+        method isOutdistanced(call) {
+            let scope = call.scope;
+
+            let node = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+            let d_min = try!(try!(call.arguments.require(scope, 1)).check::<JsInteger>()).value() as u32;
+            let d_max = try!(try!(call.arguments.require(scope, 2)).check::<JsInteger>()).value() as u32;
+            let x_percent = try!(try!(call.arguments.require(scope, 3)).check::<JsNumber>()).value() as f64;
+
+            match call.arguments.this(scope).grab(|wot| {
+                wot.is_outdistanced(NodeId(node), d_min, d_max, x_percent)
+            }) {
+                Some(result) => Ok(JsBoolean::new(scope, result).upcast()),
+                None => Err(Throw),
+            } 
+        }  
+
+        method setMaxCert(call) {
+            let scope = call.scope;
+
+            let max_cert = try!(try!(call.arguments.require(scope, 0)).check::<JsInteger>()).value() as usize;
+
+            call.arguments.this(scope).grab(|wot| {
+                wot.max_cert = max_cert;
+            });
+
+            Ok(JsObject::new(scope).upcast())
+        }
+
+        method getMaxCert(call) {
+            let scope = call.scope;
 
-use neon::vm::{Call, JsResult};
-use neon::js::JsString;
+            let max_cert = call.arguments.this(scope).grab(|wot| {
+                wot.max_cert
+            });
 
-fn hello(call: Call) -> JsResult<JsString> {
-    let scope = call.scope;
-    Ok(JsString::new(scope, "hello node").unwrap())
+            Ok(JsInteger::new(scope, max_cert as i32).upcast())
+        }
+    }
 }
 
 register_module!(m, {
-    m.export("hello", hello)
+    let class: Handle<JsClass<JsWebOfTrust>> = try!(JsWebOfTrust::class(m.scope));
+    let constructor: Handle<JsFunction<JsWebOfTrust>> = try!(class.constructor(m.scope));
+    try!(m.exports.set("WebOfTrust", constructor));
+    Ok(())
 });