diff --git a/.gitignore b/.gitignore
index d5a9aeb477d2e00b007fa30c3101803ecb83debc..6f70c11376c37ee4ff8d9b888e9e2b48b1a75fec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,9 +26,6 @@ coverage
 # Dependency directories
 node_modules/
 
-# Build files 
-dist/
-
 # Optional npm cache directory
 .npm
 
diff --git a/dist/adapter.esm.js b/dist/adapter.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..a7e1c8d81f13ccd323f1c83d321d9141ffbd5036
--- /dev/null
+++ b/dist/adapter.esm.js
@@ -0,0 +1,167 @@
+import Validator from 'swagger-model-validator';
+import axios from 'axios';
+
+/**
+ * Handle authentication and send/receive with the backbone
+ */
+class Adapter {
+  constructor(protocol, config) {
+    this.config = config;
+    this.protocol = protocol;
+    this.axios = axios;
+    this.validator = new Validator(protocol.schema);
+    this.auth();
+  }
+
+  /**
+   * Test parsing the message based on the provided protocol schema
+   * 
+   * The message must be successfully json decoded into an object 
+   * prior to validation
+   * 
+   * At present this returns the validation result which is an 
+   * object containing a boolean valid field as well as details 
+   * of any errors. 
+   * @param {object} message 
+   * @returns {object}
+   */
+  validate(message) {
+    return this.validator.validate(
+      message,
+      this.protocol.schema.definitions.Message
+    );
+  }
+
+  /**
+   * Ensure the token in this.credentials is still valid
+   * @returns {boolean}
+   */
+  tokenValid() {
+    let valid = false;
+    if (this.credentials) {
+      const now = new Date().toISOString();
+      valid = this.credentials.expiry > now;
+    }
+    return valid;
+  }
+
+  /**
+   * Return an http headers object containing a bearer token authorisation header
+   * @returns {object}
+   */
+  getAuthorizationHeader() {
+    if (!this.tokenValid())
+      return this.auth().then((response) => {
+        return {
+          Authorization: `Bearer ${this.credentials.token}`,
+        };
+      });
+    else {
+      return Promise.resolve({
+        Authorization: `Bearer ${this.credentials.token}`,
+      });
+    }
+  }
+
+  /**
+   * Do a client credentials grant and store in this.credentials
+   * @returns {object}
+   */
+  auth() {
+    return this.axios
+      .get(`{this.config.api}/token`, {
+        params: {
+          client_id: this.config.client_id,
+          secret: this.config.secret,
+        },
+      })
+      .then((response) => {
+        this.credentials = response.data;
+        return response;
+      });
+  }
+
+  /**
+   * Call the GET /receive endpoint and process the messages with decode
+   * 
+   * Returns the response  
+   * @returns {object}
+   */
+  poll() {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.get(`{this.config.api}/receive`, {
+          headers,
+        });
+      })
+      .then((response) => {
+        response.data.forEach((message) => {
+          const parsed = JSON.parse(message.message);
+          const validation = this.validate(parsed);
+          if (validation.valid) {
+            const type = this.protocol.getType(parsed);
+            this.protocol.decode(type, parsed);
+          } 
+        });
+        return response;
+      });
+  }
+
+  /**
+   * Publish a message to the backbone with the specified topic 
+   * 
+   * Messages should be passed through encode before sending
+   * @param {string} topic 
+   * @param {string} body 
+   * @returns 
+   */
+  publish(topic, body) {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.post(
+          `{this.config.api}/send`,
+          {
+            topic,
+            body,
+          },
+          {
+            headers,
+          }
+        );
+      })
+      .then((response) => {
+        return response;
+      });
+  }
+
+  /**
+   * Broadcast the message on the backbone
+   * 
+   * Broadcast messages bypass the normal publisher queues 
+   * this means they can be used to deliver messages more 
+   * quickly in an emergency scenario.
+   * 
+   * Messages should be passed through encode before sending
+   * @param {*} body 
+   * @returns 
+   */
+  broadcast(body) {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.post(
+          `{this.config.api}/notify`,
+          {
+            body,
+          },
+          {
+            headers,
+          }
+        );
+      })
+      .then((response) => {
+        return response;
+      });
+  }
+}
+
+export { Adapter };
diff --git a/dist/adapter.js b/dist/adapter.js
new file mode 100644
index 0000000000000000000000000000000000000000..031bbba0e742db6d0170ccddab409ef82dde28de
--- /dev/null
+++ b/dist/adapter.js
@@ -0,0 +1,169 @@
+'use strict';
+
+var Validator = require('swagger-model-validator');
+var axios = require('axios');
+
+/**
+ * Handle authentication and send/receive with the backbone
+ */
+class Adapter {
+  constructor(protocol, config) {
+    this.config = config;
+    this.protocol = protocol;
+    this.axios = axios;
+    this.validator = new Validator(protocol.schema);
+    this.auth();
+  }
+
+  /**
+   * Test parsing the message based on the provided protocol schema
+   * 
+   * The message must be successfully json decoded into an object 
+   * prior to validation
+   * 
+   * At present this returns the validation result which is an 
+   * object containing a boolean valid field as well as details 
+   * of any errors. 
+   * @param {object} message 
+   * @returns {object}
+   */
+  validate(message) {
+    return this.validator.validate(
+      message,
+      this.protocol.schema.definitions.Message
+    );
+  }
+
+  /**
+   * Ensure the token in this.credentials is still valid
+   * @returns {boolean}
+   */
+  tokenValid() {
+    let valid = false;
+    if (this.credentials) {
+      const now = new Date().toISOString();
+      valid = this.credentials.expiry > now;
+    }
+    return valid;
+  }
+
+  /**
+   * Return an http headers object containing a bearer token authorisation header
+   * @returns {object}
+   */
+  getAuthorizationHeader() {
+    if (!this.tokenValid())
+      return this.auth().then((response) => {
+        return {
+          Authorization: `Bearer ${this.credentials.token}`,
+        };
+      });
+    else {
+      return Promise.resolve({
+        Authorization: `Bearer ${this.credentials.token}`,
+      });
+    }
+  }
+
+  /**
+   * Do a client credentials grant and store in this.credentials
+   * @returns {object}
+   */
+  auth() {
+    return this.axios
+      .get(`{this.config.api}/token`, {
+        params: {
+          client_id: this.config.client_id,
+          secret: this.config.secret,
+        },
+      })
+      .then((response) => {
+        this.credentials = response.data;
+        return response;
+      });
+  }
+
+  /**
+   * Call the GET /receive endpoint and process the messages with decode
+   * 
+   * Returns the response  
+   * @returns {object}
+   */
+  poll() {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.get(`{this.config.api}/receive`, {
+          headers,
+        });
+      })
+      .then((response) => {
+        response.data.forEach((message) => {
+          const parsed = JSON.parse(message.message);
+          const validation = this.validate(parsed);
+          if (validation.valid) {
+            const type = this.protocol.getType(parsed);
+            this.protocol.decode(type, parsed);
+          } 
+        });
+        return response;
+      });
+  }
+
+  /**
+   * Publish a message to the backbone with the specified topic 
+   * 
+   * Messages should be passed through encode before sending
+   * @param {string} topic 
+   * @param {string} body 
+   * @returns 
+   */
+  publish(topic, body) {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.post(
+          `{this.config.api}/send`,
+          {
+            topic,
+            body,
+          },
+          {
+            headers,
+          }
+        );
+      })
+      .then((response) => {
+        return response;
+      });
+  }
+
+  /**
+   * Broadcast the message on the backbone
+   * 
+   * Broadcast messages bypass the normal publisher queues 
+   * this means they can be used to deliver messages more 
+   * quickly in an emergency scenario.
+   * 
+   * Messages should be passed through encode before sending
+   * @param {*} body 
+   * @returns 
+   */
+  broadcast(body) {
+    return this.getAuthorizationHeader()
+      .then((headers) => {
+        return this.axios.post(
+          `{this.config.api}/notify`,
+          {
+            body,
+          },
+          {
+            headers,
+          }
+        );
+      })
+      .then((response) => {
+        return response;
+      });
+  }
+}
+
+exports.Adapter = Adapter;
diff --git a/dist/protocol.esm.js b/dist/protocol.esm.js
new file mode 100644
index 0000000000000000000000000000000000000000..bd50069d95117a2d8ff08f85b37dc4f8c878eef3
--- /dev/null
+++ b/dist/protocol.esm.js
@@ -0,0 +1,71 @@
+import Validator from 'swagger-model-validator';
+
+/**
+ * GenericProtocol defines a simple passthru handler for messages
+ * This can be extended to handle different schemas 
+ * 
+ * The assunption is that all messages conform to a single 
+ * wrapper schema definition. Different payloads are handled 
+ * by a oneOf definitions within the schema determined by a 
+ * field value. 
+ * 
+ * This class can be extended and overridden to handle 
+ * different schemas and to implement the client specific 
+ * logic related to be executed when invoked for a given 
+ * message type. 
+ * 
+ * By default encode and decode are just passthru stubs 
+ * 
+ * decode is invoked when receiving a message from the backbone
+ * encode is invoked before delivering a message to the backbone 
+ * 
+ * The intention is that these allow you to transform the message
+ * and call invoke internal functions and services as required  
+ */
+class GenericProtocol {
+  constructor(schema, services) {
+    this.schema = schema;
+    this.validator = new Validator(schema);
+    this.services = services;
+  }
+
+  /**
+   * Validate that a message meets the reqiured schema 
+   * @param {object} message 
+   * @returns {object}
+   */
+  validate(message) {
+    return this.validator.validate(message, this.schema.definitions.Message);
+  }
+
+  /**
+   * Identify the payload type from the message content
+   * @param {object} message 
+   * @returns {string}
+   */
+  getType(message) {
+    return message.message_type;
+  }
+
+  /**
+   * Invoked on receiving a message from the backbone 
+   * @param {string} type 
+   * @param {object} message 
+   * @returns {*}
+   */
+  decode(type, message) {
+    return message;
+  }
+
+  /**
+   * Optionally invoked before delivering a message to the backbone
+   * @param {string} type 
+   * @param {*} message 
+   * @returns {object}
+   */
+  encode(type, message) {
+    return message;
+  }
+}
+
+export { GenericProtocol };
diff --git a/dist/protocol.js b/dist/protocol.js
new file mode 100644
index 0000000000000000000000000000000000000000..4ced40e9adc71012668693a495fee38e2cf30047
--- /dev/null
+++ b/dist/protocol.js
@@ -0,0 +1,73 @@
+'use strict';
+
+var Validator = require('swagger-model-validator');
+
+/**
+ * GenericProtocol defines a simple passthru handler for messages
+ * This can be extended to handle different schemas 
+ * 
+ * The assunption is that all messages conform to a single 
+ * wrapper schema definition. Different payloads are handled 
+ * by a oneOf definitions within the schema determined by a 
+ * field value. 
+ * 
+ * This class can be extended and overridden to handle 
+ * different schemas and to implement the client specific 
+ * logic related to be executed when invoked for a given 
+ * message type. 
+ * 
+ * By default encode and decode are just passthru stubs 
+ * 
+ * decode is invoked when receiving a message from the backbone
+ * encode is invoked before delivering a message to the backbone 
+ * 
+ * The intention is that these allow you to transform the message
+ * and call invoke internal functions and services as required  
+ */
+class GenericProtocol {
+  constructor(schema, services) {
+    this.schema = schema;
+    this.validator = new Validator(schema);
+    this.services = services;
+  }
+
+  /**
+   * Validate that a message meets the reqiured schema 
+   * @param {object} message 
+   * @returns {object}
+   */
+  validate(message) {
+    return this.validator.validate(message, this.schema.definitions.Message);
+  }
+
+  /**
+   * Identify the payload type from the message content
+   * @param {object} message 
+   * @returns {string}
+   */
+  getType(message) {
+    return message.message_type;
+  }
+
+  /**
+   * Invoked on receiving a message from the backbone 
+   * @param {string} type 
+   * @param {object} message 
+   * @returns {*}
+   */
+  decode(type, message) {
+    return message;
+  }
+
+  /**
+   * Optionally invoked before delivering a message to the backbone
+   * @param {string} type 
+   * @param {*} message 
+   * @returns {object}
+   */
+  encode(type, message) {
+    return message;
+  }
+}
+
+exports.GenericProtocol = GenericProtocol;
diff --git a/package.json b/package.json
index aca596b87c2fd37097f0e8d1903673608fe874cf..454524210b54d6149a624640f705954574809c55 100644
--- a/package.json
+++ b/package.json
@@ -24,8 +24,7 @@
     "lintfix": "prettier --write --list-different . && yarn lint:js --fix",
     "prepare": "husky install",
     "test": "jest",
