diff --git a/app/modules/plugin.js b/app/modules/plugin.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c6708376332f86e952db8128ef76b77eb6cdedf
--- /dev/null
+++ b/app/modules/plugin.js
@@ -0,0 +1,129 @@
+"use strict";
+
+const co = require('co');
+const fs = require('fs');
+const path = require('path');
+const spawn = require('child_process').spawn;
+
+module.exports = {
+  duniter: {
+
+    methods: {
+      canWrite: getNPMAccess
+    },
+
+    cli: [{
+      name: 'plug [what]',
+      desc: 'Plugs in a duniter module to this Duniter codebase, making it available for the node.',
+      logs: false,
+      onDatabaseExecute: (server, conf, program, params) => co(function*() {
+        const what = params[0];
+        try {
+          console.log('Trying to install module "%s"...', what)
+          yield checkNPMAccess()
+          yield npmInstall(what)
+          console.log('Module successfully installed.')
+        } catch (err) {
+          console.error('Error during installation of the plugin:', err);
+        }
+        // Close the DB connection properly
+        return server && server.disconnect()
+      })
+    }, {
+      name: 'unplug [what]',
+      desc: 'Plugs in a duniter module to this Duniter codebase, making it available for the node.',
+      logs: false,
+      onDatabaseExecute: (server, conf, program, params) => co(function*() {
+        const what = params[0];
+        try {
+          console.log('Trying to remove module "%s"...', what)
+          yield checkNPMAccess()
+          yield npmRemove(what)
+          console.log('Module successfully uninstalled.')
+        } catch (err) {
+          console.error('Error during installation of the plugin:', err);
+        }
+        // Close the DB connection properly
+        return server && server.disconnect()
+      })
+    }]
+  }
+}
+
+function npmInstall(what) {
+  return new Promise((res, rej) => {
+    const npm = getNPM()
+    const cwd = getCWD()
+    const install = spawn(npm, ['i', '--save', what], { cwd })
+
+    install.stdout.pipe(process.stdout)
+    install.stderr.pipe(process.stderr)
+
+    install.stderr.on('data', (data) => {
+      if (data.toString().match(/ERR!/)) {
+        setTimeout(() => {
+          install.kill('SIGINT')
+        }, 100)
+      }
+    });
+
+    install.on('close', (code) => {
+      if (code === null || code > 0) {
+        return rej('could not retrieve or install the plugin')
+      }
+      res()
+    });
+  })
+}
+
+
+function npmRemove(what) {
+  return new Promise((res, rej) => {
+    const npm = getNPM()
+    const cwd = getCWD()
+    const uninstall = spawn(npm, ['remove', '--save', what], { cwd })
+
+    uninstall.stdout.pipe(process.stdout)
+    uninstall.stderr.pipe(process.stderr)
+
+    uninstall.stderr.on('data', (data) => {
+      if (data.toString().match(/ERR!/)) {
+        setTimeout(() => {
+          uninstall.kill('SIGINT')
+        }, 100)
+      }
+    });
+
+    uninstall.on('close', (code) => {
+      if (code === null || code > 0) {
+        return rej('error during the uninstallation of the plugin')
+      }
+      res()
+    });
+  })
+}
+
+function getNPM() {
+  return process.argv[0].replace(/node$/, 'npm')
+}
+
+function getCWD() {
+  return process.argv[1].replace(/bin\/duniter$/, '')
+}
+
+function checkNPMAccess() {
+  return co(function*() {
+    const hasReadWriteAccess = yield getNPMAccess()
+    if (!hasReadWriteAccess) {
+      throw 'no write access on disk'
+    }
+  })
+}
+
+function getNPMAccess() {
+  return new Promise((res) => {
+    fs.access(path.join(__dirname, '/../../package.json'), fs.constants.R_OK | fs.constants.W_OK, (err) => {
+      res(!err)
+    })
+  })
+}
diff --git a/index.js b/index.js
index d880837da0cf998eb325df819e9fae540e90235c..f66df9097cf5623458f78e28327db5a26963a3d2 100644
--- a/index.js
+++ b/index.js
@@ -21,6 +21,7 @@ const revertDependency    = require('./app/modules/revert');
 const daemonDependency    = require('./app/modules/daemon');
 const pSignalDependency   = require('./app/modules/peersignal');
 const routerDependency    = require('./app/modules/router');
+const pluginDependency    = require('./app/modules/plugin');
 
 const MINIMAL_DEPENDENCIES = [
   { name: 'duniter-config',    required: configDependency }
@@ -35,7 +36,8 @@ const DEFAULT_DEPENDENCIES = MINIMAL_DEPENDENCIES.concat([
   { name: 'duniter-revert',    required: revertDependency },
   { name: 'duniter-daemon',    required: daemonDependency },
   { name: 'duniter-psignal',   required: pSignalDependency },
-  { name: 'duniter-router',    required: routerDependency }
+  { name: 'duniter-router',    required: routerDependency },
+  { name: 'duniter-plugin',    required: pluginDependency }
 ]);
 
 const PRODUCTION_DEPENDENCIES = DEFAULT_DEPENDENCIES.concat([
@@ -69,15 +71,17 @@ module.exports.statics = {
     // Look for compliant packages
     const prodDeps = Object.keys(pjson.dependencies);
     const devDeps = Object.keys(pjson.devDependencies);
-    const duniterDeps = _.filter(prodDeps.concat(devDeps), (dep) => dep.match(/^duniter-/));
+    const duniterDeps = prodDeps.concat(devDeps)
     for(const dep of duniterDeps) {
-      const required = require(dep);
-      if (required.duniter) {
-        duniterModules.push({
-          name: dep,
-          required
-        });
-      }
+      try {
+        const required = require(dep);
+        if (required.duniter) {
+          duniterModules.push({
+            name: dep,
+            required
+          });
+        }
+      } catch (e) { /* Silent errors for packages that fail to load */ }
     }
 
     // The final stack