-    "build": "cross-env NODE_ENV=production rollup -c",
-    "install": "yarn build"
+    "build": "cross-env NODE_ENV=production rollup -c"
   },
   "lint-staged": {
     "*.{js,vue}": "eslint --cache",
diff --git a/src/adapter/index.js b/src/adapter/index.js
index 33b6849c47f0d7874b068775365d0649001d62aa..6b449f3b5d6e3274e9eb947db660e1b9900da008 100644
--- a/src/adapter/index.js
+++ b/src/adapter/index.js
@@ -5,8 +5,8 @@ import axios from 'axios';
  * Handle authentication and send/receive with the backbone
  */
 export class Adapter {
-  constructor(protocol, soarConfig) {
-    this.apiRoot = soarConfig.api;
+  constructor(protocol, config) {
+    this.config = config;
     this.protocol = protocol;
     this.axios = axios;
     this.validator = new Validator(protocol.schema);
@@ -69,10 +69,10 @@ export class Adapter {
    */
   auth() {
     return this.axios
-      .get(`{this.apiRoot}/token`, {
+      .get(`{this.config.api}/token`, {
         params: {
-          client_id: soarConfig.client_id,
-          secret: soarConfig.secret,
+          client_id: this.config.client_id,
+          secret: this.config.secret,
         },
       })
       .then((response) => {
@@ -90,7 +90,7 @@ export class Adapter {
   poll() {
     return this.getAuthorizationHeader()
       .then((headers) => {
-        return this.axios.get(`{this.apiRoot}/receive`, {
+        return this.axios.get(`{this.config.api}/receive`, {
           headers,
         });
       })
@@ -119,7 +119,7 @@ export class Adapter {
     return this.getAuthorizationHeader()
       .then((headers) => {
         return this.axios.post(
-          `{this.apiRoot}/send`,
+          `{this.config.api}/send`,
           {
             topic,
             body,
@@ -149,7 +149,7 @@ export class Adapter {
     return this.getAuthorizationHeader()
       .then((headers) => {
         return this.axios.post(
-          `{this.apiRoot}/notify`,
+          `{this.config.api}/notify`,
           {
             body,
           },