From 913c12e7357d77aaaf5205839e915304467a9699 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 23 May 2018 12:16:32 -0400
Subject: [PATCH 01/62] Removed local registry from jdiscovery

---
 lib/jamserver/jamlib.js         |   2 +-
 lib/jdiscovery/jregistrar.js    |  35 +--
 lib/jdiscovery/localregistry.js | 411 --------------------------------
 3 files changed, 3 insertions(+), 445 deletions(-)
 delete mode 100644 lib/jdiscovery/localregistry.js

diff --git a/lib/jamserver/jamlib.js b/lib/jamserver/jamlib.js
index 51d89e98..c23c496b 100644
--- a/lib/jamserver/jamlib.js
+++ b/lib/jamserver/jamlib.js
@@ -25,7 +25,7 @@ var Registrar = require('jdiscovery');
 var machType = getMachineType(cmdopts);
 deviceParams.setItem('machType', machType);
 
-var reggie = new Registrar(cmdopts.app, machType, deviceParams.getItem('deviceId'), cmdopts.port, { protocols: { mqtt: true, mdns: true, localStorage: false } });
+var reggie = new Registrar(cmdopts.app, machType, deviceParams.getItem('deviceId'), cmdopts.port, { protocols: { mqtt: true, mdns: true } });
 
 var jamsys = require('./jamsys');
 jamsys.init(reggie, machType, cmdopts.tags, cmdopts.iflow, cmdopts.oflow, deviceParams.getItem('deviceId'));
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index af9b84c5..eed079f6 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -3,7 +3,6 @@ var EventEmitter = require('events'),
     constants = require('../jamserver/constants'),
     MQTTRegistry = require('./mqttregistry'),
     MDNSRegistry = require('./mdnsregistry'),
-    LocalRegistry = require('./localregistry'),
     os = require('os');
 
 //==============================================================================
@@ -27,8 +26,8 @@ function getIPv4Address() {
 
 //==============================================================================
 // Registrar Class
-// This class is the interface between the application and the MQTT, mDNS, and
-// local storage registries
+// This class is the interface between the application
+// and the MQTT, mDNS registries
 //==============================================================================
 
 function Registrar(app, machType, id, port, config) {
@@ -89,11 +88,6 @@ function Registrar(app, machType, id, port, config) {
         noProtocols = false;
     }
 
-    if (!config || !config.protocols || config.protocols.localStorage) {
-        this.localRegistry = new LocalRegistry(app, machType, id, port);
-        noProtocols = false;
-    }
-
     if (noProtocols) {
         throw new Error('a Registrar must use at least one protocol');
     }
@@ -214,16 +208,6 @@ function Registrar(app, machType, id, port, config) {
             self._respondToAttrRemovalEvent(self, attr, event, nodeId, constants.globals.Protocol.MDNS);
         });
     }
-
-    if (this.localRegistry) {
-        this.localRegistry.on('discovery', function(attr, event, nodeId, value, dedupeId) {
-            self._respondToDiscoveryEvent(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.LOCALSTORAGE);
-        });
-
-        this.localRegistry.on('attr-removed', function(attr, event, nodeId) {
-            self._respondToAttrRemovalEvent(self, attr, event, nodeId, constants.globals.Protocol.LOCALSTORAGE);
-        });
-    }
 }
 
 /* Registrar inherits from EventEmitter */
@@ -263,9 +247,6 @@ Registrar.prototype.registerAndDiscover = function(options) {
         if (this.mdnsRegistry) {
             this.mdnsRegistry.registerAndDiscover();
         }
-        if (this.localRegistry) {
-            this.localRegistry.registerAndDiscover();
-        }
         this.started = true;
     }
 }
@@ -304,9 +285,6 @@ Registrar.prototype.addAttributes = function(attrs, override) {
     if (this.mdnsRegistry) {
         this.mdnsRegistry.addAttributes(attrs, dedupeId);
     }
-    if (this.localRegistry) {
-        this.localRegistry.addAttributes(attrs, dedupeId);
-    }
 }
 
 Registrar.prototype.removeAttributes = function(attrs) {
@@ -319,9 +297,6 @@ Registrar.prototype.removeAttributes = function(attrs) {
     if (this.mdnsRegistry) {
         this.mdnsRegistry.removeAttributes(attrs);
     }
-    if (this.localRegistry) {
-        this.localRegistry.removeAttributes(attrs);
-    }
 }
 
 /**
@@ -344,9 +319,6 @@ Registrar.prototype.discoverAttributes = function(dattrs) {
     if (this.mdnsRegistry) {
         this.mdnsRegistry.discoverAttributes(dattrs);
     }
-    if (this.localRegistry) {
-        this.localRegistry.discoverAttributes(dattrs);
-    }
 }
 
 Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
@@ -357,9 +329,6 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
     if (this.mdnsRegistry) {
         this.mdnsRegistry.stopDiscoveringAttributes(dattrs);
     }
-    if (this.localRegistry) {
-        this.localRegistry.stopDiscoveringAttributes(dattrs);
-    }
 }
 
 //==============================================================================
diff --git a/lib/jdiscovery/localregistry.js b/lib/jdiscovery/localregistry.js
deleted file mode 100644
index 8c5fec71..00000000
--- a/lib/jdiscovery/localregistry.js
+++ /dev/null
@@ -1,411 +0,0 @@
-//==============================================================================
-// Registers a node locally (using local storage)
-//==============================================================================
-
-var LocalStorage = require('node-localstorage').LocalStorage,
-    lockFile = require('lockfile'),
-    constants = require('../jamserver/constants'),
-    logger = require('../jamserver/jerrlog'),
-    Registry = require('./registry'),
-    os = require('os');
-
-/* create an mDNS advertisement on the local network */
-
-function LocalRegistry(app, machType, id, port) {
-    Registry.call(this, app, machType, id, port);
-    this.localStorage = null;
-    this.binName = this._getBinName();
-    // put the 'app' as a hidden directory in user's home
-    this.appDir = os.homedir() + '/.' + app;
-    // the timestamp when we last scanned local storage for other nodes;
-    // set to zero to catch nodes that started before this node
-    this.lastScanAt = 0;
-    this.currentOfflineMachs = {};
-
-    /**
-     * Attributes to write to local storage the next time we check in. A map from
-     * attribute name to { payload: attribute_value, dedupeId: deduplication_id }
-     * objects.
-     */
-    this.attrsToAdd = {};
-    // attributes to remove from local storage the next time we check in
-    this.attrsToRemove = [];
-    // attributes to discover the next time we scan
-    this.attrsToDiscover = {
-        device: {},
-        fog: {},
-        cloud: {}
-    };
-
-    // the previous devices, fogs, and clouds we found in local storage
-    this.prevMachs = {};
-
-    // whether or not scanning and checkins have been started
-    this.started = false;
-}
-
-/* LocalRegistry inherits from Registry */
-LocalRegistry.prototype = Object.create(Registry.prototype);
-LocalRegistry.prototype.constructor = LocalRegistry;
-
-/**
- * API for local storage registration/discovery
- */
-LocalRegistry.prototype.registerAndDiscover = function() {
-    if (!this.started) {
-        // initialize the local storage
-        var self = this;
-        this._initLocalStorage(this, function() {
-            self.started = true;
-            self._kickStartCheckIns(self);
-            self._kickStartScanning(self);
-        });
-    }
-}
-
-LocalRegistry.prototype._initLocalStorage = function(self, cb) {
-    lockFile.lock(constants.localStorage.initLock, { stale: constants.localStorage.stale }, function (err) {
-        if (err) {
-            // failed to acquire lock, which means someone else already has it; wait until the node with the lock
-            // has finished initializing local storage
-            grabbedLock = false;
-            var tempLs;
-            while (true) {
-                tempLs = new LocalStorage(self.appDir);
-                if (tempLs.getItem('initialized')) {
-                    self.localStorage = tempLs;
-                    break;
-                }
-            }
-            self.emit('ls-initialized');
-            cb();
-            return;
-        }
-
-        // we've grabbed the lock
-        self.localStorage = new LocalStorage(self.appDir);
-        if (!self.localStorage.getItem('initialized')) {
-            // we need to perform the initialization
-            for (var i = 0; i < constants.localStorage.numBins; i++) {
-                self.localStorage.setItem('devices_' + i, '{}');
-                self.localStorage.setItem('fogs_' + i, '{}');
-                self.localStorage.setItem('clouds_' + i, '{}');
-            }
-            self.localStorage.setItem('initialized', 'true');
-        }
-        lockFile.unlockSync(constants.localStorage.initLock);
-        self.emit('ls-initialized');
-        cb();
-    });
-}
-
-/**
- * Register a node on local storage by having it write itself into local storage (fogs and clouds only)
- */
-LocalRegistry.prototype._kickStartCheckIns = function(self) {
-    // create an object to be written to local storage
-    var now = Date.now();
-    var data = {
-        lastCheckIn: now,
-        createdAt: now
-    };
-
-    // add attrs
-    for (var attr in self.attrsToAdd) {
-        if (self.attrsToAdd[attr].payload instanceof Function) {
-            data[attr] = {
-                payload: self.attrsToAdd[attr].payload(),
-                id: self.attrsToAdd[attr].dedupeId,
-                updatedAt: now
-            };
-        } else {
-            data[attr] = {
-                payload: self.attrsToAdd[attr].payload,
-                id: self.attrsToAdd[attr].dedupeId,
-                updatedAt: now
-            };
-        }
-    }
-    // reset attrsToAdd
-    self.attrsToAdd = {};
-
-    self._addNodeToLocalStorage(self, data, 1, function() {
-        // check in every so often to indicate that we're still here
-        setInterval(self._checkIn, constants.localStorage.checkInInterval, self, 1);
-    });
-}
-
-/**
- * Kick-start scanning
- */
-LocalRegistry.prototype._kickStartScanning = function(self) {
-    self._scan(self);
-    setInterval(self._scan, constants.localStorage.scanInterval, self);
-}
-
-/**
- * Adds a node's information to local storage
- */
-LocalRegistry.prototype._addNodeToLocalStorage = function(self, data, attemptNumber, cb) {
-    if (self.binName !== undefined) {
-        lockFile.lock(self.binName, { stale: constants.localStorage.stale }, function (err) {
-            if (err) {
-                setTimeout(self._addNodeToLocalStorage, self._getWaitTime(attemptNumber), self, data, attemptNumber + 1, cb);
-                return;
-            }
-            var nodes = JSON.parse(self.localStorage.getItem(self.binName));
-            nodes[self.id] = data;
-            self.localStorage.setItem(self.binName, JSON.stringify(nodes));
-            lockFile.unlockSync(self.binName);
-            cb();
-        });
-    }
-}
-
-/**
- * Update lastCheckIn field
- * Also, at this time, we update the attributes of the node
- */
-LocalRegistry.prototype._checkIn = function(self, attemptNumber) {
-    lockFile.lock(self.binName, { stale: constants.localStorage.stale }, function (err) {
-        if (err) {
-            setTimeout(self._checkIn, self._getWaitTime(attemptNumber), self, attemptNumber + 1);
-            return;
-        }
-        var now = Date.now();
-        var nodes = JSON.parse(self.localStorage.getItem(self.binName));
-        // update lastCheckIn field
-        nodes[self.id].lastCheckIn = now;
-        // update attributes
-        // remove any that need removing
-        for (var i = 0; i < self.attrsToRemove.length; i++) {
-            delete nodes[self.id][self.attrsToRemove[i]];
-        }
-        // reset attrsToRemove
-        self.attrsToRemove = [];
-        // add any that need adding
-        for (var attr in self.attrsToAdd) {
-            if (self.attrsToAdd[attr].payload instanceof Function) {
-                nodes[self.id][attr] = {
-                    payload: self.attrsToAdd[attr].payload(),
-                    id: self.attrsToAdd[attr].dedupeId,
-                    updatedAt: now
-                };
-            } else {
-                nodes[self.id][attr] = {
-                    payload: self.attrsToAdd[attr].payload,
-                    id: self.attrsToAdd[attr].dedupeId,
-                    updatedAt: now
-                };
-            }
-            delete self.attrsToAdd[attr];
-        }
-        self.localStorage.setItem(self.binName, JSON.stringify(nodes));
-        lockFile.unlockSync(self.binName);
-    });
-}
-
-/**
- * Scans local storage for other nodes
- */
-LocalRegistry.prototype._scan = function(self) {
-    var binName;
-    var baseName;
-    var currMachs = {};
-    var machs;
-
-    if (Object.keys(self.attrsToDiscover.device).length !== 0) {
-        baseName = 'devices_';
-        for (var i = 0; i < constants.localStorage.numBins; i++) {
-            binName = baseName + i;
-            machs = JSON.parse(self.localStorage.getItem(binName));
-            self._makeDiscoveries(self, machs, self.attrsToDiscover.device);
-            self._detectRemovedAttrs(self, machs, self.attrsToDiscover.device);
-            for (var key in machs) {
-                currMachs[key] = machs[key];
-            }
-        }
-    }
-
-    if (Object.keys(self.attrsToDiscover.fog).length !== 0) {
-        baseName = 'fogs_';
-        for (var i = 0; i < constants.localStorage.numBins; i++) {
-            binName = baseName + i;
-            machs = JSON.parse(self.localStorage.getItem(binName));
-            self._makeDiscoveries(self, machs, self.attrsToDiscover.fog);
-            self._detectRemovedAttrs(self, machs, self.attrsToDiscover.fog);
-            for (var key in machs) {
-                currMachs[key] = machs[key];
-            }
-        }
-    }
-
-    if (Object.keys(self.attrsToDiscover.cloud).length !== 0) {
-        baseName = 'clouds_';
-        for (var i = 0; i < constants.localStorage.numBins; i++) {
-            binName = baseName + i;
-            machs = JSON.parse(self.localStorage.getItem(binName));
-            self._makeDiscoveries(self, machs, self.attrsToDiscover.cloud);
-            self._detectRemovedAttrs(self, machs, self.attrsToDiscover.cloud);
-            for (var key in machs) {
-                currMachs[key] = machs[key];
-            }
-        }
-    }
-
-    self.prevMachs = currMachs;
-
-    // update when we last scanned
-    self.lastScanAt = Date.now();
-}
-
-LocalRegistry.prototype._makeDiscoveries = function(self, machs, dattrs) {
-    // only the machs that are newly online are of interest to us, unless we are interested in node status,
-    // in which case newly offline nodes are also of interest
-    var now = Date.now();
-    for (var machId in machs) {
-        for (var attr in dattrs) {
-            if (attr === 'status') {
-                // check if the node has gone offline
-                if ((now - machs[machId].lastCheckIn) > 2 * constants.localStorage.checkInInterval) {
-                    // if we haven't already noted that the machine is offline...
-                    if (!self.currentOfflineMachs[machId]) {
-                        self.currentOfflineMachs[machId] = true;
-                        // pass a dedupeId of zero for node down events
-                        self.emit('discovery', 'status', dattrs[attr].offline, machId, 'offline', 0);
-                    }
-                } else if (machs[machId].createdAt > self.lastScanAt) {
-                    // the node is newly online (or was online before the current node went online)
-                    self.emit('discovery', 'status', dattrs[attr].online, machId, machs[machId].status.payload, machs[machId].status.id);
-                    // in case we currently have this node recorded as offline
-                    delete self.currentOfflineMachs[machId];
-                }
-            } else {
-                if (machs[machId].hasOwnProperty(attr) && machs[machId][attr].updatedAt > self.lastScanAt) {
-                    self.emit('discovery', attr, dattrs[attr].onAdd, machId, machs[machId][attr].payload, machs[machId][attr].id);
-                }
-            }
-        }
-    }
-}
-
-LocalRegistry.prototype._detectRemovedAttrs = function(self, machs, dattrs) {
-    // for each attribute to discover, see if it was there the last time we scanned local storage but is gone now
-    for (var machId in machs) {
-        for (var attr in dattrs) {
-            if (self.prevMachs[machId] && self.prevMachs[machId][attr] && !machs[machId][attr]) {
-                self.emit('attr-removed', attr, dattrs[attr].onRemove, machId);
-            }
-        }
-    }
-}
-
-//==============================================================================
-// Add and discover attributes
-//==============================================================================
-
-/**
- * Add custom, discoverable attributes on the node
- */
-LocalRegistry.prototype.addAttributes = function(attrs, dedupeId) {
-    for (var attr in attrs) {
-        delete this.attrsToRemove[attr];
-        this.attrsToAdd[attr] = { payload: attrs[attr], dedupeId: dedupeId };
-    }
-}
-
-/**
- * Removes attrs, a list of attribute keys, from this node
- */
-LocalRegistry.prototype.removeAttributes = function(attrs) {
-    for (var i = 0; i < attrs.length; i++) {
-        delete this.attrsToAdd[attrs[i]];
-    }
-    if (this.started) {
-        this.attrsToRemove = this.attrsToRemove.concat(attrs);
-    }
-}
-
-/**
- * Discover other nodes with the given attributes
- * This function need only store the attributes on the node. The LocalRegistry will
- * look for other nodes with these attributes the next time it scans local storage.
- */
-LocalRegistry.prototype.discoverAttributes = function(dattrs) {
-    for (var key in dattrs.device) {
-        this.attrsToDiscover.device[key] = dattrs.device[key];
-    }
-
-    for (var key in dattrs.fog) {
-        this.attrsToDiscover.fog[key] = dattrs.fog[key];
-    }
-
-    for (var key in dattrs.cloud) {
-        this.attrsToDiscover.cloud[key] = dattrs.cloud[key];
-    }
-}
-
-/**
- * Stops discovering the specified attributes
- */
-LocalRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
-    if (dattrs.device) {
-        for (var i = 0; i < dattrs.device.length; i++) {
-            delete this.attrsToDiscover.device[dattrs.device[i]];
-        }
-    }
-
-    if (dattrs.fog) {
-        for (var i = 0; i < dattrs.fog.length; i++) {
-            delete this.attrsToDiscover.fog[dattrs.fog[i]];
-        }
-    }
-
-    if (dattrs.cloud) {
-        for (var i = 0; i < dattrs.cloud.length; i++) {
-            delete this.attrsToDiscover.cloud[dattrs.cloud[i]];
-        }
-    }
-}
-
-//==============================================================================
-// Helpers
-//==============================================================================
-
-LocalRegistry.prototype._getBinName = function() {
-    var binNumber = this._hash(this.id);
-    if (this.machType === constants.globals.NodeType.FOG) {
-        return 'fogs_' + binNumber;
-    } else if (this.machType === constants.globals.NodeType.CLOUD) {
-        return 'clouds_' + binNumber;
-    } else {
-        return 'devices_' + binNumber;
-    }
-}
-
-/**
- * Hash a uuid into an integer in the range 0 to constants.localStorage.numBins-1
- */
-LocalRegistry.prototype._hash = function(uuid) {
-    var hash = 0, i, chr;
-    if (uuid.length === 0) return hash;
-    for (i = 0; i < uuid.length; i++) {
-        chr = uuid.charCodeAt(i);
-        hash = ((hash << 5) - hash) + chr;
-        hash |= 0; // convert to a 32 bit integer
-    }
-    if (hash < 0) {
-        hash += 1;
-        hash *= -1;
-    }
-    return hash % constants.localStorage.numBins;
-}
-
-/**
- * Helper for computing wait time
- */
-LocalRegistry.prototype._getWaitTime = function(attemptNumber) {
-    return Math.ceil(Math.random() * Math.pow(2, attemptNumber));
-}
-
-module.exports = LocalRegistry;

From 4467640167fe5a4b223bc6d1463ac9430096b603 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 24 May 2018 12:14:55 -0400
Subject: [PATCH 02/62] Moved jdisc tester, dup elim true by default

---
 lib/jdiscovery/apps/tester.js | 161 ++++++++++++++++++++++++++++++++++
 lib/jdiscovery/jregistrar.js  |   2 +-
 2 files changed, 162 insertions(+), 1 deletion(-)
 create mode 100644 lib/jdiscovery/apps/tester.js

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
new file mode 100644
index 00000000..6792bd13
--- /dev/null
+++ b/lib/jdiscovery/apps/tester.js
@@ -0,0 +1,161 @@
+var Registrar = require('../jregistrar'),
+errLog = require('../../jamserver/jerrlog'),
+globals = require('../../jamserver/constants').globals,
+events = require('events'),
+Random = require('random-js');
+
+var random = new Random(Random.engines.mt19937().autoSeed());
+
+var machType = process.argv[2],
+phoneType = process.argv[3],
+phoneNumber = process.argv[4],
+app = 'tester',
+port = 42429,
+id = random.uuid4();
+
+// don't forget to initialize the logger!
+errLog.init(app, false);
+
+console.log('_______________________________________________');
+console.log(machType + ' id: ' + id);
+console.log('-----------------------------------------------');
+console.log();
+
+var reggie = new Registrar(app, machType, id, port);
+
+//------------------------------------------------------------------------------
+// Default discoveries
+//------------------------------------------------------------------------------
+
+if (machType === 'device') {
+reggie.on('fog-up', function(fogId, connInfo) {
+    console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+});
+
+reggie.on('fog-down', function(fogId) {
+    console.log('FOG DOWN: id: ' + fogId);
+});
+} else if (machType === 'fog') {
+reggie.on('cloud-up', function(cloudId, connInfo) {
+    console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+
+});
+
+reggie.on('cloud-down', function(cloudId) {
+    console.log('CLOUD DOWN: id: ' + cloudId);
+});
+}
+
+// on rare occasions, you might get an error
+reggie.on('error', function(err) {
+switch(err.name) {
+    case 'permissions_err':
+	console.log(err.message);
+	console.log('Subscriptions: ' + err.value);
+	break;
+    default:
+	console.log('unknown error');
+	break;
+}
+});
+
+//------------------------------------------------------------------------------
+// Custom attributes/discoveries
+//------------------------------------------------------------------------------
+/*
+if (machType === 'device') {
+// we'll have devices announce if they are phones (iphone or android)
+// we'll say all devices are thermostats too...I know it doesn't make sense but it's just meant
+// to be demonstrative :P
+if (phoneType === 'iPhone') {
+    reggie.addAttributes({
+	iPhone: phoneNumber
+    });
+} else if (phoneType === 'Android') {
+    reggie.addAttributes({
+	android: 'psych, get an iPhone!'
+    });
+
+    // in 10 seconds, turn this android into an iphone
+    setTimeout(function() {
+	reggie.removeAttributes('android');
+	reggie.addAttributes({
+	    iPhone: phoneNumber
+	});
+    }, 5000);
+}
+reggie.addAttributes({
+    thermostat: function() {
+	// returns some random number, which we'll treat as the temperature
+	return 'Temperature: ' + Math.random() * 100;
+    }
+});
+} else if (machType === 'fog') {
+// since we'll have clouds discover fogs, we don't need fogs to discover clouds
+reggie.stopDiscoveringAttributes({
+    cloud: ['status']
+});
+
+reggie.discoverAttributes({
+    device: {
+	thermostat: {
+	    onAdd: 'thermo-added',
+	    onRemove: 'thermo-removed'
+	}
+    }
+});
+
+reggie.on('thermo-added', function(id, temp) {
+    console.log('DEVICE ' + id + ' is a thermostat with temperature ' + temp);
+});
+
+reggie.on('thermo-removed', function(id, temp) {
+    console.log('DEVICE ' + id + ' is no longer a thermostat');
+});
+} else {
+// maybe clouds want to discover fogs, and iphone devices
+reggie.discoverAttributes({
+    device: {
+	iPhone: {
+	    onAdd: 'iPhone-added',
+	    onRemove: 'iPhone-removed'
+	},
+	android: {
+	    onAdd: 'android-added',
+	    onRemove: 'android-removed'
+	}
+    },
+    fog: {
+	status: {
+	    online: 'fog-up',
+	    offline: 'fog-down'
+	}
+    }
+});
+
+reggie.on('fog-up', function(fogId, connInfo) {
+    console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+});
+
+reggie.on('fog-down', function(fogId) {
+    console.log('FOG DOWN: id: ' + fogId);
+});
+
+reggie.on('iPhone-added', function(deviceId, phoneNumber) {
+    console.log('DEVICE ' + deviceId + ' is an iPhone with number ' + phoneNumber);
+});
+
+reggie.on('iPhone-removed', function(deviceId) {
+    console.log('DEVICE ' + deviceId + ' is no longer an iPhone');
+});
+
+reggie.on('android-added', function(deviceId, phoneNumber) {
+    console.log('DEVICE ' + deviceId + ' is an Android with number: ' + phoneNumber);
+});
+
+reggie.on('android-removed', function(deviceId) {
+    console.log('DEVICE ' + deviceId + ' is no longer an Android');
+});
+}
+*/
+reggie.registerAndDiscover();
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index eed079f6..6afe0641 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -96,7 +96,7 @@ function Registrar(app, machType, id, port, config) {
         this.eliminateDuplicates = config.eliminateDuplicates;
     } else {
         // by default, eliminate duplicates
-        this.eliminateDuplicates = false;
+        this.eliminateDuplicates = true;
     }
 
     /**

From b7a41f6affdf83a2bd704bc6137c35dc1b072514 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 25 May 2018 16:13:23 -0400
Subject: [PATCH 03/62] JDISC: Enabled clean sessions in MQTT registry

---
 lib/jdiscovery/mqttregistry.js | 50 ++++++++++++++++++----------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 7ab40e94..3da86a46 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -61,6 +61,32 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
     this._prepareForEvents();
 }
 
+/**
+ * Returns connection options to the mqtt broker contingent upon the connecting node
+ * takes as arguments the name of the application, the type of the machine, and the
+ * id of the machine
+ * CleanSession: true
+ * Last Will (LWT): (retain, true)
+ */
+MQTTRegistry.prototype._getConnectionOptions = function() {
+    // create the will
+    var will = {
+        topic: this.app + '/' + this.machType + '/' + this.id + '/status',
+        payload: JSON.stringify({ payload: 'offline' }),
+        qos: this.pubQos,
+        retain: true
+    }
+
+    // set and return the connection options
+    return {
+        clientId: this.id,
+        keepalive: constants.mqtt.keepAlive,
+        clean: true,
+        connectTimeout: constants.mqtt.connectionTimeout,
+        will: will
+    };
+}
+
 /**
  * A general helper for listening for events from the MQTT client
  */
@@ -407,30 +433,6 @@ MQTTRegistry.prototype.unpublish = function(self, attr) {
     // if we're disconnected, then we'll automatically try to publish the attributes when we connect
 }
 
-/**
- * Returns connection options to the mqtt broker contingent upon the connecting node
- * takes as arguments the name of the application, the type of the machine, and the
- * id of the machine
- */
-MQTTRegistry.prototype._getConnectionOptions = function() {
-    // create the will
-    var will = {
-        topic: this.app + '/' + this.machType + '/' + this.id + '/status',
-        payload: JSON.stringify({ payload: 'offline' }),
-        qos: this.pubQos,
-        retain: true
-    }
-
-    // set and return the connection options
-    return {
-        clientId: this.id,
-        keepalive: constants.mqtt.keepAlive,
-        clean: false,
-        connectTimeout: constants.mqtt.connectionTimeout,
-        will: will
-    };
-}
-
 //==============================================================================
 // Custom attribute publication/discovery
 //==============================================================================

From 6e192c3a3640937aeca9899ea9c7f8e9b7ca523a Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 25 May 2018 16:42:17 -0400
Subject: [PATCH 04/62] JDISC: Removed default discovery of clouds by devs

---
 lib/jamserver/constants.js    |  2 +-
 lib/jdiscovery/apps/tester.js | 29 +++++++++++++++--------------
 lib/jdiscovery/jregistrar.js  |  6 ------
 3 files changed, 16 insertions(+), 21 deletions(-)

diff --git a/lib/jamserver/constants.js b/lib/jamserver/constants.js
index 93ab1e70..49a3ec78 100644
--- a/lib/jamserver/constants.js
+++ b/lib/jamserver/constants.js
@@ -45,7 +45,7 @@ module.exports = Object.freeze({
         retryInterval: 2000, // 2 seconds
         longRetryInterval: 60000, // 1 minute
         localBrokerUrl: 'http://localhost:1883',
-        brokerUrl: 'tcp://jamscript.madhacker.biz:1883'
+        brokerUrl: 'http://localhost:1884'
     },
 
     mdns: {
diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 6792bd13..aabf6483 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -21,29 +21,30 @@ console.log(machType + ' id: ' + id);
 console.log('-----------------------------------------------');
 console.log();
 
-var reggie = new Registrar(app, machType, id, port);
+var reggie = new Registrar(app, machType, id, port,
+                           { protocols: { mqtt: true, mdns: false } });
 
 //------------------------------------------------------------------------------
 // Default discoveries
 //------------------------------------------------------------------------------
 
 if (machType === 'device') {
-reggie.on('fog-up', function(fogId, connInfo) {
-    console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
-});
 
-reggie.on('fog-down', function(fogId) {
-    console.log('FOG DOWN: id: ' + fogId);
-});
-} else if (machType === 'fog') {
-reggie.on('cloud-up', function(cloudId, connInfo) {
-    console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+    reggie.on('fog-up', function(fogId, connInfo) {
+        console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+    });
+    reggie.on('fog-down', function(fogId) {
+        console.log('FOG DOWN: id: ' + fogId);
+    });
 
-});
+} else if (machType === 'fog') {
 
-reggie.on('cloud-down', function(cloudId) {
-    console.log('CLOUD DOWN: id: ' + cloudId);
-});
+    reggie.on('cloud-up', function(cloudId, connInfo) {
+        console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+    });
+    reggie.on('cloud-down', function(cloudId) {
+        console.log('CLOUD DOWN: id: ' + cloudId);
+    });
 }
 
 // on rare occasions, you might get an error
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 6afe0641..989bfbf7 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -126,12 +126,6 @@ function Registrar(app, machType, id, port, config) {
                     offline: 'fog-down'
                     // if the status value is `offline`, then we emit fog-down, else we emit fog-up
                 }
-            },
-            cloud: {
-                status: {
-                    online: 'cloud-up',
-                    offline: 'cloud-down'
-                }
             }
         });
     } else if (this.machType === globals.NodeType.FOG) {

From 1cade4f5fbc1e05fdf9cdc95c5fcedd469547117 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 29 May 2018 10:19:29 -0400
Subject: [PATCH 05/62] JDISC: Readded default cloud discovery for devs

---
 lib/jdiscovery/jregistrar.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 989bfbf7..6afe0641 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -126,6 +126,12 @@ function Registrar(app, machType, id, port, config) {
                     offline: 'fog-down'
                     // if the status value is `offline`, then we emit fog-down, else we emit fog-up
                 }
+            },
+            cloud: {
+                status: {
+                    online: 'cloud-up',
+                    offline: 'cloud-down'
+                }
             }
         });
     } else if (this.machType === globals.NodeType.FOG) {

From 21bbf376b165e35c936850c079e530de0df66143 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 29 May 2018 11:54:30 -0400
Subject: [PATCH 06/62] JDISC: MQTT: Added exit procedure + Implemented new
 policy

---
 lib/jdiscovery/apps/tester.js  | 14 ++---
 lib/jdiscovery/mqttregistry.js | 94 +++++++++++++++++-----------------
 2 files changed, 51 insertions(+), 57 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index aabf6483..ef5426de 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -9,9 +9,9 @@ var random = new Random(Random.engines.mt19937().autoSeed());
 var machType = process.argv[2],
 phoneType = process.argv[3],
 phoneNumber = process.argv[4],
-app = 'tester',
-port = 42429,
-id = random.uuid4();
+port = process.argv[5],
+id = process.argv[6],
+app = 'tester';
 
 // don't forget to initialize the logger!
 errLog.init(app, false);
@@ -24,9 +24,7 @@ console.log();
 var reggie = new Registrar(app, machType, id, port,
                            { protocols: { mqtt: true, mdns: false } });
 
-//------------------------------------------------------------------------------
 // Default discoveries
-//------------------------------------------------------------------------------
 
 if (machType === 'device') {
 
@@ -60,10 +58,8 @@ switch(err.name) {
 }
 });
 
-//------------------------------------------------------------------------------
 // Custom attributes/discoveries
-//------------------------------------------------------------------------------
-/*
+
 if (machType === 'device') {
 // we'll have devices announce if they are phones (iphone or android)
 // we'll say all devices are thermostats too...I know it doesn't make sense but it's just meant
@@ -158,5 +154,5 @@ reggie.on('android-removed', function(deviceId) {
     console.log('DEVICE ' + deviceId + ' is no longer an Android');
 });
 }
-*/
+
 reggie.registerAndDiscover();
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 3da86a46..3e9a1041 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -66,9 +66,10 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
  * takes as arguments the name of the application, the type of the machine, and the
  * id of the machine
  * CleanSession: true
- * Last Will (LWT): (retain, true)
+ * Last Will (LWT): (retain: true)
  */
 MQTTRegistry.prototype._getConnectionOptions = function() {
+
     // create the will
     var will = {
         topic: this.app + '/' + this.machType + '/' + this.id + '/status',
@@ -95,47 +96,36 @@ MQTTRegistry.prototype._prepareForEvents = function() {
 
     /* connect event emitted on successful connection or reconnection */
     this.client.on('connect', function (connack) {
-        if (!connack.sessionPresent) {
-            /*
-             * session is not present - subscriptions start from scratch but
-             * old publications could still be persisted on the broker
-             */
-            // reset attrsToUnsubFrom
-            self.attrsToUnsubFrom = {
-                device: {},
-                fog: {},
-                cloud: {}
-            };
-            // subscribe
-            // combine subscribedAttrs and attrsToSubTo so that we can subscribe to all of them
-            for (var attr in self.subscribedAttrs.device) {
-                self.attrsToSubTo.device[attr] = self.subscribedAttrs.device[attr];
-            }
-            for (var attr in self.subscribedAttrs.fog) {
-                self.attrsToSubTo.fog[attr] = self.subscribedAttrs.fog[attr];
-            }
-            for (var attr in self.subscribedAttrs.cloud) {
-                self.attrsToSubTo.cloud[attr] = self.subscribedAttrs.cloud[attr];
-            }
-            self.subscribedAttrs = {
-                device: {},
-                fog: {},
-                cloud: {}
-            };
-            self._subscribeWithRetries(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
-        } else {
-            /*
-             * session is present - old subscriptions are still there so we only need to subscribe to new ones
-             * but we also need to unsubscribe from attrsToUnsubFrom
-             * we make no assumptions about the state of publications
-             */
-            // subscribe
-            self._subscribeWithRetries(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
-            // unsubscribe
-            self._unsubscribeWithRetries(self, JSON.parse(JSON.stringify(self.attrsToRemove)), constants.mqtt.retries);
-        }
 
-        // publish
+		/*
+		 * session is not present - subscriptions start from scratch but
+		 * old publications still persist on the broker
+		 */
+		// reset attrsToUnsubFrom
+		self.attrsToUnsubFrom = {
+			device: {},
+			fog: {},
+			cloud: {}
+		};
+		// ...SUBSCRIBE...
+		// combine subscribedAttrs and attrsToSubTo so that we can subscribe to all of them
+		for (var attr in self.subscribedAttrs.device) {
+			self.attrsToSubTo.device[attr] = self.subscribedAttrs.device[attr];
+		}
+		for (var attr in self.subscribedAttrs.fog) {
+			self.attrsToSubTo.fog[attr] = self.subscribedAttrs.fog[attr];
+		}
+		for (var attr in self.subscribedAttrs.cloud) {
+			self.attrsToSubTo.cloud[attr] = self.subscribedAttrs.cloud[attr];
+		}
+		self.subscribedAttrs = {
+			device: {},
+			fog: {},
+			cloud: {}
+		};
+		self._subscribeWithRetries(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
+
+        // ...PUBLISH...
         for (var attr in self.publishedAttrs) {
             self.attrsToPublish[attr] = self.publishedAttrs[attr];
         }
@@ -143,7 +133,7 @@ MQTTRegistry.prototype._prepareForEvents = function() {
         for (var attr in self.attrsToPublish) {
             self._publishWithRetries(self, attr, self.attrsToPublish[attr].payload, self.attrsToPublish[attr].dedupeId, constants.mqtt.retries);
         }
-        // unpublish
+        // ...UNPUBLISH...
         for (var attr in self.attrsToRemove) {
             self._unpublishWithRetries(self, attr, constants.mqtt.retries);
         }
@@ -367,7 +357,7 @@ MQTTRegistry.prototype.unsubscribe = function(self, dattrs) {
     if (self.client && self.client.connected) {
         self._unsubscribeWithRetries(self, dattrs, constants.mqtt.retries);
     }
-    // if we're disconnected, then we'll automatically try to unsubscribe from the attributes when we connect
+    // if we're disconnected, then we have already been unsubscribed (cleanSession: true)
 }
 
 /**
@@ -592,15 +582,23 @@ MQTTRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
 }
 
 /**
- * Closes the client, executing the callback upon completion
+ * Closes the mqtt registry
+ * --> Cleans up all publications on mqtt broker
+ * --> Kills connection to broker
  */
-/*
-MQTTRegistry.prototype.quit = function(cb) {
+MQTTRegistry.prototype.quit = function() {
+
     if (this.client) {
-        this.client.end(false, cb);
+        // unpublish on all attributes
+        this.removeAttributes(this.publishedAttrs);
+        // unpublish status
+        this.client.publish(this.app + '/' + this.machType + '/' + this.id,
+                            null, {qos: this.pubQos, retain: true});
+        // end connection
+        this.client.end(false);
     }
+    this.client = null;
 }
-*/
 
 /* exports */
 module.exports = MQTTRegistry;

From af8da2b58d655b02153ac99da903fd02a7cccb4c Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 29 May 2018 12:02:30 -0400
Subject: [PATCH 07/62] JDISC: Fixed status clearing in mqtt exit

---
 lib/jdiscovery/mqttregistry.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 3e9a1041..47589365 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -592,7 +592,7 @@ MQTTRegistry.prototype.quit = function() {
         // unpublish on all attributes
         this.removeAttributes(this.publishedAttrs);
         // unpublish status
-        this.client.publish(this.app + '/' + this.machType + '/' + this.id,
+        this.client.publish(this.app + '/' + this.machType + '/' + this.id + '/status',
                             null, {qos: this.pubQos, retain: true});
         // end connection
         this.client.end(false);

From b3f002f06436e2a75f56a6321b70b20da2739d9a Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 31 May 2018 10:05:51 -0400
Subject: [PATCH 08/62] JDISC: MQTT registry uses QoS 1 exclusively

---
 lib/jdiscovery/jregistrar.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 6afe0641..f499ce5b 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -77,8 +77,9 @@ function Registrar(app, machType, id, port, config) {
     var noProtocols = true;
 
     if (!config || !config.protocols || config.protocols.mqtt) {
-        var subQos = this.machType == globals.NodeType.DEVICE ? 0 : 1;
-        var pubQos = this.machType == globals.NodeType.DEVICE ? 0 : 1;
+        // QoS |1| for MQTT: Message delivery is __AT_LEAST_ONCE__
+        var subQos = 1,
+            pubQos = 1;
         this.mqttRegistry = new MQTTRegistry(app, machType, id, port, subQos, pubQos);
         noProtocols = false;
     }

From 8f2bcc82ffd21a3482006641a09dccf78906c487 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 31 May 2018 12:00:19 -0400
Subject: [PATCH 09/62] JDISC: __PARTIAL__ Implementing FIFO event ordering &&
 code refactoring

---
 lib/jdiscovery/jregistrar.js | 436 +++++++++--------------------------
 1 file changed, 103 insertions(+), 333 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index f499ce5b..ef9d35a5 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -5,25 +5,6 @@ var EventEmitter = require('events'),
     MDNSRegistry = require('./mdnsregistry'),
     os = require('os');
 
-//==============================================================================
-// Helpers
-//==============================================================================
-
-/**
- * returns the IPv4 address of the node
- */
-function getIPv4Address() {
-    var niaddrs = os.networkInterfaces();
-    for (var ni in niaddrs) {
-        nielm = niaddrs[ni];
-        for (n in nielm) {
-            if (nielm[n].family === 'IPv4' && nielm[n].internal === false)
-                return nielm[n].address
-        }
-    }
-    return globals.localhost;
-}
-
 //==============================================================================
 // Registrar Class
 // This class is the interface between the application
@@ -46,20 +27,34 @@ function Registrar(app, machType, id, port, config) {
     }
     this.port = port;
 
-    /*
-     * Store discoveries so that we can easily check for duplicates.
-     * discoveries is an object which maps from an attribute name, e.g. 'status' to
-     * a map of <node ID, message ID> pairs. e.g. if discoveries looks like:
-     *  {
-     *      status: {
-     *          a: 123,
-     *          b: 456
+    /**
+     * DISCOVERY TABLE (dt)
+     * Notes:
+     *  --> We don't keep an entry for our own node
+     *
+     * And an example...
+     * {
+     *      node_id : {
+     *          'attrs' : {
+     *              'status' : {
+     *                  'seqval' : seqval,
+     *                  'data' : data
+     *              },
+     *              other_attr : {
+     *                  'seqval' : seqval,
+     *                  'data' : data
+     *              }
+     *          }
+     *          'network' : 'LAN' || 'WAN',
+     *          'other_useful_info' : ...
+     *      },
+     *      other_node_id : {
+     *          ...
      *      }
-     *  }
-     * then we know that the last message received from node 'a' regarding the
-     * 'attribute' status had ID 123.
+     * }
      */
-    this.discoveries = {};
+    this.seqval = 0;
+    this.dt = {};
 
     /**
      * Reserved attributes.
@@ -73,7 +68,6 @@ function Registrar(app, machType, id, port, config) {
     /**
      * config-specific set-up
      */
-
     var noProtocols = true;
 
     if (!config || !config.protocols || config.protocols.mqtt) {
@@ -83,23 +77,14 @@ function Registrar(app, machType, id, port, config) {
         this.mqttRegistry = new MQTTRegistry(app, machType, id, port, subQos, pubQos);
         noProtocols = false;
     }
-
     if (!config || !config.protocols || config.protocols.mdns) {
         this.mdnsRegistry = new MDNSRegistry(app, machType, id, port);
         noProtocols = false;
     }
-
     if (noProtocols) {
         throw new Error('a Registrar must use at least one protocol');
     }
 
-    if (config && config.hasOwnProperty('eliminateDuplicates')) {
-        this.eliminateDuplicates = config.eliminateDuplicates;
-    } else {
-        // by default, eliminate duplicates
-        this.eliminateDuplicates = true;
-    }
-
     /**
      * Set up default attributes, which are the same for devices, fogs, and clouds.
      * The only default attribute is 'status'.
@@ -108,7 +93,7 @@ function Registrar(app, machType, id, port, config) {
         status: function() {
             return {
                 port: port,
-                ip: getIPv4Address()
+                ip: this._getIPv4Address()
             };
         }
     }, true);
@@ -154,19 +139,22 @@ function Registrar(app, machType, id, port, config) {
     var self = this;
 
     if (this.mqttRegistry) {
-        this.mqttRegistry.on('sub-error', function(dattrs) {
-            setTimeout(self.mqttRegistry.subscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, dattrs);
+        /**
+         * MQTT SPECIFIC ERRORS
+         */ 
+        this.mqttRegistry.on('sub-error', function(attrs) {
+            setTimeout(self.mqttRegistry.subscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
         });
 
-        this.mqttRegistry.on('subs-denied', function(dattrs) {
+        this.mqttRegistry.on('subs-denied', function(attrs) {
             var err = new Error('MQTT subscriptions denied');
             err.name = 'permissions_err';
-            err.value = dattrs;
+            err.value = attrs;
             self.emit('error', err);
         });
 
-        this.mqttRegistry.on('unsub-error', function(dattrs) {
-            setTimeout(self.mqttRegistry.unsubscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, dattrs);
+        this.mqttRegistry.on('unsub-error', function(attrs) {
+            setTimeout(self.mqttRegistry.unsubscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
         });
 
         this.mqttRegistry.on('pub-error', function(attr, value) {
@@ -177,19 +165,26 @@ function Registrar(app, machType, id, port, config) {
             setTimeout(self.mqttRegistry.unpublish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr);
         });
 
+        /**
+         * MQTT PROPAGATABLE EVENTS
+         */ 
         this.mqttRegistry.on('discovery', function(attr, event, nodeId, value, dedupeId) {
-            self._respondToDiscoveryEvent(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MQTT);
+            self._respondToDiscoveryEvent.call(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MQTT);
         });
 
         this.mqttRegistry.on('attr-removed', function(attr, event, nodeId) {
-            self._respondToAttrRemovalEvent(self, attr, event, nodeId, constants.globals.Protocol.MQTT);
+            self._respondToAttrRemovalEvent.call(self, attr, event, nodeId, constants.globals.Protocol.MQTT);
         });
     }
 
     if (this.mdnsRegistry) {
+        /**
+         * mDNS SPECIFIC ERRORS
+         */ 
         this.mdnsRegistry.on('ad-error', function(attr, adName, txtRecord) {
             // an ad failed - try again after some time
-            setTimeout(self.mdnsRegistry._createAdvertisementWithRetries, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, adName, txtRecord, 0);
+            setTimeout(self.mdnsRegistry._createAdvertisementWithRetries, constants.mdns.longRetryInterval,
+                        self.mdnsRegistry, attr, adName, txtRecord, 0);
         });
 
         this.mdnsRegistry.on('browser-error', function(attr, machType, events) {
@@ -201,12 +196,15 @@ function Registrar(app, machType, id, port, config) {
             }
         });
 
+        /**
+         * mDNS PROPAGATABLE EVENTS
+         */ 
         this.mdnsRegistry.on('discovery', function(attr, event, nodeId, value, dedupeId) {
-            self._respondToDiscoveryEvent(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MDNS);
+            self._respondToDiscoveryEvent.call(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MDNS);
         });
 
         this.mdnsRegistry.on('attr-removed', function(attr, event, nodeId) {
-            self._respondToAttrRemovalEvent(self, attr, event, nodeId, constants.globals.Protocol.MDNS);
+            self._respondToAttrRemovalEvent.call(self, attr, event, nodeId, constants.globals.Protocol.MDNS);
         });
     }
 }
@@ -227,6 +225,9 @@ Registrar.prototype.constructor = Registrar;
  *   attrsToDiscover: as in this.discoverAttributes
  */
 Registrar.prototype.registerAndDiscover = function(options) {
+    if (this.started)
+        return;
+
     if (options) {
         if (typeof options !== 'object') {
             throw new Error('options must be an object - see the docs');
@@ -235,69 +236,31 @@ Registrar.prototype.registerAndDiscover = function(options) {
         if (options.attrsToAdd) {
             this.addAttributes(options.attrsToAdd);
         }
-
         if (options.attrsToDiscover) {
             this.discoverAttributes(options.attrsToDiscover);
         }
     }
 
-    if (!this.started) {
-        if (this.mqttRegistry) {
-            this.mqttRegistry.registerAndDiscover();
-        }
-        if (this.mdnsRegistry) {
-            this.mdnsRegistry.registerAndDiscover();
-        }
-        this.started = true;
+    if (this.mqttRegistry) {
+        this.mqttRegistry.registerAndDiscover();
     }
-}
-
-/**
- * Upon receipt of an attribute removal event, pass it onto the rest of the application if it
- * is something we don't already know
- */
-Registrar.prototype._respondToAttrRemovalEvent = function(self, attr, event, nodeId, protocol) {
-    if (self.eliminateDuplicates && (!self.discoveries.hasOwnProperty(attr) || !self.discoveries[attr].hasOwnProperty(nodeId))) {
-        return;
+    if (this.mdnsRegistry) {
+        this.mdnsRegistry.registerAndDiscover();
     }
-    delete self.discoveries[attr][nodeId];
-    self.emit(event, nodeId, protocol);
-}
 
-//==============================================================================
-// Add and discover attributes
-//==============================================================================
+    this.started = true;
+}
 
 /**
  * Add custom, discoverable attributes to this node
  * attrs is an object of key value pairs
  */
-Registrar.prototype.addAttributes = function(attrs, override) {
-    // error handling
-    if (!override) {
-        this._checkFormOfAttrsToAdd(attrs);
-    }
-    // use the time corresponding to the publication of these attributes as the message ID
-    const dedupeId = Date.now();
-    // add the attributes on each protocol
-    if (this.mqttRegistry) {
-        this.mqttRegistry.addAttributes(attrs, dedupeId);
-    }
-    if (this.mdnsRegistry) {
-        this.mdnsRegistry.addAttributes(attrs, dedupeId);
-    }
+Registrar.prototype.addAttributes = function(attrs) {
+    this._modifyAttributes("addAttributes", attrs, this._getSeqVal());
 }
 
 Registrar.prototype.removeAttributes = function(attrs) {
-    // error handling
-    attrs = this._reformatAttrsToRemove(attrs);
-    // remove the attributes on each protocol
-    if (this.mqttRegistry) {
-        this.mqttRegistry.removeAttributes(attrs);
-    }
-    if (this.mdnsRegistry) {
-        this.mdnsRegistry.removeAttributes(attrs);
-    }
+    this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
 
 /**
@@ -312,23 +275,20 @@ Registrar.prototype.removeAttributes = function(attrs) {
  *    }
  * (b) As a shortcut for all, one can simply pass an object of <attr, event> pairs
  */
-Registrar.prototype.discoverAttributes = function(dattrs) {
-    dattrs = this._checkAndReformatAttrsToDiscover(dattrs);
-    if (this.mqttRegistry) {
-        this.mqttRegistry.discoverAttributes(dattrs);
-    }
-    if (this.mdnsRegistry) {
-        this.mdnsRegistry.discoverAttributes(dattrs);
-    }
+Registrar.prototype.discoverAttributes = function(attrs) {
+    this._modifyAttributes("discoverAttributes", attrs, this._getSeqVal());
 }
 
-Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
-    dattrs = this._checkAndReformatAttrsToStopDiscovering(dattrs);
+Registrar.prototype.stopDiscoveringAttributes = function(attrs) {
+    this._modifyAttributes("stopDiscoveringAttributes", attrs, this._getSeqVal());
+}
+
+Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
     if (this.mqttRegistry) {
-        this.mqttRegistry.stopDiscoveringAttributes(dattrs);
+        this.mqttRegistry[fun](attrs, seqno);
     }
     if (this.mdnsRegistry) {
-        this.mdnsRegistry.stopDiscoveringAttributes(dattrs);
+        this.mdnsRegistry.[fun](attrs, seqno);
     }
 }
 
@@ -337,247 +297,57 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
 //==============================================================================
 
 /**
- * Checks the format of a set of attributes to discover, and reformats them into the form accepted
- * by the three registries
+ * Upon receipt of a discovery event, pass it onto the rest of the application if it is not a duplicate
  */
-Registrar.prototype._checkAndReformatAttrsToDiscover = function(attrs) {
-    // error handling
-    if (typeof attrs !== 'object') {
-        throw new Error('you must specify the attributes you want discovered as an object - see the docs');
-    }
-    // check that the attrs parameter is properly formed
-    var formedAttrs;
-    if (attrs.all === undefined &&
-        attrs.device === undefined &&
-        attrs.fog === undefined &&
-        attrs.cloud === undefined) {
-            this._checkFormOfAttrsToDiscover(attrs);
-            formedAttrs = {
-                device: {},
-                fog: {},
-                cloud: {}
-            };
-            for (var key in attrs) {
-                formedAttrs.device[key] = attrs[key];
-                formedAttrs.fog[key] = attrs[key];
-                formedAttrs.cloud[key] = attrs[key];
-            }
-    } else {
-        this._checkFormOfAttrsToDiscover(attrs.all);
-        this._checkFormOfAttrsToDiscover(attrs.device);
-        this._checkFormOfAttrsToDiscover(attrs.fog);
-        this._checkFormOfAttrsToDiscover(attrs.cloud);
-        for (var key in attrs.all) {
-            attrs.device[key] = attrs.all[key];
-            attrs.fog[key] = attrs.all[key];
-            attrs.cloud[key] = attrs.all[key];
-        }
-        formedAttrs = attrs;
-    }
-    return formedAttrs;
-}
+Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
-Registrar.prototype._checkAndReformatAttrsToStopDiscovering = function(dattrs) {
-    // error handling
-    if (!(dattrs instanceof Object) && !(dattrs instanceof Array)) {
-        throw new Error('you must specify the attributes to stop discovering in an object or array - see the docs');
-    }
+    console.log("---------------------------------- ", event, id, data);
 
-    // check that the attrs parameter is properly formed
-    var formedDattrs;
-    if (dattrs instanceof Array) {
-        formedDattrs = {
-            device: [],
-            fog: [],
-            cloud: []
-        };
-        for (var i = 0; i < dattrs.length; i++) {
-            if (typeof dattrs[i] != 'string') {
-                throw new Error('the attribute \'' + dattrs[i] + '\' is not a string');
-            }
-            formedDattrs.device.push(dattrs[i]);
-            formedAttrs.fog.push(dattrs[i]);
-            formedAttrs.cloud.push(dattrs[i]);
-        }
-    } else {
-        if (dattrs.all) {
-            this._checkArrayOfStrings(dattrs.all);
-        }
-        if (dattrs.device) {
-            this._checkArrayOfStrings(dattrs.device);
-        }
-        if (dattrs.fog) {
-            this._checkArrayOfStrings(dattrs.fog);
-        }
-        if (dattrs.cloud) {
-            this._checkArrayOfStrings(dattrs.cloud);
-        }
-        if (dattrs.all) {
-            for (var i = 0; i < dattrs.all.length; i++) {
-                dattrs.device.push(dattrs.all[i]);
-                dattrs.fog.push(dattrs.all[i]);
-                dattrs.cloud.push(dattrs.all[i]);
-            }
-        }
-        formedDattrs = dattrs;
+    if (!this.dt.hasOwnProperty(attr)) {
+        this.dt[attr] = {};
     }
-    return formedDattrs;
-}
+    this.dt[attr][id] = seqval;
 
-Registrar.prototype._checkArrayOfStrings = function(arr) {
-    if (!(arr instanceof Array)) {
-        throw new Error('attributes to stop discovering must be passed as an array of strings');
-    }
-
-    for (var i = 0; i < arr.length; i++) {
-        if (typeof arr[i] != 'string') {
-            throw new Error('the attribute \'' + arr[i] + '\' is not a string');
-        }
+    // Because node offline events end up here instead of in _respondToAttrRemovalEvent, we need to check the attribute
+    // and value in order to know what arguments to pass along with the event.
+    if (attr === 'status' && data === 'offline') {
+        this.emit(event, id, protocol);
+        return;
     }
+    this.emit(event, id, data, protocol);
 }
 
 /**
- * A helper for Registrar.prototype.discoverAttributes;
- * ensures that attrs is an object of <string, string> pairs
+ * Upon receipt of an attribute removal event, pass it onto the rest of the application
  */
-Registrar.prototype._checkFormOfAttrsToDiscover = function(attrs) {
-    for (var key in attrs) {
-        if (key == 'status') {
-            // ensure that online and offline events are specified
-            if (!attrs.status instanceof Object) {
-                throw new Error('discovery of the status attribute requires \'online\' and \'offline\' event names, passed in an object - see the docs');
-            }
-
-            // online
-            if (!attrs.status.hasOwnProperty('online')) {
-                throw new Error('\'online\' event required for discovery of status attribute');
-            } else {
-                if (typeof attrs.status.online != 'string') {
-                    throw new Error('the event name \'' + attrs.status.online + '\' must be a string');
-                }
-            }
-
-            // offline
-            if (!attrs.status.hasOwnProperty('offline')) {
-                throw new Error('\'offline\' event required for discovery of status attribute');
-            } else {
-                if (typeof attrs.status.offline != 'string') {
-                    throw new Error('the event name \'' + attrs.status.offline + '\' must be a string');
-                }
-            }
-        } else {
-            // ensure that onAdd and onRemove events are specified
-            if (!attrs[key] instanceof Object) {
-                throw new Error('discovery of an attribute requires \'onAdd\' and \'onRemove\' event names, passed in an object - see the docs');
-            }
-
-            // onAdd
-            if (!attrs[key].hasOwnProperty('onAdd')) {
-                throw new Error('\'onAdd\' event required for discovery of an attribute');
-            } else {
-                if (typeof attrs[key].onAdd != 'string') {
-                    throw new Error('the event name \'' + attrs[key].onAdd + '\' must be a string');
-                }
-            }
-
-            // onRemove
-            if (!attrs[key].hasOwnProperty('onRemove')) {
-                throw new Error('\'onRemove\' event required for discovery of an attribute');
-            } else {
-                if (typeof attrs[key].onRemove != 'string') {
-                    throw new Error('the event name \'' + attrs[key].onRemove + '\' must be a string');
-                }
-            }
-        }
-    }
-}
-
-Registrar.prototype._checkFormOfAttrsToAdd = function(attrs) {
-    if (typeof attrs !== 'object') {
-        throw new Error('attrs must be an object');
-    }
-    for (var i = 0; i < this.reservedAttrs.length; i++) {
-        if (attrs[this.reservedAttrs[i]] !== undefined) {
-            throw new Error('the attribute \'' + this.reservedAttrs[i] + '\' is reserved');
-        }
-    }
-    for (var attr in attrs) {
-        if (attrs[attr] === '') {
-            throw new Error('the attribute ' + attr + ' has an empty string as its value - this is not permitted');
-        }
-    }
-}
-
-Registrar.prototype._reformatAttrsToRemove = function(attrs) {
-    if (typeof attrs == 'string') {
-        attrs = [attrs];
-    } else if (!(attrs instanceof Array)) {
-        throw new Error('attrs must be a string or an array of strings');
-    }
-
-    for (var i = 0; i < attrs.length; i++) {
-        if (typeof attrs[i] != 'string') {
-            throw new Error('attrs must be a string or an array of strings');
-        } else if (attrs[i] == 'status') {
-            throw new Error('the \'status\' attribute cannot be removed');
-        }
+Registrar.prototype._respondToAttrRemovalEvent = function(attr, event, id, protocol) {
+    if ((!this.dt.hasOwnProperty(attr) || !this.dt[attr].hasOwnProperty(nodeId))) {
+        return;
     }
-
-    return attrs;
+    delete this.dt[attr][nodeId];
+    this.emit(event, id, protocol);
 }
 
 /**
- * Upon receipt of a discovery event, pass it onto the rest of the application if it is not a duplicate
+ * returns next seq number for event ordering
  */
-Registrar.prototype._respondToDiscoveryEvent = function(self, attr, event, nodeId, value, dedupeId, protocol) {
-
-    console.log("---------------------------------- ", event, nodeId, value);
-
-    if (self.eliminateDuplicates && self._isDuplicate(self, attr, nodeId, dedupeId)) {
-        return;
-    }
-    self._updateDiscoveries(self, attr, nodeId, dedupeId);
-
-    // Because node offline events end up here instead of in _respondToAttrRemovalEvent, we need to check the attribute
-    // and value in order to know what arguments to pass along with the event.
-    if (attr === 'status' && value === 'offline') {
-        self.emit(event, nodeId, protocol);
-        return;
-    }
-    self.emit(event, nodeId, value, protocol);
+Registar.prototype._getSeqVal = function() {
+    return this.seqval++;
 }
 
 /**
- * Returns true if a discovery is a duplicate and false otherwise.
- * attr - the attrubute for which a discovery was made
- * nodeId - the ID of the node for which the discovery was made
- * dedupeId - an ID that tells us if this message is a duplicate or not
+ * returns the IPv4 address of the node
  */
-Registrar.prototype._isDuplicate = function(self, attr, nodeId, dedupeId) {
-    if (!self.discoveries.hasOwnProperty(attr)) {
-        return false;
-    }
-
-    if (!self.discoveries[attr].hasOwnProperty(nodeId)) {
-        return false;
-    }
-
-    // Compare the dedupe ID of the last message with that of the current message
-    // Because the dedupe IDs are timestamps, we say that a message is a duplicate
-    // if its ID is less than or equal to the last received.
-    if (dedupeId === 0 && self.discoveries[attr][nodeId] !== 0) {
-        // The one exception is that a dedupeId of zero is used for node down events,
-        // so we need to account for this special case.
-        return false;
-    }
-    return dedupeId <= self.discoveries[attr][nodeId];
-}
-
-Registrar.prototype._updateDiscoveries = function(self, attr, nodeId, dedupeId) {
-    if (!self.discoveries.hasOwnProperty(attr)) {
-        self.discoveries[attr] = {};
+Registar.prototype._getIPv4Address = function() {
+    var niaddrs = os.networkInterfaces();
+    for (var ni in niaddrs) {
+        nielm = niaddrs[ni];
+        for (n in nielm) {
+            if (nielm[n].family === 'IPv4' && nielm[n].internal === false)
+                return nielm[n].address
+        }
     }
-    self.discoveries[attr][nodeId] = dedupeId;
+    return globals.localhost;
 }
 
 /* exports */

From c804aa8b453c7d23ce0072336d6ce15bf0a54c92 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 31 May 2018 14:03:42 -0400
Subject: [PATCH 10/62] JDISC: __PARTIAL__ Seq num update

---
 lib/jdiscovery/jregistrar.js | 50 ++++++++++++++++++++++++++----------
 1 file changed, 36 insertions(+), 14 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index ef9d35a5..c60ef0a6 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -53,8 +53,14 @@ function Registrar(app, machType, id, port, config) {
      *      }
      * }
      */
-    this.seqval = 0;
     this.dt = {};
+    /**
+     * SEQUENCE VALUE must start at __1__, when wrap-around happens, reset to 1
+     * Wrap around is handled by setting seqval to 0 in message to remote nodes
+     * On detection, they set the current seqval in their dt to 0, and since we are
+     * restarting at 1 locally, it will all work out
+     */
+    this.seqval = 1;
 
     /**
      * Reserved attributes.
@@ -168,12 +174,12 @@ function Registrar(app, machType, id, port, config) {
         /**
          * MQTT PROPAGATABLE EVENTS
          */ 
-        this.mqttRegistry.on('discovery', function(attr, event, nodeId, value, dedupeId) {
-            self._respondToDiscoveryEvent.call(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MQTT);
+        this.mqttRegistry.on('discovery', function(attr, event, id, data, seqval) {
+            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MQTT);
         });
 
-        this.mqttRegistry.on('attr-removed', function(attr, event, nodeId) {
-            self._respondToAttrRemovalEvent.call(self, attr, event, nodeId, constants.globals.Protocol.MQTT);
+        this.mqttRegistry.on('attr-removed', function(attr, event, id) {
+            self._respondToAttrRemovalEvent.call(self, attr, event, id, constants.globals.Protocol.MQTT);
         });
     }
 
@@ -199,12 +205,12 @@ function Registrar(app, machType, id, port, config) {
         /**
          * mDNS PROPAGATABLE EVENTS
          */ 
-        this.mdnsRegistry.on('discovery', function(attr, event, nodeId, value, dedupeId) {
-            self._respondToDiscoveryEvent.call(self, attr, event, nodeId, value, dedupeId, constants.globals.Protocol.MDNS);
+        this.mdnsRegistry.on('discovery', function(attr, event, id, data, seqval) {
+            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MDNS);
         });
 
-        this.mdnsRegistry.on('attr-removed', function(attr, event, nodeId) {
-            self._respondToAttrRemovalEvent.call(self, attr, event, nodeId, constants.globals.Protocol.MDNS);
+        this.mdnsRegistry.on('attr-removed', function(attr, event, id) {
+            self._respondToAttrRemovalEvent.call(self, attr, event, id, constants.globals.Protocol.MDNS);
         });
     }
 }
@@ -285,10 +291,10 @@ Registrar.prototype.stopDiscoveringAttributes = function(attrs) {
 
 Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
     if (this.mqttRegistry) {
-        this.mqttRegistry[fun](attrs, seqno);
+        this.mqttRegistry[fun](attrs, seqval);
     }
     if (this.mdnsRegistry) {
-        this.mdnsRegistry.[fun](attrs, seqno);
+        this.mdnsRegistry[fun](attrs, seqval);
     }
 }
 
@@ -301,12 +307,24 @@ Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
  */
 Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
+    // DUBUGGING: Print shit
     console.log("---------------------------------- ", event, id, data);
 
-    if (!this.dt.hasOwnProperty(attr)) {
-        this.dt[attr] = {};
+    // Update discovery table
+    if (!this.dt.hasOwnProperty(id))
+        this.dt[id] = {};
+    if (!this.dt.id.hasOwnProperty(attr))
+        this.dt.attr = {};
+
+    // Insert data/seqval pair if not already received
+    if ( !this.dt.id.attr.hasOwnProperty('data')
+       || this.dt.id.attr['seqval'] < seqval
+       || (  (this.dt.id.attr['seqval'] > (Number.MAX_SAFE_INTEGER/2))
+          && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
+    {
+        this.dt.attr['data'] = data;
+        this.dt.attr['seqval'] = seqval;
     }
-    this.dt[attr][id] = seqval;
 
     // Because node offline events end up here instead of in _respondToAttrRemovalEvent, we need to check the attribute
     // and value in order to know what arguments to pass along with the event.
@@ -332,6 +350,10 @@ Registrar.prototype._respondToAttrRemovalEvent = function(attr, event, id, proto
  * returns next seq number for event ordering
  */
 Registar.prototype._getSeqVal = function() {
+    if (this.seqval == Number.MAX_SAFE_INTEGER) {
+        this.seqval = 1;
+        return 0;
+    }
     return this.seqval++;
 }
 

From 5692e5be1b8dfa5c6defc1ee28a1099eb9a5105e Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 4 Jun 2018 11:30:45 -0400
Subject: [PATCH 11/62] JDISC: jregistar: seqval update, interface change

---
 lib/jdiscovery/jregistrar.js | 70 ++++++++++++++++++------------------
 1 file changed, 34 insertions(+), 36 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index c60ef0a6..96ea07aa 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -54,13 +54,7 @@ function Registrar(app, machType, id, port, config) {
      * }
      */
     this.dt = {};
-    /**
-     * SEQUENCE VALUE must start at __1__, when wrap-around happens, reset to 1
-     * Wrap around is handled by setting seqval to 0 in message to remote nodes
-     * On detection, they set the current seqval in their dt to 0, and since we are
-     * restarting at 1 locally, it will all work out
-     */
-    this.seqval = 1;
+    this.seqval = 0;
 
     /**
      * Reserved attributes.
@@ -173,7 +167,7 @@ function Registrar(app, machType, id, port, config) {
 
         /**
          * MQTT PROPAGATABLE EVENTS
-         */ 
+         */
         this.mqttRegistry.on('discovery', function(attr, event, id, data, seqval) {
             self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MQTT);
         });
@@ -227,7 +221,7 @@ Registrar.prototype.constructor = Registrar;
  * Register a node on the network, and discover other nodes.
  * `options` is an optional parameter
  * `options` include:
- *   attrsToAdd: key/value pair as in this.addAttributes
+ *   attrsToSet: key/value pair as in this.setAttributes
  *   attrsToDiscover: as in this.discoverAttributes
  */
 Registrar.prototype.registerAndDiscover = function(options) {
@@ -239,21 +233,15 @@ Registrar.prototype.registerAndDiscover = function(options) {
             throw new Error('options must be an object - see the docs');
         }
 
-        if (options.attrsToAdd) {
-            this.addAttributes(options.attrsToAdd);
+        if (options.attrsToSet) {
+            this.setAttributes(options.attrsToAdd);
         }
         if (options.attrsToDiscover) {
             this.discoverAttributes(options.attrsToDiscover);
         }
     }
 
-    if (this.mqttRegistry) {
-        this.mqttRegistry.registerAndDiscover();
-    }
-    if (this.mdnsRegistry) {
-        this.mdnsRegistry.registerAndDiscover();
-    }
-
+    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x.registerAndDiscover);
     this.started = true;
 }
 
@@ -261,17 +249,16 @@ Registrar.prototype.registerAndDiscover = function(options) {
  * Add custom, discoverable attributes to this node
  * attrs is an object of key value pairs
  */
-Registrar.prototype.addAttributes = function(attrs) {
-    this._modifyAttributes("addAttributes", attrs, this._getSeqVal());
+Registrar.prototype.setAttributes = function(attrs) {
+    this._modifyAttributes("setAttributes", attrs, this._getSeqVal());
 }
-
 Registrar.prototype.removeAttributes = function(attrs) {
     this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
 
 /**
  * Specify attributes to be discovered.
- * dattrs can have one of the following forms:
+ * attrs can have one of the following forms:
  * (a)
  *    {
  *        all: {attr: event}, // discover these attributes for all nodes
@@ -284,7 +271,6 @@ Registrar.prototype.removeAttributes = function(attrs) {
 Registrar.prototype.discoverAttributes = function(attrs) {
     this._modifyAttributes("discoverAttributes", attrs, this._getSeqVal());
 }
-
 Registrar.prototype.stopDiscoveringAttributes = function(attrs) {
     this._modifyAttributes("stopDiscoveringAttributes", attrs, this._getSeqVal());
 }
@@ -311,19 +297,31 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
     console.log("---------------------------------- ", event, id, data);
 
     // Update discovery table
-    if (!this.dt.hasOwnProperty(id))
+    if (!this.dt.hasOwnProperty(id)) {
         this.dt[id] = {};
-    if (!this.dt.id.hasOwnProperty(attr))
-        this.dt.attr = {};
+        this.dt[id]['attrs'] = {};
+    }
+    if (!this.dt[id]['attrs'].hasOwnProperty(attr))
+        this.dt.[id]['attrs'][attr] = {};
 
     // Insert data/seqval pair if not already received
-    if ( !this.dt.id.attr.hasOwnProperty('data')
-       || this.dt.id.attr['seqval'] < seqval
-       || (  (this.dt.id.attr['seqval'] > (Number.MAX_SAFE_INTEGER/2))
+    if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
+       || this.dt[id]['attrs'][attr]['seqval'] < seqval
+       || (  (this.dt[id]['attrs'][attr]['seqval'] > (Number.MAX_SAFE_INTEGER/2))
           && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
     {
-        this.dt.attr['data'] = data;
-        this.dt.attr['seqval'] = seqval;
+        this.dt[id]['attrs'][attr]['data'] = data;
+        this.dt[id]['attrs'][attr]['seqval'] = seqval;
+
+        // maintain info about where the node is: 'LAN' or 'WAN'
+        if (  this.dt[id]['network'] !== 'LAN'
+           && protocol === constants.globals.Protocol.MDNS) {
+            this.dt[id]['network'] = 'LAN';
+        } else {
+            this.dt[id]['network'] = 'WAN';
+        }
+    } else {
+        return;
     }
 
     // Because node offline events end up here instead of in _respondToAttrRemovalEvent, we need to check the attribute
@@ -339,10 +337,11 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
  * Upon receipt of an attribute removal event, pass it onto the rest of the application
  */
 Registrar.prototype._respondToAttrRemovalEvent = function(attr, event, id, protocol) {
-    if ((!this.dt.hasOwnProperty(attr) || !this.dt[attr].hasOwnProperty(nodeId))) {
+    if (  !this.dt.hasOwnProperty(id)
+       || !this.dt.id.hasOwnProperty(attr))
         return;
-    }
-    delete this.dt[attr][nodeId];
+
+    delete this.dt.id.attr;
     this.emit(event, id, protocol);
 }
 
@@ -351,8 +350,7 @@ Registrar.prototype._respondToAttrRemovalEvent = function(attr, event, id, proto
  */
 Registar.prototype._getSeqVal = function() {
     if (this.seqval == Number.MAX_SAFE_INTEGER) {
-        this.seqval = 1;
-        return 0;
+        this.seqval = 0;
     }
     return this.seqval++;
 }

From 72c5d7e253f3f450ac88a6bec797c3c97de153eb Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 4 Jun 2018 11:44:54 -0400
Subject: [PATCH 12/62] JDISC: var name changes...

---
 lib/jdiscovery/jregistrar.js   | 18 +++++++--------
 lib/jdiscovery/mqttregistry.js | 42 +++++++++++++++++-----------------
 2 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 96ea07aa..9d1bab65 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -11,11 +11,11 @@ var EventEmitter = require('events'),
 // and the MQTT, mDNS registries
 //==============================================================================
 
-function Registrar(app, machType, id, port, config) {
+function Registrar(app, type, id, port, config) {
     // the name of the application
     this.app = app;
     // the type of the machine the registar is running on (device, fog, or cloud)
-    this.machType = machType;
+    this.type = type;
     // the id of the machine
     this.id = id;
     // the port the program is running on
@@ -74,11 +74,11 @@ function Registrar(app, machType, id, port, config) {
         // QoS |1| for MQTT: Message delivery is __AT_LEAST_ONCE__
         var subQos = 1,
             pubQos = 1;
-        this.mqttRegistry = new MQTTRegistry(app, machType, id, port, subQos, pubQos);
+        this.mqttRegistry = new MQTTRegistry(app, type, id, port, subQos, pubQos);
         noProtocols = false;
     }
     if (!config || !config.protocols || config.protocols.mdns) {
-        this.mdnsRegistry = new MDNSRegistry(app, machType, id, port);
+        this.mdnsRegistry = new MDNSRegistry(app, type, id, port);
         noProtocols = false;
     }
     if (noProtocols) {
@@ -102,7 +102,7 @@ function Registrar(app, machType, id, port, config) {
      * Prep the default discoveries to be made by a node:
      * devices discover fogs and fogs discover clouds.
      */
-    if (this.machType === globals.NodeType.DEVICE) {
+    if (this.type === globals.NodeType.DEVICE) {
         // default discoveries:
         // devices discover fogs
         this.discoverAttributes({
@@ -120,7 +120,7 @@ function Registrar(app, machType, id, port, config) {
                 }
             }
         });
-    } else if (this.machType === globals.NodeType.FOG) {
+    } else if (this.type === globals.NodeType.FOG) {
         // default discoveries:
         // fogs discover clouds
         this.discoverAttributes({
@@ -187,12 +187,12 @@ function Registrar(app, machType, id, port, config) {
                         self.mdnsRegistry, attr, adName, txtRecord, 0);
         });
 
-        this.mdnsRegistry.on('browser-error', function(attr, machType, events) {
+        this.mdnsRegistry.on('browser-error', function(attr, type, events) {
             // a browser failed - try again after some time
             if (attr === 'status') {
-                setTimeout(self.mdnsRegistry._browseForStatus, constants.mdns.longRetryInterval, self.mdnsRegistry, machType, events);
+                setTimeout(self.mdnsRegistry._browseForStatus, constants.mdns.longRetryInterval, self.mdnsRegistry, type, events);
             } else {
-                setTimeout(self.mdnsRegistry._browse, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, machType, events);
+                setTimeout(self.mdnsRegistry._browse, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, type, events);
             }
         });
 
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 47589365..8ccdcb50 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -7,8 +7,8 @@ var mqtt = require('mqtt'),
     constants = require('../jamserver/constants'),
     Registry = require('./registry');
 
-function MQTTRegistry(app, machType, id, port, subQos, pubQos) {
-    Registry.call(this, app, machType, id, port);
+function MQTTRegistry(app, type, id, port, subQos, pubQos) {
+    Registry.call(this, app, type, id, port);
     // the quality of service to use for subscriptions
     this.subQos = subQos;
     // the quality of service to use for publications
@@ -72,7 +72,7 @@ MQTTRegistry.prototype._getConnectionOptions = function() {
 
     // create the will
     var will = {
-        topic: this.app + '/' + this.machType + '/' + this.id + '/status',
+        topic: this.app + '/' + this.type + '/' + this.id + '/status',
         payload: JSON.stringify({ payload: 'offline' }),
         qos: this.pubQos,
         retain: true
@@ -143,20 +143,20 @@ MQTTRegistry.prototype._prepareForEvents = function() {
     this.client.on('message', function (topic, message, packet) {
         // parse the mach type, the mach id, and the attribute out of the topic
         var components = topic.split('/');
-        var machType = components[1];
-        var machId = components[2];
+        var type = components[1];
+        var id = components[2];
         var attr = components[3];
 
         // if the message is an empty string, then this is the result of an "unpublish".
         // i.e. another node unpublished an attribute and we are now being notified.
         if (message.toString() === '') {
             var eventName;
-            if (self.subscribedAttrs[machType].hasOwnProperty(attr)) {
-                eventName = self.subscribedAttrs[machType][attr].onRemove;
+            if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
+                eventName = self.subscribedAttrs[type][attr].onRemove;
             }
 
             if (eventName !== undefined) {
-                self.emit('attr-removed', attr, eventName, machId);
+                self.emit('attr-removed', attr, eventName, id);
             } else {
                 console.log('Message received yet we are not subscribed to this message, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
             }
@@ -164,7 +164,7 @@ MQTTRegistry.prototype._prepareForEvents = function() {
         }
 
         const parsedMsg = JSON.parse(message.toString());
-        self._handleMessage(self, machType, machId, attr, parsedMsg.payload, parsedMsg.id);
+        self._handleMessage(self, type, id, attr, parsedMsg.payload, parsedMsg.id);
     });
 
     this.client.on('offline', function () {
@@ -180,24 +180,24 @@ MQTTRegistry.prototype._prepareForEvents = function() {
  * Handles receipt of a message from the MQTT broker. Finds the subscription that
  * the message corresponds to and executes the appropriate action.
  */
-MQTTRegistry.prototype._handleMessage = function(self, machType, machId, attr, payload, dedupeId) {
+MQTTRegistry.prototype._handleMessage = function(self, type, id, attr, payload, dedupeId) {
     var eventName;
-    if (self.subscribedAttrs[machType].hasOwnProperty(attr)) {
+    if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
         if (attr === 'status') {
             if (payload === 'offline') {
-                eventName = self.subscribedAttrs[machType].status.offline;
+                eventName = self.subscribedAttrs[type].status.offline;
                 // pass a dedupeId of zero for node down events
                 dedupeId = 0;
             } else {
-                eventName = self.subscribedAttrs[machType].status.online;
+                eventName = self.subscribedAttrs[type].status.online;
             }
         } else {
-            eventName = self.subscribedAttrs[machType][attr].onAdd;
+            eventName = self.subscribedAttrs[type][attr].onAdd;
         }
     }
 
     if (eventName !== undefined) {
-        self.emit('discovery', attr, eventName, machId, payload, dedupeId);
+        self.emit('discovery', attr, eventName, id, payload, dedupeId);
     } else {
         console.log('Message received yet we are not subscribed to this message channel, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
     }
@@ -268,12 +268,12 @@ MQTTRegistry.prototype._subscribeWithRetries = function(self, dattrs, retries) {
         } else {
             // move any attrs that were denied from subscribedAttrs to attrsToSubTo and
             // emit an event indicating the attributes that were denied
-            var components, machType, attr;
+            var components, type, attr;
             for (var i = 0; i < granted.length; i++) {
                 components = granted[i].topic.split('/');
-                machType = components[1];
+                type = components[1];
                 attr = components[3];
-                delete dattrs[machType][attr];
+                delete dattrs[type][attr];
             }
 
             for (var attr in dattrs.device) {
@@ -375,7 +375,7 @@ MQTTRegistry.prototype._publishWithRetries = function(self, attr, value, dedupeI
     } else {
         msg = JSON.stringify({ payload: value, id: dedupeId });
     }
-    self.client.publish(self.app + '/' + self.machType + '/' + self.id + '/' + attr, msg, {qos: self.pubQos, retain: true}, function (err) {
+    self.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, msg, {qos: self.pubQos, retain: true}, function (err) {
         if (err) {
             if (retries === 0) {
                 setTimeout(self._publishWithRetries, constants.mqtt.retryInterval, self, attr, value, dedupeId, retries - 1);
@@ -401,7 +401,7 @@ MQTTRegistry.prototype.publish = function(self, attr, value) {
  * Helper for "un"-publishing an attribute
  */
 MQTTRegistry.prototype._unpublishWithRetries = function(self, attr, retries) {
-    self.client.publish(self.app + '/' + self.machType + '/' + self.id + '/' + attr, null, {qos: self.pubQos, retain: true}, function (err) {
+    self.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, null, {qos: self.pubQos, retain: true}, function (err) {
         if (err) {
             if (retries > 0) {
                 setTimeout(self._unpublishWithRetries, constants.mqtt.retryInterval, self, attr, retries - 1);
@@ -592,7 +592,7 @@ MQTTRegistry.prototype.quit = function() {
         // unpublish on all attributes
         this.removeAttributes(this.publishedAttrs);
         // unpublish status
-        this.client.publish(this.app + '/' + this.machType + '/' + this.id + '/status',
+        this.client.publish(this.app + '/' + this.type + '/' + this.id + '/status',
                             null, {qos: this.pubQos, retain: true});
         // end connection
         this.client.end(false);

From b8f8ae6cb50d94f5b0aad413496ba17e2adfa449 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 4 Jun 2018 12:26:46 -0400
Subject: [PATCH 13/62] JDISC: More var name changes...

---
 lib/jdiscovery/mqttregistry.js | 20 ++++++++++----------
 lib/jdiscovery/registry.js     |  4 ++--
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 8ccdcb50..a14ea974 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -150,13 +150,13 @@ MQTTRegistry.prototype._prepareForEvents = function() {
         // if the message is an empty string, then this is the result of an "unpublish".
         // i.e. another node unpublished an attribute and we are now being notified.
         if (message.toString() === '') {
-            var eventName;
+            var event;
             if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
-                eventName = self.subscribedAttrs[type][attr].onRemove;
+                event = self.subscribedAttrs[type][attr].onRemove;
             }
 
-            if (eventName !== undefined) {
-                self.emit('attr-removed', attr, eventName, id);
+            if (event !== undefined) {
+                self.emit('attr-removed', attr, event, id);
             } else {
                 console.log('Message received yet we are not subscribed to this message, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
             }
@@ -181,23 +181,23 @@ MQTTRegistry.prototype._prepareForEvents = function() {
  * the message corresponds to and executes the appropriate action.
  */
 MQTTRegistry.prototype._handleMessage = function(self, type, id, attr, payload, dedupeId) {
-    var eventName;
+    var event;
     if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
         if (attr === 'status') {
             if (payload === 'offline') {
-                eventName = self.subscribedAttrs[type].status.offline;
+                event = self.subscribedAttrs[type].status.offline;
                 // pass a dedupeId of zero for node down events
                 dedupeId = 0;
             } else {
-                eventName = self.subscribedAttrs[type].status.online;
+                event = self.subscribedAttrs[type].status.online;
             }
         } else {
-            eventName = self.subscribedAttrs[type][attr].onAdd;
+            event = self.subscribedAttrs[type][attr].onAdd;
         }
     }
 
-    if (eventName !== undefined) {
-        self.emit('discovery', attr, eventName, id, payload, dedupeId);
+    if (event !== undefined) {
+        self.emit('discovery', attr, event, id, payload, dedupeId);
     } else {
         console.log('Message received yet we are not subscribed to this message channel, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
     }
diff --git a/lib/jdiscovery/registry.js b/lib/jdiscovery/registry.js
index 2cf9b31c..57eabebc 100644
--- a/lib/jdiscovery/registry.js
+++ b/lib/jdiscovery/registry.js
@@ -5,9 +5,9 @@
 var EventEmitter = require('events').EventEmitter,
     constants = require('../jamserver/constants');
 
-function Registry(app, machType, id, port) {
+function Registry(app, type, id, port) {
     this.app = app;
-    this.machType = machType;
+    this.type = type;
     this.id = id;
     this.port = port;
 }

From 01fea92bba3d6a7d4c2e653e10b24bfdd204075d Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 4 Jun 2018 12:46:25 -0400
Subject: [PATCH 14/62] JDISC: Added dattrs format conversion in jregistar

---
 lib/jdiscovery/jregistrar.js | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 9d1bab65..b7d168be 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -268,19 +268,26 @@ Registrar.prototype.removeAttributes = function(attrs) {
  *    }
  * (b) As a shortcut for all, one can simply pass an object of <attr, event> pairs
  */
-Registrar.prototype.discoverAttributes = function(attrs) {
-    this._modifyAttributes("discoverAttributes", attrs, this._getSeqVal());
+Registrar.prototype.discoverAttributes = function(dattrs) {
+    dattrs = this._formatDattrs(dattrs);
+    this._modifyAttributes("discoverAttributes", dattrs, this._getSeqVal());
 }
-Registrar.prototype.stopDiscoveringAttributes = function(attrs) {
-    this._modifyAttributes("stopDiscoveringAttributes", attrs, this._getSeqVal());
+Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
+    dattrs = this._formatDattrs(dattrs);
+    this._modifyAttributes("stopDiscoveringAttributes", dattrs, this._getSeqVal());
+}
+Registar.prototype._formatDattrs = function(dattrs) {
+    if(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud)
+        return dattrs;
+    return { 'all' : dattrs };
 }
 
-Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
+Registrar.prototype._modifyAttributes = function(fun, xattrs, seqval) {
     if (this.mqttRegistry) {
-        this.mqttRegistry[fun](attrs, seqval);
+        this.mqttRegistry[fun](xattrs, seqval);
     }
     if (this.mdnsRegistry) {
-        this.mdnsRegistry[fun](attrs, seqval);
+        this.mdnsRegistry[fun](xattrs, seqval);
     }
 }
 

From 4b69e3b4c6faddcf2cf57a9616f4acdb2ab45409 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 4 Jun 2018 13:48:59 -0400
Subject: [PATCH 15/62] JDISC: jregistar: Fixed seqval use error, refactoring

---
 lib/jdiscovery/jregistrar.js | 50 ++++++++++++++++++++----------------
 1 file changed, 28 insertions(+), 22 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index b7d168be..c9ff3881 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -70,6 +70,7 @@ function Registrar(app, type, id, port, config) {
      */
     var noProtocols = true;
 
+    this.mqttRegistry = null;
     if (!config || !config.protocols || config.protocols.mqtt) {
         // QoS |1| for MQTT: Message delivery is __AT_LEAST_ONCE__
         var subQos = 1,
@@ -77,6 +78,7 @@ function Registrar(app, type, id, port, config) {
         this.mqttRegistry = new MQTTRegistry(app, type, id, port, subQos, pubQos);
         noProtocols = false;
     }
+    this.mdnsRegistry = null;
     if (!config || !config.protocols || config.protocols.mdns) {
         this.mdnsRegistry = new MDNSRegistry(app, type, id, port);
         noProtocols = false;
@@ -89,7 +91,7 @@ function Registrar(app, type, id, port, config) {
      * Set up default attributes, which are the same for devices, fogs, and clouds.
      * The only default attribute is 'status'.
      */
-    this.addAttributes({
+    this.setAttributes({
         status: function() {
             return {
                 port: port,
@@ -172,8 +174,8 @@ function Registrar(app, type, id, port, config) {
             self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MQTT);
         });
 
-        this.mqttRegistry.on('attr-removed', function(attr, event, id) {
-            self._respondToAttrRemovalEvent.call(self, attr, event, id, constants.globals.Protocol.MQTT);
+        this.mqttRegistry.on('attr-removed', function(attr, event, id, seqval) {
+            self._respondToRemovalEvent.call(self, attr, event, id, seqval, constants.globals.Protocol.MQTT);
         });
     }
 
@@ -203,8 +205,8 @@ function Registrar(app, type, id, port, config) {
             self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MDNS);
         });
 
-        this.mdnsRegistry.on('attr-removed', function(attr, event, id) {
-            self._respondToAttrRemovalEvent.call(self, attr, event, id, constants.globals.Protocol.MDNS);
+        this.mdnsRegistry.on('attr-removed', function(attr, event, id, seqval) {
+            self._respondToRemovalEvent.call(self, attr, event, id, seqval, constants.globals.Protocol.MDNS);
         });
     }
 }
@@ -241,7 +243,7 @@ Registrar.prototype.registerAndDiscover = function(options) {
         }
     }
 
-    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x.registerAndDiscover);
+    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x.registerAndDiscover());
     this.started = true;
 }
 
@@ -255,6 +257,9 @@ Registrar.prototype.setAttributes = function(attrs) {
 Registrar.prototype.removeAttributes = function(attrs) {
     this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
+Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
+    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x[fun](attrs, seqval));
+}
 
 /**
  * Specify attributes to be discovered.
@@ -269,28 +274,23 @@ Registrar.prototype.removeAttributes = function(attrs) {
  * (b) As a shortcut for all, one can simply pass an object of <attr, event> pairs
  */
 Registrar.prototype.discoverAttributes = function(dattrs) {
-    dattrs = this._formatDattrs(dattrs);
-    this._modifyAttributes("discoverAttributes", dattrs, this._getSeqVal());
+    dattrs = this._formatDattributes(dattrs);
+    this._modifyDattributes("discoverAttributes", dattrs);
 }
 Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
-    dattrs = this._formatDattrs(dattrs);
-    this._modifyAttributes("stopDiscoveringAttributes", dattrs, this._getSeqVal());
+    dattrs = this._formatDattributes(dattrs);
+    this._modifyDattributes("stopDiscoveringAttributes", dattrs);
 }
-Registar.prototype._formatDattrs = function(dattrs) {
+Registar.prototype._formatDattributes = function(dattrs) {
     if(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud)
         return dattrs;
     return { 'all' : dattrs };
 }
-
-Registrar.prototype._modifyAttributes = function(fun, xattrs, seqval) {
-    if (this.mqttRegistry) {
-        this.mqttRegistry[fun](xattrs, seqval);
-    }
-    if (this.mdnsRegistry) {
-        this.mdnsRegistry[fun](xattrs, seqval);
-    }
+Registrar.prototype._modifyDattributes = function(fun, dattrs) {
+    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x[fun](dattrs));
 }
 
+
 //==============================================================================
 // Helpers
 //==============================================================================
@@ -343,13 +343,19 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
 /**
  * Upon receipt of an attribute removal event, pass it onto the rest of the application
  */
-Registrar.prototype._respondToAttrRemovalEvent = function(attr, event, id, protocol) {
+Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, protocol) {
     if (  !this.dt.hasOwnProperty(id)
        || !this.dt.id.hasOwnProperty(attr))
         return;
 
-    delete this.dt.id.attr;
-    this.emit(event, id, protocol);
+    if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
+       || this.dt[id]['attrs'][attr]['seqval'] < seqval
+       || (  (this.dt[id]['attrs'][attr]['seqval'] > (Number.MAX_SAFE_INTEGER/2))
+          && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
+    {
+        delete this.dt.id.attr;
+        this.emit(event, id, protocol);
+    }
 }
 
 /**

From 2d72ad23b603682f7d780d168b91bde19eff3a66 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 5 Jun 2018 10:09:31 -0400
Subject: [PATCH 16/62] JDISC: Fixed format marshalling for dattrs

---
 lib/jdiscovery/jregistrar.js | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index c9ff3881..95bd4c21 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -263,7 +263,7 @@ Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
 
 /**
  * Specify attributes to be discovered.
- * attrs can have one of the following forms:
+ * dattrs can have one of the following forms:
  * (a)
  *    {
  *        all: {attr: event}, // discover these attributes for all nodes
@@ -281,10 +281,24 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
     dattrs = this._formatDattributes(dattrs);
     this._modifyDattributes("stopDiscoveringAttributes", dattrs);
 }
+/**
+ * Marshall dattrs to the following format expected by the registries
+ * { 'device' : {...}, 'fog' : {...}, 'cloud' : {...} }
+ */
 Registar.prototype._formatDattributes = function(dattrs) {
-    if(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud)
-        return dattrs;
-    return { 'all' : dattrs };
+    if(!(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud))
+        dattrs = { 'all' : dattrs };
+    ['device', 'fog', 'cloud'].map(
+        x => {
+            if(!dattrs[x]) dattrs[x] = {};
+            // add all attrs in 'all' to each machine type
+            if(dattrs.all)
+                Object.assign(dattrs[x], dattrs.all);
+        }
+    )
+    if(dattrs.all)
+        delete dattrs.all;
+    return dattrs;
 }
 Registrar.prototype._modifyDattributes = function(fun, dattrs) {
     [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x[fun](dattrs));

From a225b92d9d145befc8fbba4c32823a470c8c3bec Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 5 Jun 2018 12:31:03 -0400
Subject: [PATCH 17/62] JDISC: Refactoring MQTTreg, jreg and more...

---
 lib/jdiscovery/jregistrar.js   | 121 +++++++-------------
 lib/jdiscovery/mdnsregistry.js |  18 +++
 lib/jdiscovery/mqttregistry.js | 201 +++++++++++++++------------------
 3 files changed, 145 insertions(+), 195 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 95bd4c21..0334593a 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -91,22 +91,21 @@ function Registrar(app, type, id, port, config) {
      * Set up default attributes, which are the same for devices, fogs, and clouds.
      * The only default attribute is 'status'.
      */
-    this.setAttributes({
-        status: function() {
-            return {
-                port: port,
-                ip: this._getIPv4Address()
-            };
+    this.setAttributes(
+    {
+        status: {
+            port: port,
+            ip: this._getIPv4Address()
         }
-    }, true);
+    });
 
     /**
      * Prep the default discoveries to be made by a node:
-     * devices discover fogs and fogs discover clouds.
+     * devices discover (fogs, clouds) and fogs discover clouds.
      */
     if (this.type === globals.NodeType.DEVICE) {
         // default discoveries:
-        // devices discover fogs
+        // devices discover fogs, clouds
         this.discoverAttributes({
             fog: {
                 status: {
@@ -137,78 +136,23 @@ function Registrar(app, type, id, port, config) {
         // If this node is a cloud, then it discovers nothing by default!
     }
 
-    // listen for events from the Registries
-    var self = this;
-
-    if (this.mqttRegistry) {
-        /**
-         * MQTT SPECIFIC ERRORS
-         */ 
-        this.mqttRegistry.on('sub-error', function(attrs) {
-            setTimeout(self.mqttRegistry.subscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
-        });
-
-        this.mqttRegistry.on('subs-denied', function(attrs) {
-            var err = new Error('MQTT subscriptions denied');
-            err.name = 'permissions_err';
-            err.value = attrs;
-            self.emit('error', err);
-        });
-
-        this.mqttRegistry.on('unsub-error', function(attrs) {
-            setTimeout(self.mqttRegistry.unsubscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
-        });
-
-        this.mqttRegistry.on('pub-error', function(attr, value) {
-            setTimeout(self.mqttRegistry.publish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr, value);
-        });
-
-        this.mqttRegistry.on('unpub-error', function(attr) {
-            setTimeout(self.mqttRegistry.unpublish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr);
-        });
-
-        /**
-         * MQTT PROPAGATABLE EVENTS
-         */
-        this.mqttRegistry.on('discovery', function(attr, event, id, data, seqval) {
-            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MQTT);
-        });
-
-        this.mqttRegistry.on('attr-removed', function(attr, event, id, seqval) {
-            self._respondToRemovalEvent.call(self, attr, event, id, seqval, constants.globals.Protocol.MQTT);
-        });
-    }
-
-    if (this.mdnsRegistry) {
-        /**
-         * mDNS SPECIFIC ERRORS
-         */ 
-        this.mdnsRegistry.on('ad-error', function(attr, adName, txtRecord) {
-            // an ad failed - try again after some time
-            setTimeout(self.mdnsRegistry._createAdvertisementWithRetries, constants.mdns.longRetryInterval,
-                        self.mdnsRegistry, attr, adName, txtRecord, 0);
-        });
-
-        this.mdnsRegistry.on('browser-error', function(attr, type, events) {
-            // a browser failed - try again after some time
-            if (attr === 'status') {
-                setTimeout(self.mdnsRegistry._browseForStatus, constants.mdns.longRetryInterval, self.mdnsRegistry, type, events);
-            } else {
-                setTimeout(self.mdnsRegistry._browse, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, type, events);
-            }
+    /**
+     * SET PROPAGATABLE EVENTS
+     */
+    [this.mqttRegistry, this.mdnsRegistry].filter(x => x).map(x =>
+    {
+        let self = this;
+        x.on('error', function(info) {
+            self.emit('error', info);
         });
-
-        /**
-         * mDNS PROPAGATABLE EVENTS
-         */ 
-        this.mdnsRegistry.on('discovery', function(attr, event, id, data, seqval) {
-            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, constants.globals.Protocol.MDNS);
+        x.on('discovery', function(attr, event, id, data, seqval) {
+            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, x.protocol);
         });
 
-        this.mdnsRegistry.on('attr-removed', function(attr, event, id, seqval) {
-            self._respondToRemovalEvent.call(self, attr, event, id, seqval, constants.globals.Protocol.MDNS);
+        x.on('attr-removed', function(attr, event, id, seqval) {
+            self._respondToRemovalEvent.call(self, attr, event, id, seqval, x.protocol);
         });
-    }
+    });
 }
 
 /* Registrar inherits from EventEmitter */
@@ -266,12 +210,23 @@ Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
  * dattrs can have one of the following forms:
  * (a)
  *    {
- *        all: {attr: event}, // discover these attributes for all nodes
- *        device: {attr: event}, // discover these attributes just for devices
- *        fog: {attr: event}, // discover these attributes just for fogs
- *        cloud: {attr: event} // discover these attributes just for clouds
+ *        all: {attr: event, ...}, // discover these attributes for all nodes
+ *        device: {attr: event, ...}, // discover these attributes just for devices
+ *        fog: {attr: event, ...}, // discover these attributes just for fogs
+ *        cloud: {attr: event, ...} // discover these attributes just for clouds
  *    }
- * (b) As a shortcut for all, one can simply pass an object of <attr, event> pairs
+ * (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
+ *
+ * For the status attribute, the format is:
+ *      status: {
+ *          online: 'fog-up',
+ *          offline: 'fog-down'
+ *      }
+ * Whereas for custom attributes, the format is:
+ *      is_a_phone: {
+ *          onAdd: 'phone-found'
+ *          onRemove: 'phone-lost'
+ *      }
  */
 Registrar.prototype.discoverAttributes = function(dattrs) {
     dattrs = this._formatDattributes(dattrs);
@@ -282,7 +237,7 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
     this._modifyDattributes("stopDiscoveringAttributes", dattrs);
 }
 /**
- * Marshall dattrs to the following format expected by the registries
+ * Marshall dattrs to the following format (expected by the registries)
  * { 'device' : {...}, 'fog' : {...}, 'cloud' : {...} }
  */
 Registar.prototype._formatDattributes = function(dattrs) {
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 8dba2d93..ed239888 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -16,6 +16,7 @@ var sequence = [
 
 function MDNSRegistry(app, machType, id, port) {
     Registry.call(this, app, machType, id, port);
+    this.protocol = constants.globals.Protocol.MDNS;
     this.ads = {};
     this.browsers = {
         device: {},
@@ -47,6 +48,23 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
     this.discoverAttributes(this.attrsToDiscover);
 }
 
+/* TO HANDLE INTERNALLY TODO
+        this.mdnsRegistry.on('ad-error', function(attr, adName, txtRecord) {
+            // an ad failed - try again after some time
+            setTimeout(self.mdnsRegistry._createAdvertisementWithRetries, constants.mdns.longRetryInterval,
+                        self.mdnsRegistry, attr, adName, txtRecord, 0);
+        });
+
+        this.mdnsRegistry.on('browser-error', function(attr, type, events) {
+            // a browser failed - try again after some time
+            if (attr === 'status') {
+                setTimeout(self.mdnsRegistry._browseForStatus, constants.mdns.longRetryInterval, self.mdnsRegistry, type, events);
+            } else {
+                setTimeout(self.mdnsRegistry._browse, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, type, events);
+            }
+        });
+*/
+
 //------------------------------------------------------------------------------
 // Advertisement creation
 //------------------------------------------------------------------------------
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index a14ea974..76018bf2 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -9,6 +9,7 @@ var mqtt = require('mqtt'),
 
 function MQTTRegistry(app, type, id, port, subQos, pubQos) {
     Registry.call(this, app, type, id, port);
+    this.protocol = constants.globals.Protocol.MQTT; 
     // the quality of service to use for subscriptions
     this.subQos = subQos;
     // the quality of service to use for publications
@@ -25,25 +26,6 @@ function MQTTRegistry(app, type, id, port, subQos, pubQos) {
         fog: {},
         cloud: {}
     };
-    /**
-     * Attributes to be published on (re)connection. A map from attribute name
-     * to { payload: attribute_value, dedupeId: deduplication_id } objects.
-     */
-    this.attrsToPublish = {};
-    // attributes to remove when reconnecting
-    this.attrsToRemove = {};
-    // attributes to subscribe to on (re)connection
-    this.attrsToSubTo = {
-        device: {},
-        fog: {},
-        cloud: {}
-    };
-    // attributes to unsubscribe from on reconnection
-    this.attrsToUnsubFrom = {
-        device: {},
-        fog: {},
-        cloud: {}
-    };
 }
 
 /* MQTTRegistry inherits from Registry */
@@ -98,45 +80,23 @@ MQTTRegistry.prototype._prepareForEvents = function() {
     this.client.on('connect', function (connack) {
 
 		/*
-		 * session is not present - subscriptions start from scratch but
-		 * old publications still persist on the broker
+		 * session is not present - subscriptions start from scratch
+		 * publications persist across connections on the broker (retain flag)
 		 */
-		// reset attrsToUnsubFrom
-		self.attrsToUnsubFrom = {
-			device: {},
-			fog: {},
-			cloud: {}
-		};
-		// ...SUBSCRIBE...
-		// combine subscribedAttrs and attrsToSubTo so that we can subscribe to all of them
-		for (var attr in self.subscribedAttrs.device) {
-			self.attrsToSubTo.device[attr] = self.subscribedAttrs.device[attr];
-		}
-		for (var attr in self.subscribedAttrs.fog) {
-			self.attrsToSubTo.fog[attr] = self.subscribedAttrs.fog[attr];
-		}
-		for (var attr in self.subscribedAttrs.cloud) {
-			self.attrsToSubTo.cloud[attr] = self.subscribedAttrs.cloud[attr];
-		}
-		self.subscribedAttrs = {
-			device: {},
-			fog: {},
-			cloud: {}
-		};
-		self._subscribeWithRetries(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
-
-        // ...PUBLISH...
-        for (var attr in self.publishedAttrs) {
-            self.attrsToPublish[attr] = self.publishedAttrs[attr];
-        }
-        self.publishedAttrs = {};
+
+        /*...SUBSCRIBE...*/
+		self._subscribeWithRetries.call(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
+
+        /* XXX We aren't actually republishing anything ;) 
+        ...PUBLISH...
         for (var attr in self.attrsToPublish) {
             self._publishWithRetries(self, attr, self.attrsToPublish[attr].payload, self.attrsToPublish[attr].dedupeId, constants.mqtt.retries);
         }
-        // ...UNPUBLISH...
+        ...UNPUBLISH...
         for (var attr in self.attrsToRemove) {
             self._unpublishWithRetries(self, attr, constants.mqtt.retries);
         }
+        */
     });
 
     /* message event received when client receives a published packet */
@@ -147,24 +107,9 @@ MQTTRegistry.prototype._prepareForEvents = function() {
         var id = components[2];
         var attr = components[3];
 
-        // if the message is an empty string, then this is the result of an "unpublish".
-        // i.e. another node unpublished an attribute and we are now being notified.
-        if (message.toString() === '') {
-            var event;
-            if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
-                event = self.subscribedAttrs[type][attr].onRemove;
-            }
-
-            if (event !== undefined) {
-                self.emit('attr-removed', attr, event, id);
-            } else {
-                console.log('Message received yet we are not subscribed to this message, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
-            }
-            return;
-        }
-
-        const parsedMsg = JSON.parse(message.toString());
-        self._handleMessage(self, type, id, attr, parsedMsg.payload, parsedMsg.id);
+        // if the message payload is empty, this is the result of an "unpublish".
+        const parsed = JSON.parse(message.toString());
+        self._handleMessage.call(self, type, id, attr, parsed.payload, parsed.id);
     });
 
     this.client.on('offline', function () {
@@ -176,50 +121,81 @@ MQTTRegistry.prototype._prepareForEvents = function() {
     });
 }
 
+/*
+        this.mqttRegistry.on('sub-error', function(attrs) {
+            setTimeout(self.mqttRegistry.subscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
+        });
+
+        this.mqttRegistry.on('subs-denied', function(attrs) {
+            var err = new Error('MQTT subscriptions denied');
+            err.name = 'permissions_err';
+            err.value = attrs;
+            self.emit('error', err);
+        });
+
+        this.mqttRegistry.on('unsub-error', function(attrs) {
+            setTimeout(self.mqttRegistry.unsubscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
+        });
+
+        this.mqttRegistry.on('pub-error', function(attr, value) {
+            setTimeout(self.mqttRegistry.publish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr, value);
+        });
+
+        this.mqttRegistry.on('unpub-error', function(attr) {
+            setTimeout(self.mqttRegistry.unpublish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr);
+        });
+*/
+
 /**
  * Handles receipt of a message from the MQTT broker. Finds the subscription that
  * the message corresponds to and executes the appropriate action.
  */
-MQTTRegistry.prototype._handleMessage = function(self, type, id, attr, payload, dedupeId) {
+MQTTRegistry.prototype._handleMessage = function(type, id, attr, data, seqval) {
+
     var event;
-    if (self.subscribedAttrs[type].hasOwnProperty(attr)) {
+
+    // payload empty <=> unpublish on attr
+    if (data === '') 
+    {
+        if (this.subscribedAttrs[type].hasOwnProperty(attr)) {
+            event = this.subscribedAttrs[type][attr].onRemove;
+        }
+        if (event !== undefined)
+            this.emit('attr-removed', attr, event, id, seqval);
+    } 
+    else if (this.subscribedAttrs[type].hasOwnProperty(attr)) 
+    {
         if (attr === 'status') {
             if (payload === 'offline') {
-                event = self.subscribedAttrs[type].status.offline;
-                // pass a dedupeId of zero for node down events
-                dedupeId = 0;
+                event = this.subscribedAttrs[type].status.offline;
             } else {
-                event = self.subscribedAttrs[type].status.online;
+                event = this.subscribedAttrs[type].status.online;
             }
         } else {
-            event = self.subscribedAttrs[type][attr].onAdd;
+            event = this.subscribedAttrs[type][attr].onAdd;
         }
-    }
-
-    if (event !== undefined) {
-        self.emit('discovery', attr, event, id, payload, dedupeId);
-    } else {
-        console.log('Message received yet we are not subscribed to this message channel, or shouldn\'t be... This should not be because we are still awaiting a subscription confirmation for this message, as this issue has been fixed. If you ever see this message, then something is probably wrong.');
+        if (event !== undefined)
+            this.emit('discovery', attr, event, id, data, seqval);
     }
 }
 
 /**
  * Helper for setting up subscriptions to the broker with retries
  */
-MQTTRegistry.prototype._subscribeWithRetries = function(self, dattrs, retries) {
+MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
     // format subscriptions to be sent to the broker
     var subs = {};
 
     for (var attr in dattrs.device) {
-        subs[self.app + '/device/+/' + attr] = self.subQos;
+        subs[this.app + '/device/+/' + attr] = this.subQos;
     }
 
     for (var attr in dattrs.fog) {
-        subs[self.app + '/fog/+/' + attr] = self.subQos;
+        subs[this.app + '/fog/+/' + attr] = this.subQos;
     }
 
     for (var attr in dattrs.cloud) {
-        subs[self.app + '/cloud/+/' + attr] = self.subQos;
+        subs[this.app + '/cloud/+/' + attr] = this.subQos;
     }
 
     if (Object.keys(subs).length === 0) {
@@ -228,42 +204,43 @@ MQTTRegistry.prototype._subscribeWithRetries = function(self, dattrs, retries) {
 
     // optimistically move these subscriptions from attrsToSubTo to subscribedAttrs
     for (var attr in dattrs.device) {
-        delete self.attrsToSubTo.device[attr];
-        self.subscribedAttrs.device[attr] = dattrs.device[attr];
+        delete this.attrsToSubTo.device[attr];
+        this.subscribedAttrs.device[attr] = dattrs.device[attr];
     }
 
     for (var attr in dattrs.fog) {
-        delete self.attrsToSubTo.fog[attr];
-        self.subscribedAttrs.fog[attr] = dattrs.fog[attr];
+        delete this.attrsToSubTo.fog[attr];
+        this.subscribedAttrs.fog[attr] = dattrs.fog[attr];
     }
 
     for (var attr in dattrs.cloud) {
-        delete self.attrsToSubTo.cloud[attr];
-        self.subscribedAttrs.cloud[attr] = dattrs.cloud[attr];
+        delete this.attrsToSubTo.cloud[attr];
+        this.subscribedAttrs.cloud[attr] = dattrs.cloud[attr];
     }
 
     // perform subscriptions
-    self.client.subscribe(subs, function (err, granted) {
+    this.client.subscribe(subs, function (err, granted) {
         if (err) {
             if (retries !== 0) {
-                setTimeout(self._subscribeWithRetries, constants.mqtt.retryInterval, self, dattrs, retries - 1);
+                setTimeout(this._subscribeWithRetries.bind(this), 
+                    constants.mqtt.retryInterval, this, dattrs, retries - 1);
             } else {
                 // move all attributes back to attrsToSubTo and emit an error
                 for (var attr in dattrs.device) {
-                    delete self.subscribedAttrs.device[attr];
-                    self.attrsToSubTo.device[attr] = dattrs.device[attr];
+                    delete this.subscribedAttrs.device[attr];
+                    this.attrsToSubTo.device[attr] = dattrs.device[attr];
                 }
 
                 for (var attr in dattrs.fog) {
-                    delete self.subscribedAttrs.fog[attr];
-                    self.attrsToSubTo.fog[attr] = dattrs.fog[attr];
+                    delete this.subscribedAttrs.fog[attr];
+                    this.attrsToSubTo.fog[attr] = dattrs.fog[attr];
                 }
 
                 for (var attr in dattrs.cloud) {
-                    delete self.subscribedAttrs.cloud[attr];
-                    self.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
+                    delete this.subscribedAttrs.cloud[attr];
+                    this.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
                 }
-                self.emit('sub-error', dattrs);
+                this.emit('sub-error', dattrs);
             }
         } else {
             // move any attrs that were denied from subscribedAttrs to attrsToSubTo and
@@ -277,33 +254,33 @@ MQTTRegistry.prototype._subscribeWithRetries = function(self, dattrs, retries) {
             }
 
             for (var attr in dattrs.device) {
-                delete self.subscribedAttrs.device[attr];
-                self.attrsToSubTo.device[attr] = dattrs.device[attr];
+                delete this.subscribedAttrs.device[attr];
+                this.attrsToSubTo.device[attr] = dattrs.device[attr];
             }
 
             for (var attr in dattrs.fog) {
-                delete self.subscribedAttrs.fog[attr];
-                self.attrsToSubTo.fog[attr] = dattrs.fog[attr];
+                delete this.subscribedAttrs.fog[attr];
+                this.attrsToSubTo.fog[attr] = dattrs.fog[attr];
             }
 
             for (var attr in dattrs.cloud) {
-                delete self.subscribedAttrs.cloud[attr];
-                self.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
+                delete this.subscribedAttrs.cloud[attr];
+                this.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
             }
 
             // report any subscriptions that weren't granted
             if (Object.keys(dattrs.device).length !== 0 ||
                 Object.keys(dattrs.fog).length !== 0 ||
                 Object.keys(dattrs.cloud).length !== 0) {
-                    self.emit('subs-denied', dattrs);
+                    this.emit('subs-denied', dattrs);
             }
         }
     });
 }
 
-MQTTRegistry.prototype.subscribe = function(self, dattrs) {
-    if (self.client && self.client.connected) {
-        self._subscribeWithRetries(self, dattrs, constants.mqtt.retries);
+MQTTRegistry.prototype.subscribe = function(dattrs) {
+    if (this.client && this.client.connected) {
+        this._subscribeWithRetries.call(this, dattrs, constants.mqtt.retries);
     }
     // if we're disconnected, then we'll automatically try to subscribe to the attributes when we connect
 }
@@ -518,7 +495,7 @@ MQTTRegistry.prototype.discoverAttributes = function(dattrs) {
     }
 
     if (subs !== null && this.client && this.client.connected) {
-        this._subscribeWithRetries(this, subs, constants.mqtt.retries);
+        this._subscribeWithRetries.call(this, subs, constants.mqtt.retries);
     }
 }
 

From e59ee80980312460822bd58a9ed44007b61fd15e Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 5 Jun 2018 17:12:17 -0400
Subject: [PATCH 18/62] JDISC: Update mqtt registry behaviour and impl (in
 progress)

---
 lib/jdiscovery/mqttregistry.js | 475 ++++++++-------------------------
 1 file changed, 115 insertions(+), 360 deletions(-)

diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 76018bf2..47ac854d 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -1,7 +1,3 @@
-//==============================================================================
-// Registers a node on the network using MQTT
-//==============================================================================
-
 var mqtt = require('mqtt'),
     logger = require('../jamserver/jerrlog.js'),
     constants = require('../jamserver/constants'),
@@ -39,64 +35,15 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
     // create an mqtt client
     this.client = mqtt.connect(constants.mqtt.brokerUrl, this._getConnectionOptions());
 
-    // set up event listeners for the client
-    this._prepareForEvents();
-}
-
-/**
- * Returns connection options to the mqtt broker contingent upon the connecting node
- * takes as arguments the name of the application, the type of the machine, and the
- * id of the machine
- * CleanSession: true
- * Last Will (LWT): (retain: true)
- */
-MQTTRegistry.prototype._getConnectionOptions = function() {
-
-    // create the will
-    var will = {
-        topic: this.app + '/' + this.type + '/' + this.id + '/status',
-        payload: JSON.stringify({ payload: 'offline' }),
-        qos: this.pubQos,
-        retain: true
-    }
-
-    // set and return the connection options
-    return {
-        clientId: this.id,
-        keepalive: constants.mqtt.keepAlive,
-        clean: true,
-        connectTimeout: constants.mqtt.connectionTimeout,
-        will: will
-    };
-}
-
-/**
- * A general helper for listening for events from the MQTT client
- */
-MQTTRegistry.prototype._prepareForEvents = function() {
     var self = this;
 
     /* connect event emitted on successful connection or reconnection */
     this.client.on('connect', function (connack) {
-
 		/*
 		 * session is not present - subscriptions start from scratch
 		 * publications persist across connections on the broker (retain flag)
 		 */
-
-        /*...SUBSCRIBE...*/
 		self._subscribeWithRetries.call(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
-
-        /* XXX We aren't actually republishing anything ;) 
-        ...PUBLISH...
-        for (var attr in self.attrsToPublish) {
-            self._publishWithRetries(self, attr, self.attrsToPublish[attr].payload, self.attrsToPublish[attr].dedupeId, constants.mqtt.retries);
-        }
-        ...UNPUBLISH...
-        for (var attr in self.attrsToRemove) {
-            self._unpublishWithRetries(self, attr, constants.mqtt.retries);
-        }
-        */
     });
 
     /* message event received when client receives a published packet */
@@ -120,31 +67,32 @@ MQTTRegistry.prototype._prepareForEvents = function() {
         console.log('WARNING: mqtt client received an error');
     });
 }
+/**
+ * Returns connection options to the mqtt broker contingent upon the connecting node
+ * takes as arguments the name of the application, the type of the machine, and the
+ * id of the machine
+ * CleanSession: true
+ * Last Will (LWT): (retain: true)
+ */
+MQTTRegistry.prototype._getConnectionOptions = function() {
 
-/*
-        this.mqttRegistry.on('sub-error', function(attrs) {
-            setTimeout(self.mqttRegistry.subscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
-        });
-
-        this.mqttRegistry.on('subs-denied', function(attrs) {
-            var err = new Error('MQTT subscriptions denied');
-            err.name = 'permissions_err';
-            err.value = attrs;
-            self.emit('error', err);
-        });
-
-        this.mqttRegistry.on('unsub-error', function(attrs) {
-            setTimeout(self.mqttRegistry.unsubscribe, constants.mqtt.longRetryInterval, self.mqttRegistry, attrs);
-        });
-
-        this.mqttRegistry.on('pub-error', function(attr, value) {
-            setTimeout(self.mqttRegistry.publish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr, value);
-        });
+    // create the will
+    var will = {
+        topic: this.app + '/' + this.type + '/' + this.id + '/status',
+        payload: JSON.stringify({ payload: 'offline' }),
+        qos: this.pubQos,
+        retain: true
+    }
 
-        this.mqttRegistry.on('unpub-error', function(attr) {
-            setTimeout(self.mqttRegistry.unpublish, constants.mqtt.longRetryInterval, self.mqttRegistry, attr);
-        });
-*/
+    // set and return the connection options
+    return {
+        clientId: this.id,
+        keepalive: constants.mqtt.keepAlive,
+        clean: true,
+        connectTimeout: constants.mqtt.connectionTimeout,
+        will: will
+    };
+}
 
 /**
  * Handles receipt of a message from the MQTT broker. Finds the subscription that
@@ -183,176 +131,93 @@ MQTTRegistry.prototype._handleMessage = function(type, id, attr, data, seqval) {
  * Helper for setting up subscriptions to the broker with retries
  */
 MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
-    // format subscriptions to be sent to the broker
-    var subs = {};
 
-    for (var attr in dattrs.device) {
-        subs[this.app + '/device/+/' + attr] = this.subQos;
-    }
-
-    for (var attr in dattrs.fog) {
-        subs[this.app + '/fog/+/' + attr] = this.subQos;
-    }
-
-    for (var attr in dattrs.cloud) {
-        subs[this.app + '/cloud/+/' + attr] = this.subQos;
+    // create topic strings to be sent to the broker for subscription
+    var topics = [];
+    ['device', 'fog', 'cloud'].map(x =>
+    {
+        for (var attr in dattrs[x]) {
+            topics.push(this.app + '/' + x + '/+/' + attr);
+        }
     }
-
-    if (Object.keys(subs).length === 0) {
+    if (topics.length === 0)
         return;
-    }
-
-    // optimistically move these subscriptions from attrsToSubTo to subscribedAttrs
-    for (var attr in dattrs.device) {
-        delete this.attrsToSubTo.device[attr];
-        this.subscribedAttrs.device[attr] = dattrs.device[attr];
-    }
 
-    for (var attr in dattrs.fog) {
-        delete this.attrsToSubTo.fog[attr];
-        this.subscribedAttrs.fog[attr] = dattrs.fog[attr];
-    }
-
-    for (var attr in dattrs.cloud) {
-        delete this.attrsToSubTo.cloud[attr];
-        this.subscribedAttrs.cloud[attr] = dattrs.cloud[attr];
-    }
+    // add new subscriptions to subscribedAttrs
+    ['device', 'fog', 'cloud'].map(x =>
+    {
+        for (var attr in dattrs[x]) {
+            this.subscribedAttrs[x][attr] = dattrs[x][attr];
+        }
+    });
 
     // perform subscriptions
-    this.client.subscribe(subs, function (err, granted) {
+    var self = this;
+    this.client.subscribe(topics, { qos : this.subQos }, function (err, granted) {
         if (err) {
-            if (retries !== 0) {
-                setTimeout(this._subscribeWithRetries.bind(this), 
-                    constants.mqtt.retryInterval, this, dattrs, retries - 1);
+            // Let -1 signify infinite retries
+            if (retries > 0 || retries == -1) {
+                setTimeout(self._subscribeWithRetries.bind(self),
+                    constants.mqtt.retryInterval, dattrs, (retries === -1) ? -1 : (retries-1));
             } else {
-                // move all attributes back to attrsToSubTo and emit an error
-                for (var attr in dattrs.device) {
-                    delete this.subscribedAttrs.device[attr];
-                    this.attrsToSubTo.device[attr] = dattrs.device[attr];
-                }
-
-                for (var attr in dattrs.fog) {
-                    delete this.subscribedAttrs.fog[attr];
-                    this.attrsToSubTo.fog[attr] = dattrs.fog[attr];
+                granted.map(x =>
+                {
+                    let topic = x.topic.split('/');
+                    let type = topic[1];
+                    let attr = topic[3];
+                    delete dattrs[type][attr]
                 }
-
-                for (var attr in dattrs.cloud) {
-                    delete this.subscribedAttrs.cloud[attr];
-                    this.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
+                ['device', 'fog', 'cloud'].map(x =>
+                {
+                    for (var attr in dattrs[x])
+                        delete self.subscribedAttrs[x][attr];
                 }
-                this.emit('sub-error', dattrs);
-            }
-        } else {
-            // move any attrs that were denied from subscribedAttrs to attrsToSubTo and
-            // emit an event indicating the attributes that were denied
-            var components, type, attr;
-            for (var i = 0; i < granted.length; i++) {
-                components = granted[i].topic.split('/');
-                type = components[1];
-                attr = components[3];
-                delete dattrs[type][attr];
-            }
-
-            for (var attr in dattrs.device) {
-                delete this.subscribedAttrs.device[attr];
-                this.attrsToSubTo.device[attr] = dattrs.device[attr];
-            }
-
-            for (var attr in dattrs.fog) {
-                delete this.subscribedAttrs.fog[attr];
-                this.attrsToSubTo.fog[attr] = dattrs.fog[attr];
-            }
-
-            for (var attr in dattrs.cloud) {
-                delete this.subscribedAttrs.cloud[attr];
-                this.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
-            }
-
-            // report any subscriptions that weren't granted
-            if (Object.keys(dattrs.device).length !== 0 ||
-                Object.keys(dattrs.fog).length !== 0 ||
-                Object.keys(dattrs.cloud).length !== 0) {
-                    this.emit('subs-denied', dattrs);
+                // notify user something fishy is going on, probably an attr name error..
+                console.log('WARNING: MQTT Registry failed to subscribe to some topics');
             }
         }
     });
 }
-
-MQTTRegistry.prototype.subscribe = function(dattrs) {
-    if (this.client && this.client.connected) {
-        this._subscribeWithRetries.call(this, dattrs, constants.mqtt.retries);
-    }
-    // if we're disconnected, then we'll automatically try to subscribe to the attributes when we connect
-}
-
 /**
  * Unsubscribe from a series of topics
  */
-MQTTRegistry.prototype._unsubscribeWithRetries = function(self, dattrs, retries) {
-    var topics = [];
-    for (var attr in dattrs.device) {
-        topics.push(self.app + '/device/+/' + dattrs.device[attr]);
-    }
+MQTTRegistry.prototype._unsubscribeWithRetries = function(dattrs, retries) {
 
-    for (var attr in dattrs.fog) {
-        topics.push(self.app + '/fog/+/' + dattrs.fog[attr]);
-    }
-
-    for (var attr in dattrs.cloud) {
-        topics.push(self.app + '/cloud/+/' + dattrs.cloud[attr]);
+    var topics = [];
+    ['device', 'fog', 'cloud'].map(x =>
+    {
+        for (var attr in dattrs[x]) {
+            topics.push(this.app + '/' + x + '/+/' + attr);
+        }
     }
-
-    if (topics.length === 0) {
+    if (topics.length === 0)
         return;
-    }
 
-    self.client.unsubscribe(topics, function(err) {
+    var self = this;
+    this.client.unsubscribe(topics, function(err) {
         if (err) {
-            if (retries > 0) {
-                setTimeout(self._unsubscribeWithRetries, constants.mqtt.retryInterval, self, topics, retries - 1);
-            } else {
-                self.emit('unsub-error', dattrs);
-            }
+            // Let -1 signify infinite retries
+            if (retries > 0 || retries == -1)
+                setTimeout(self._unsubscribeWithRetries.bind(self),
+                    constants.mqtt.retryInterval, dattrs, (retries === -1) ? -1 : (retries-1));
         } else {
-            for (var attr in dattrs.device) {
-                delete self.subscribedAttrs.device[attr];
-                delete self.attrsToUnsubFrom.device[attr];
-            }
-            for (var attr in dattrs.fog) {
-                delete self.subscribedAttrs.fog[attr];
-                delete self.attrsToUnsubFrom.fog[attr];
-            }
-            for (var attr in dattrs.cloud) {
-                delete self.subscribedAttrs.cloud[attr];
-                delete self.attrsToUnsubFrom.cloud[attr];
+            ['device', 'fog', 'cloud'].map(x =>
+            {
+                for (var attr in dattrs[x])
+                    delete self.subscribedAttrs[x][attr];
             }
         }
     });
 }
-
-MQTTRegistry.prototype.unsubscribe = function(self, dattrs) {
-    if (self.client && self.client.connected) {
-        self._unsubscribeWithRetries(self, dattrs, constants.mqtt.retries);
-    }
-    // if we're disconnected, then we have already been unsubscribed (cleanSession: true)
-}
-
-/**
- * Helper for publishing an attribute with retries.
- * attr - the name of the attribute
- * value - the value of the attribute
- * dedupeId - the ID to publish with the attributes for deduplication purposes on
- *  on the receiving node
- * retries - the number of retries, if publishing fails
- */
-MQTTRegistry.prototype._publishWithRetries = function(self, attr, value, dedupeId, retries) {
+MQTTRegistry.prototype._publishWithRetries = function(attr, value, dedupeId, retries) {
     var msg;
     if (value instanceof Function) {
         msg = JSON.stringify({ payload: value(), id: dedupeId });
     } else {
         msg = JSON.stringify({ payload: value, id: dedupeId });
     }
-    self.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, msg, {qos: self.pubQos, retain: true}, function (err) {
+    var self = this;
+    this.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, msg, {qos: self.pubQos, retain: true}, function (err) {
         if (err) {
             if (retries === 0) {
                 setTimeout(self._publishWithRetries, constants.mqtt.retryInterval, self, attr, value, dedupeId, retries - 1);
@@ -366,18 +231,8 @@ MQTTRegistry.prototype._publishWithRetries = function(self, attr, value, dedupeI
         }
     });
 }
-
-MQTTRegistry.prototype.publish = function(self, attr, value) {
-    if (self.client && self.client.connected) {
-        self._publishWithRetries(self, attr, value, constants.mqtt.retries);
-    }
-    // if we're disconnected, then we'll automatically try to publish the attributes when we connect
-}
-
-/**
- * Helper for "un"-publishing an attribute
- */
-MQTTRegistry.prototype._unpublishWithRetries = function(self, attr, retries) {
+MQTTRegistry.prototype._unpublishWithRetries = function(attr, retries) {
+    var self = this;
     self.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, null, {qos: self.pubQos, retain: true}, function (err) {
         if (err) {
             if (retries > 0) {
@@ -393,168 +248,68 @@ MQTTRegistry.prototype._unpublishWithRetries = function(self, attr, retries) {
     });
 }
 
-MQTTRegistry.prototype.unpublish = function(self, attr) {
-    if (self.client && self.client.connected) {
-        self._unpublishWithRetries(self, attr, constants.mqtt.retries);
+// TODO when client is not connected, what shoud control graph be for these?
+MQTTRegistry.prototype.subscribe = function(dattrs) {
+    if (this.client && this.client.connected) {
+        this._subscribeWithRetries(dattrs, constants.mqtt.retries);
+    }
+}
+MQTTRegistry.prototype.unsubscribe = function(dattrs) {
+    if (this.client && this.client.connected) {
+        this._unsubscribeWithRetries(dattrs, constants.mqtt.retries);
+    }
+}
+MQTTRegistry.prototype.publish = function(attr, value) {
+    if (this.client && this.client.connected) {
+        this._publishWithRetries(attr, value, constants.mqtt.retries);
+    }
+}
+MQTTRegistry.prototype.unpublish = function(attr) {
+    if (this.client && this.client.connected) {
+        this._unpublishWithRetries(attr, constants.mqtt.retries);
     }
-    // if we're disconnected, then we'll automatically try to publish the attributes when we connect
 }
 
-//==============================================================================
-// Custom attribute publication/discovery
-//==============================================================================
+/**
+ * REGISTRY INTERFACE METHODS
+ */
 
 /**
- * Add and publish attributes for this node
+ * set/unset discoverable attributes for this node
  */
-MQTTRegistry.prototype.addAttributes = function(attrs, dedupeId) {
+MQTTRegistry.prototype.setAttributes = function(attrs, seqval) {
     for (var attr in attrs) {
-        // just in case this is in the queue for removal...
-        delete this.attrsToRemove[attr];
         // check that it's not already published
         if (!this.publishedAttrs.hasOwnProperty(attr)) {
-            this.attrsToPublish[attr] = { payload: attrs[attr], dedupeId: dedupeId };
+            this.attrsToPublish[attr] = { 'data': attrs[attr], 'seqval': seqval };
             if (this.client && this.client.connected) {
                 // try to publish the attribute
-                this._publishWithRetries(this, attr, attrs[attr], dedupeId, constants.mqtt.retries);
+                this._publishWithRetries(this, attr, attrs[attr], seqval, constants.mqtt.retries);
             }
         }
     }
 }
-
-/**
- * Unpublishes the given attributes.
- * attrs - an array of the names (strings) of the attributes to remove.
- */
-MQTTRegistry.prototype.removeAttributes = function(attrs) {
+MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
     for (var i = 0; i < attrs.length; i++) {
-        // remove it from attrsToPublish, if need be
-        delete this.attrsToPublish[attrs[i]];
         if (this.publishedAttrs.hasOwnProperty(attrs[i])) {
-            this.attrsToRemove[attrs[i]] = null;
             if (this.client && this.client.connected) {
-                // try to remove it
+                // try to unpublish the attribute
                 this._unpublishWithRetries(this, attrs[i], constants.mqtt.retries);
             }
         }
     }
 }
-
+/**
+ * set/unset attributes discovered by this node
+ */
 MQTTRegistry.prototype.discoverAttributes = function(dattrs) {
-    var subs = null;
-
-    for (var attr in dattrs.device) {
-        // in case this attr is queued up to be unsubscribed from
-        delete this.attrsToUnsubFrom.device[attr];
-        if (!this.subscribedAttrs.device.hasOwnProperty(attr)) {
-            // try to subscribe to it
-            if (subs === null) {
-                subs = {
-                    device: {},
-                    fog: {},
-                    cloud: {}
-                };
-            }
-            subs.device[attr] = dattrs.device[attr];
-            this.attrsToSubTo.device[attr] = dattrs.device[attr];
-        }
-    }
-
-    for (var attr in dattrs.fog) {
-        // in case this attr is queued up to be unsubscribed from
-        delete this.attrsToUnsubFrom.fog[attr];
-        if (!this.subscribedAttrs.fog.hasOwnProperty(attr)) {
-            // try to subscribe to it
-            if (subs === null) {
-                subs = {
-                    device: {},
-                    fog: {},
-                    cloud: {}
-                };
-            }
-            subs.fog[attr] = dattrs.fog[attr];
-            this.attrsToSubTo.fog[attr] = dattrs.fog[attr];
-        }
-    }
-
-    for (var attr in dattrs.cloud) {
-        // in case this attr is queued up to be unsubscribed from
-        delete this.attrsToUnsubFrom.cloud[attr];
-        if (!this.subscribedAttrs.cloud.hasOwnProperty(attr)) {
-            // try to subscribe to it
-            if (subs === null) {
-                subs = {
-                    device: {},
-                    fog: {},
-                    cloud: {}
-                };
-            }
-            subs.cloud[attr] = dattrs.cloud[attr];
-            this.attrsToSubTo.cloud[attr] = dattrs.cloud[attr];
-        }
-    }
-
-    if (subs !== null && this.client && this.client.connected) {
-        this._subscribeWithRetries.call(this, subs, constants.mqtt.retries);
+    if (this.client && this.client.connected) {
+        this._subscribeWithRetries(dattrs, constants.mqtt.retries);
     }
 }
-
 MQTTRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
-    var unsubs = null;
-
-    if (dattrs.device) {
-        for (var i = 0; i < dattrs.device.length; i++) {
-            delete this.attrsToSubTo.device[dattrs.device[i]];
-            if (this.subscribedAttrs.hasOwnProperty(dattrs.device[i])) {
-                this.attrsToUnsubFrom.device[dattrs.device[i]] = null;
-                if (unsubs === null) {
-                    unsubs = {
-                        device: {},
-                        fog: {},
-                        cloud: {}
-                    }
-                }
-                unsubs.device[dattrs.device[i]] = null;
-            }
-        }
-    }
-
-    if (dattrs.fog) {
-        for (var i = 0; i < dattrs.fog.length; i++) {
-            delete this.attrsToSubTo.fog[dattrs.fog[i]];
-            if (this.subscribedAttrs.hasOwnProperty(dattrs.fog[i])) {
-                this.attrsToUnsubFrom.fog[dattrs.fog[i]] = null;
-                if (unsubs === null) {
-                    unsubs = {
-                        device: {},
-                        fog: {},
-                        cloud: {}
-                    }
-                }
-                unsubs.fog[dattrs.fog[i]] = null;
-            }
-        }
-    }
-
-    if (dattrs.cloud) {
-        for (var i = 0; i < dattrs.cloud.length; i++) {
-            delete this.attrsToSubTo.cloud[dattrs.cloud[i]];
-            if (this.subscribedAttrs.hasOwnProperty(dattrs.cloud[i])) {
-                this.attrsToUnsubFrom.cloud[dattrs.cloud[i]] = null;
-                if (unsubs === null) {
-                    unsubs = {
-                        device: {},
-                        fog: {},
-                        cloud: {}
-                    }
-                }
-                unsubs.cloud[dattrs.cloud[i]] = null;
-            }
-        }
-    }
-
-    if (unsubs !== null && this.client && this.client.connected) {
-        this._unsubscribeWithRetries(this, unsubs, constants.mqtt.retries);
+    if (this.client && this.client.connected) {
+        this._unsubscribeWithRetries(unsubs, constants.mqtt.retries);
     }
 }
 

From 59307d6bb427ad9e16d0ca1864e626e26e51e0ba Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 6 Jun 2018 14:31:46 -0400
Subject: [PATCH 19/62] JDISC: MQTTreg overhaul completed

---
 lib/jdiscovery/mqttregistry.js | 299 +++++++++++++++++----------------
 1 file changed, 150 insertions(+), 149 deletions(-)

diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 47ac854d..f1adb48e 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -28,45 +28,6 @@ function MQTTRegistry(app, type, id, port, subQos, pubQos) {
 MQTTRegistry.prototype = Object.create(Registry.prototype);
 MQTTRegistry.prototype.constructor = MQTTRegistry;
 
-/**
- * Performs basic registration and discovery
- */
-MQTTRegistry.prototype.registerAndDiscover = function() {
-    // create an mqtt client
-    this.client = mqtt.connect(constants.mqtt.brokerUrl, this._getConnectionOptions());
-
-    var self = this;
-
-    /* connect event emitted on successful connection or reconnection */
-    this.client.on('connect', function (connack) {
-		/*
-		 * session is not present - subscriptions start from scratch
-		 * publications persist across connections on the broker (retain flag)
-		 */
-		self._subscribeWithRetries.call(self, JSON.parse(JSON.stringify(self.attrsToSubTo)), constants.mqtt.retries);
-    });
-
-    /* message event received when client receives a published packet */
-    this.client.on('message', function (topic, message, packet) {
-        // parse the mach type, the mach id, and the attribute out of the topic
-        var components = topic.split('/');
-        var type = components[1];
-        var id = components[2];
-        var attr = components[3];
-
-        // if the message payload is empty, this is the result of an "unpublish".
-        const parsed = JSON.parse(message.toString());
-        self._handleMessage.call(self, type, id, attr, parsed.payload, parsed.id);
-    });
-
-    this.client.on('offline', function () {
-        console.log('WARNING: mqtt client is offline');
-    });
-
-    this.client.on('error', function (error) {
-        console.log('WARNING: mqtt client received an error');
-    });
-}
 /**
  * Returns connection options to the mqtt broker contingent upon the connecting node
  * takes as arguments the name of the application, the type of the machine, and the
@@ -102,8 +63,8 @@ MQTTRegistry.prototype._handleMessage = function(type, id, attr, data, seqval) {
 
     var event;
 
-    // payload empty <=> unpublish on attr
-    if (data === '') 
+    // data empty <=> unpublish on attr
+    if (data === undefined) 
     {
         if (this.subscribedAttrs[type].hasOwnProperty(attr)) {
             event = this.subscribedAttrs[type][attr].onRemove;
@@ -114,7 +75,7 @@ MQTTRegistry.prototype._handleMessage = function(type, id, attr, data, seqval) {
     else if (this.subscribedAttrs[type].hasOwnProperty(attr)) 
     {
         if (attr === 'status') {
-            if (payload === 'offline') {
+            if (data === 'offline') {
                 event = this.subscribedAttrs[type].status.offline;
             } else {
                 event = this.subscribedAttrs[type].status.online;
@@ -153,29 +114,33 @@ MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
 
     // perform subscriptions
     var self = this;
-    this.client.subscribe(topics, { qos : this.subQos }, function (err, granted) {
-        if (err) {
-            // Let -1 signify infinite retries
-            if (retries > 0 || retries == -1) {
-                setTimeout(self._subscribeWithRetries.bind(self),
-                    constants.mqtt.retryInterval, dattrs, (retries === -1) ? -1 : (retries-1));
-            } else {
-                granted.map(x =>
-                {
-                    let topic = x.topic.split('/');
-                    let type = topic[1];
-                    let attr = topic[3];
-                    delete dattrs[type][attr]
-                }
-                ['device', 'fog', 'cloud'].map(x =>
-                {
-                    for (var attr in dattrs[x])
-                        delete self.subscribedAttrs[x][attr];
+    this.client.subscribe(
+        topics, 
+        { qos : this.subQos }, 
+        function (err, granted) {
+            if (err) {
+                // Let -1 signify infinite retries
+                if (retries > 0 || retries == -1) {
+                    setTimeout(self._subscribeWithRetries.bind(self),
+                        constants.mqtt.retryInterval, dattrs,
+                        (retries === -1) ? -1 : (retries-1));
+                } else {
+                    granted.map(x =>
+                    {
+                        let topic = x.topic.split('/');
+                        let type = topic[1];
+                        let attr = topic[3];
+                        delete dattrs[type][attr]
+                    }
+                    ['device', 'fog', 'cloud'].map(x =>
+                    {
+                        for (var attr in dattrs[x])
+                            delete self.subscribedAttrs[x][attr];
+                    }
+                    // notify user something fishy is going on, probably an attr name error..
+                    console.log('WARNING: MQTT Registry failed to subscribe to some topics');
                 }
-                // notify user something fishy is going on, probably an attr name error..
-                console.log('WARNING: MQTT Registry failed to subscribe to some topics');
             }
-        }
     });
 }
 /**
@@ -194,110 +159,126 @@ MQTTRegistry.prototype._unsubscribeWithRetries = function(dattrs, retries) {
         return;
 
     var self = this;
-    this.client.unsubscribe(topics, function(err) {
-        if (err) {
-            // Let -1 signify infinite retries
-            if (retries > 0 || retries == -1)
-                setTimeout(self._unsubscribeWithRetries.bind(self),
-                    constants.mqtt.retryInterval, dattrs, (retries === -1) ? -1 : (retries-1));
-        } else {
-            ['device', 'fog', 'cloud'].map(x =>
-            {
-                for (var attr in dattrs[x])
-                    delete self.subscribedAttrs[x][attr];
+    this.client.unsubscribe(
+        topics, 
+        function(err) {
+            if (err) {
+                // Let -1 signify infinite retries
+                if (retries > 0 || retries == -1)
+                    setTimeout(self._unsubscribeWithRetries.bind(self),
+                        constants.mqtt.retryInterval, dattrs,
+                        (retries === -1) ? -1 : (retries-1));
+            } else {
+                ['device', 'fog', 'cloud'].map(x =>
+                {
+                    for (var attr in dattrs[x])
+                        delete self.subscribedAttrs[x][attr];
+                }
             }
-        }
     });
 }
-MQTTRegistry.prototype._publishWithRetries = function(attr, value, dedupeId, retries) {
-    var msg;
-    if (value instanceof Function) {
-        msg = JSON.stringify({ payload: value(), id: dedupeId });
-    } else {
-        msg = JSON.stringify({ payload: value, id: dedupeId });
-    }
+MQTTRegistry.prototype._publishWithRetries = function(attr, data, seqval, retries) {
+
+    /** 
+     * When data === undefined, the receivers will interpret this as an attr removal
+     * Data is set to _null_ on publishing attrs with no data to avoid this
+     */
+    if(data === undefined)
+        data = null;
+
     var self = this;
-    this.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, msg, {qos: self.pubQos, retain: true}, function (err) {
-        if (err) {
-            if (retries === 0) {
-                setTimeout(self._publishWithRetries, constants.mqtt.retryInterval, self, attr, value, dedupeId, retries - 1);
+    this.client.publish(
+        (this.app + '/' + this.type + '/' + this.id + '/' + attr),
+        (data instanceof Function)
+            ? JSON.stringify({ data: data(), seqval: seqval })
+            : JSON.stringify({ data: data, seqval: seqval }),
+        {qos: this.pubQos, retain: true},
+        function (err) {
+            if (err) {
+                if (retries > 0 || retries === -1)
+                    setTimeout(self._publishWithRetries.bind(self), 
+                        constants.mqtt.retryInterval, attr, data, seqval,
+                        (retries === -1) ? -1 : (retries-1));
             } else {
-                self.emit('pub-error', attr, value);
+                self.publishedAttrs[attr] = { data : data , seqval : seqval };
             }
-        } else {
-            // move the attribute from attrsToPublish to publishedAttrs
-            self.publishedAttrs[attr] = { payload: value, dedupeId: dedupeId };
-            delete self.attrsToPublish[attr];
-        }
     });
 }
-MQTTRegistry.prototype._unpublishWithRetries = function(attr, retries) {
+MQTTRegistry.prototype._unpublishWithRetries = function(attr, seqval, retries) {
+
     var self = this;
-    self.client.publish(self.app + '/' + self.type + '/' + self.id + '/' + attr, null, {qos: self.pubQos, retain: true}, function (err) {
-        if (err) {
-            if (retries > 0) {
-                setTimeout(self._unpublishWithRetries, constants.mqtt.retryInterval, self, attr, retries - 1);
+    this.client.publish(
+        (this.app + '/' + this.type + '/' + this.id + '/' + attr),
+        // n.b. 'data' property must be __undefined__
+        JSON.stringify({ seqval : seqval }), 
+        {qos: this.pubQos, retain: true}, 
+        function (err) {
+            if (err) {
+                if (retries > 0 || retries === -1)
+                    setTimeout(self._unpublishWithRetries.bind(self), 
+                        constants.mqtt.retryInterval, attr, seqval, 
+                        (retries === -1) ? -1 : (retries-1));
             } else {
-                self.emit('unpub-error', attr);
+                delete self.publishedAttrs[attr];
             }
-        } else {
-            // remove the attribute from attrsToRemove and publishedAttrs
-            delete self.attrsToRemove[attr];
-            delete self.publishedAttrs[attr];
-        }
     });
 }
-
-// TODO when client is not connected, what shoud control graph be for these?
-MQTTRegistry.prototype.subscribe = function(dattrs) {
-    if (this.client && this.client.connected) {
-        this._subscribeWithRetries(dattrs, constants.mqtt.retries);
-    }
-}
-MQTTRegistry.prototype.unsubscribe = function(dattrs) {
-    if (this.client && this.client.connected) {
-        this._unsubscribeWithRetries(dattrs, constants.mqtt.retries);
-    }
-}
-MQTTRegistry.prototype.publish = function(attr, value) {
+MQTTRegistry.prototype._publish = function(attr, data, seqval) {
     if (this.client && this.client.connected) {
-        this._publishWithRetries(attr, value, constants.mqtt.retries);
+        this._publishWithRetries(attr, data, seqval, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self._publish.bind(self), 
+            constants.mqtt.retryInterval, attr, data, seqval);
     }
 }
-MQTTRegistry.prototype.unpublish = function(attr) {
+MQTTRegistry.prototype._unpublish = function(attr, seqval) {
     if (this.client && this.client.connected) {
-        this._unpublishWithRetries(attr, constants.mqtt.retries);
+        this._unpublishWithRetries(attr, seqval, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self._unpublish.bind(self), 
+            constants.mqtt.retryInterval, attr, seqval);
     }
 }
 
 /**
  * REGISTRY INTERFACE METHODS
  */
-
 /**
- * set/unset discoverable attributes for this node
+ * Performs basic registration and discovery
  */
-MQTTRegistry.prototype.setAttributes = function(attrs, seqval) {
-    for (var attr in attrs) {
-        // check that it's not already published
-        if (!this.publishedAttrs.hasOwnProperty(attr)) {
-            this.attrsToPublish[attr] = { 'data': attrs[attr], 'seqval': seqval };
-            if (this.client && this.client.connected) {
-                // try to publish the attribute
-                this._publishWithRetries(this, attr, attrs[attr], seqval, constants.mqtt.retries);
-            }
-        }
-    }
-}
-MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
-    for (var i = 0; i < attrs.length; i++) {
-        if (this.publishedAttrs.hasOwnProperty(attrs[i])) {
-            if (this.client && this.client.connected) {
-                // try to unpublish the attribute
-                this._unpublishWithRetries(this, attrs[i], constants.mqtt.retries);
-            }
-        }
-    }
+MQTTRegistry.prototype.registerAndDiscover = function() {
+    // create an mqtt client
+    this.client = mqtt.connect(constants.mqtt.brokerUrl, this._getConnectionOptions());
+
+    var self = this;
+
+    /* connect event emitted on successful connection or reconnection */
+    this.client.on('connect', function (connack) {
+		/*
+		 * session is not present - subscriptions start from scratch
+		 * publications persist across connections on the broker (retain flag)
+		 */
+		self._subscribeWithRetries.call(self, self.subscribedAttrs, constants.mqtt.retries);
+    });
+
+    /* message event received when client receives a published packet */
+    this.client.on('message', function (topic, message, packet) {
+        // parse the machine type, the machine id, and the attribute out of the topic
+        var [type, id, attr] = topic.split('/');
+
+        const parsed = JSON.parse(message.toString());
+        self._handleMessage.call(self, type, id, attr, parsed.data, parsed.seqval);
+    });
+
+    this.client.on('offline', function () {
+        console.log('WARNING: mqtt client is offline');
+    });
+
+    this.client.on('error', function (error) {
+        console.log('WARNING: mqtt client received an error');
+    });
 }
 /**
  * set/unset attributes discovered by this node
@@ -305,29 +286,49 @@ MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
 MQTTRegistry.prototype.discoverAttributes = function(dattrs) {
     if (this.client && this.client.connected) {
         this._subscribeWithRetries(dattrs, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self.discoverAttributes.bind(self), 
+            constants.mqtt.retryInterval, dattrs);
     }
 }
 MQTTRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
     if (this.client && this.client.connected) {
-        this._unsubscribeWithRetries(unsubs, constants.mqtt.retries);
+        this._unsubscribeWithRetries(dattrs, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self.stopDiscoveringAttributes.bind(self), 
+            constants.mqtt.retryInterval, dattrs);
+    }
+}
+/**
+ * set/unset discoverable attributes for this node
+ */
+MQTTRegistry.prototype.setAttributes = function(attrs, seqval) {
+    for (var attr in attrs) {
+        this._publish(attr, attrs[attr], seqval);
+    }
+}
+MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
+    for (var attr in attrs) {
+        this._unpublish(attr, seqval);
     }
 }
-
 /**
  * Closes the mqtt registry
  * --> Cleans up all publications on mqtt broker
  * --> Kills connection to broker
  */
 MQTTRegistry.prototype.quit = function() {
-
-    if (this.client) {
+    if (this.client && this.client.connected) {
         // unpublish on all attributes
         this.removeAttributes(this.publishedAttrs);
-        // unpublish status
-        this.client.publish(this.app + '/' + this.type + '/' + this.id + '/status',
-                            null, {qos: this.pubQos, retain: true});
         // end connection
         this.client.end(false);
+    } else {
+        var self = this;
+        setTimeout(self.quit.bind(self), 
+            constants.mqtt.retryInterval);
     }
     this.client = null;
 }

From 040d06ba1357e3884d088e6457b4ebc9e2a7223b Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 6 Jun 2018 14:52:42 -0400
Subject: [PATCH 20/62] JDISC: Fixing lambda expr brackets.. Oops

---
 lib/jdiscovery/apps/tester.js  |  8 ++---
 lib/jdiscovery/jregistrar.js   | 14 +++++---
 lib/jdiscovery/mqttregistry.js | 59 ++++++++++++++++++----------------
 3 files changed, 43 insertions(+), 38 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index ef5426de..6a10dc6a 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -1,5 +1,4 @@
 var Registrar = require('../jregistrar'),
-errLog = require('../../jamserver/jerrlog'),
 globals = require('../../jamserver/constants').globals,
 events = require('events'),
 Random = require('random-js');
@@ -13,9 +12,6 @@ port = process.argv[5],
 id = process.argv[6],
 app = 'tester';
 
-// don't forget to initialize the logger!
-errLog.init(app, false);
-
 console.log('_______________________________________________');
 console.log(machType + ' id: ' + id);
 console.log('-----------------------------------------------');
@@ -57,7 +53,7 @@ switch(err.name) {
 	break;
 }
 });
-
+/*
 // Custom attributes/discoveries
 
 if (machType === 'device') {
@@ -154,5 +150,5 @@ reggie.on('android-removed', function(deviceId) {
     console.log('DEVICE ' + deviceId + ' is no longer an Android');
 });
 }
-
+*/
 reggie.registerAndDiscover();
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 0334593a..83f42454 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -187,7 +187,9 @@ Registrar.prototype.registerAndDiscover = function(options) {
         }
     }
 
-    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x.registerAndDiscover());
+    [this.mqttRegistry, this.mdnsRegistry].map(
+        x => {if(x) x.registerAndDiscover();}
+    );
     this.started = true;
 }
 
@@ -202,7 +204,9 @@ Registrar.prototype.removeAttributes = function(attrs) {
     this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
 Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
-    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x[fun](attrs, seqval));
+    [this.mqttRegistry, this.mdnsRegistry].map(
+        x => {if(x) x[fun](attrs, seqval);}
+    );
 }
 
 /**
@@ -256,7 +260,9 @@ Registar.prototype._formatDattributes = function(dattrs) {
     return dattrs;
 }
 Registrar.prototype._modifyDattributes = function(fun, dattrs) {
-    [this.mqttRegistry, this.mdnsRegistry].map(x => if(x) x[fun](dattrs));
+    [this.mqttRegistry, this.mdnsRegistry].map(
+        x => {if(x) x[fun](dattrs);}
+    );
 }
 
 
@@ -278,7 +284,7 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
         this.dt[id]['attrs'] = {};
     }
     if (!this.dt[id]['attrs'].hasOwnProperty(attr))
-        this.dt.[id]['attrs'][attr] = {};
+        this.dt[id]['attrs'][attr] = {};
 
     // Insert data/seqval pair if not already received
     if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index f1adb48e..f5ccc511 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -1,5 +1,4 @@
 var mqtt = require('mqtt'),
-    logger = require('../jamserver/jerrlog.js'),
     constants = require('../jamserver/constants'),
     Registry = require('./registry');
 
@@ -95,12 +94,12 @@ MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
 
     // create topic strings to be sent to the broker for subscription
     var topics = [];
-    ['device', 'fog', 'cloud'].map(x =>
-    {
-        for (var attr in dattrs[x]) {
-            topics.push(this.app + '/' + x + '/+/' + attr);
+    ['device', 'fog', 'cloud'].map(
+        x => {
+            for (var attr in dattrs[x])
+                topics.push(this.app + '/' + x + '/+/' + attr);
         }
-    }
+    );
     if (topics.length === 0)
         return;
 
@@ -125,18 +124,20 @@ MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
                         constants.mqtt.retryInterval, dattrs,
                         (retries === -1) ? -1 : (retries-1));
                 } else {
-                    granted.map(x =>
-                    {
-                        let topic = x.topic.split('/');
-                        let type = topic[1];
-                        let attr = topic[3];
-                        delete dattrs[type][attr]
-                    }
-                    ['device', 'fog', 'cloud'].map(x =>
-                    {
-                        for (var attr in dattrs[x])
-                            delete self.subscribedAttrs[x][attr];
-                    }
+                    granted.map(
+                        x => {
+                            let topic = x.topic.split('/');
+                            let type = topic[1];
+                            let attr = topic[3];
+                            delete dattrs[type][attr];
+                        }
+                    );
+                    ['device', 'fog', 'cloud'].map(
+                        x => {
+                            for (var attr in dattrs[x])
+                                delete self.subscribedAttrs[x][attr];
+                        }
+                    );
                     // notify user something fishy is going on, probably an attr name error..
                     console.log('WARNING: MQTT Registry failed to subscribe to some topics');
                 }
@@ -149,12 +150,13 @@ MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
 MQTTRegistry.prototype._unsubscribeWithRetries = function(dattrs, retries) {
 
     var topics = [];
-    ['device', 'fog', 'cloud'].map(x =>
-    {
-        for (var attr in dattrs[x]) {
-            topics.push(this.app + '/' + x + '/+/' + attr);
+    ['device', 'fog', 'cloud'].map(
+        x => {
+            for (var attr in dattrs[x]) {
+                topics.push(this.app + '/' + x + '/+/' + attr);
+            }
         }
-    }
+    );
     if (topics.length === 0)
         return;
 
@@ -169,11 +171,12 @@ MQTTRegistry.prototype._unsubscribeWithRetries = function(dattrs, retries) {
                         constants.mqtt.retryInterval, dattrs,
                         (retries === -1) ? -1 : (retries-1));
             } else {
-                ['device', 'fog', 'cloud'].map(x =>
-                {
-                    for (var attr in dattrs[x])
-                        delete self.subscribedAttrs[x][attr];
-                }
+                ['device', 'fog', 'cloud'].map(
+                    x => {
+                        for (var attr in dattrs[x])
+                            delete self.subscribedAttrs[x][attr];
+                    }
+                );
             }
     });
 }

From 680867b95b176e2169241935c49099ff2bbfb071 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 7 Jun 2018 14:23:31 -0400
Subject: [PATCH 21/62] JDISC: Bug fixes, and seq # scheme update

---
 lib/jdiscovery/jregistrar.js   | 52 +++++++++++++++++++++-------------
 lib/jdiscovery/mqttregistry.js |  5 ++--
 2 files changed, 35 insertions(+), 22 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 83f42454..1e1ac945 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -111,7 +111,6 @@ function Registrar(app, type, id, port, config) {
                 status: {
                     online: 'fog-up',
                     offline: 'fog-down'
-                    // if the status value is `offline`, then we emit fog-down, else we emit fog-up
                 }
             },
             cloud: {
@@ -244,7 +243,7 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
  * Marshall dattrs to the following format (expected by the registries)
  * { 'device' : {...}, 'fog' : {...}, 'cloud' : {...} }
  */
-Registar.prototype._formatDattributes = function(dattrs) {
+Registrar.prototype._formatDattributes = function(dattrs) {
     if(!(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud))
         dattrs = { 'all' : dattrs };
     ['device', 'fog', 'cloud'].map(
@@ -276,16 +275,39 @@ Registrar.prototype._modifyDattributes = function(fun, dattrs) {
 Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
     // DUBUGGING: Print shit
-    console.log("---------------------------------- ", event, id, data);
+    console.log("DEBUG: Registar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
 
     // Update discovery table
     if (!this.dt.hasOwnProperty(id)) {
         this.dt[id] = {};
         this.dt[id]['attrs'] = {};
+        // nodes are assumed to be on WAN by default
+        this.dt[id]['network'] = 'WAN';
+    }
+    // maintain info about where the node is: 'LAN' or 'WAN'
+    if (protocol === constants.globals.Protocol.MDNS) {
+        this.dt[id]['network'] = 'LAN';
     }
     if (!this.dt[id]['attrs'].hasOwnProperty(attr))
         this.dt[id]['attrs'][attr] = {};
 
+    // Because node offline events end up here instead of in _respondToRemovalEvent, we need to check the attribute
+    // and value in order to know what arguments to pass along with the event.
+    // WARNING: XXX __SEQVAL_BYPASS__ XXX
+    // It should be noted that is a potential network race condition whereby it would be
+    // possible to have status updates arriving to subscribers in the wrong order.
+    // This could potentially be solved by subscribing to status topics with QoS 2
+    // The above proposed solution is : NOT IMPLEMENTED due to how unlikely this is to happen.
+    if (attr === 'status' && data === 'offline') {
+        this.dt[id]['attrs']['status']['data'] = 'offline';
+        // Reset seqvals for all attributes in case the node crashed
+        for(var key in this.dt[id]['attrs'])
+            if(this.dt[id]['attrs'].hasOwnProperty(key))
+                this.dt[id]['attrs'][key]['seqval'] = -1;
+        this.emit(event, id, protocol);
+        return;
+    }
+
     // Insert data/seqval pair if not already received
     if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
        || this.dt[id]['attrs'][attr]['seqval'] < seqval
@@ -295,30 +317,20 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
         this.dt[id]['attrs'][attr]['data'] = data;
         this.dt[id]['attrs'][attr]['seqval'] = seqval;
 
-        // maintain info about where the node is: 'LAN' or 'WAN'
-        if (  this.dt[id]['network'] !== 'LAN'
-           && protocol === constants.globals.Protocol.MDNS) {
-            this.dt[id]['network'] = 'LAN';
-        } else {
-            this.dt[id]['network'] = 'WAN';
-        }
+        this.emit(event, id, data, protocol);
     } else {
         return;
     }
-
-    // Because node offline events end up here instead of in _respondToAttrRemovalEvent, we need to check the attribute
-    // and value in order to know what arguments to pass along with the event.
-    if (attr === 'status' && data === 'offline') {
-        this.emit(event, id, protocol);
-        return;
-    }
-    this.emit(event, id, data, protocol);
 }
 
 /**
  * Upon receipt of an attribute removal event, pass it onto the rest of the application
  */
 Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, protocol) {
+
+    // DUBUGGING: Print shit
+    console.log("DEBUG: Registar._respondToRemovalEvent: ", event, id, seqval, protocol);
+
     if (  !this.dt.hasOwnProperty(id)
        || !this.dt.id.hasOwnProperty(attr))
         return;
@@ -336,7 +348,7 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
 /**
  * returns next seq number for event ordering
  */
-Registar.prototype._getSeqVal = function() {
+Registrar.prototype._getSeqVal = function() {
     if (this.seqval == Number.MAX_SAFE_INTEGER) {
         this.seqval = 0;
     }
@@ -346,7 +358,7 @@ Registar.prototype._getSeqVal = function() {
 /**
  * returns the IPv4 address of the node
  */
-Registar.prototype._getIPv4Address = function() {
+Registrar.prototype._getIPv4Address = function() {
     var niaddrs = os.networkInterfaces();
     for (var ni in niaddrs) {
         nielm = niaddrs[ni];
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index f5ccc511..155f809f 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -39,7 +39,8 @@ MQTTRegistry.prototype._getConnectionOptions = function() {
     // create the will
     var will = {
         topic: this.app + '/' + this.type + '/' + this.id + '/status',
-        payload: JSON.stringify({ payload: 'offline' }),
+        // n.b. will pub will not have a seqval due to its nature
+        payload: JSON.stringify({ data: 'offline' }),
         qos: this.pubQos,
         retain: true
     }
@@ -269,7 +270,7 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
     /* message event received when client receives a published packet */
     this.client.on('message', function (topic, message, packet) {
         // parse the machine type, the machine id, and the attribute out of the topic
-        var [type, id, attr] = topic.split('/');
+        var [app, type, id, attr] = topic.split('/');
 
         const parsed = JSON.parse(message.toString());
         self._handleMessage.call(self, type, id, attr, parsed.data, parsed.seqval);

From faad81ff224412a6b85b3f989173f6127fd521f4 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 7 Jun 2018 16:15:22 -0400
Subject: [PATCH 22/62] JDISC: Started mDNSreg overhaul

---
 lib/jdiscovery/mdnsregistry.js | 121 +++++++++++++--------------------
 lib/jdiscovery/mqttregistry.js |   4 +-
 2 files changed, 49 insertions(+), 76 deletions(-)

diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index ed239888..d45615b6 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -1,10 +1,5 @@
-//==============================================================================
-// Registers a node on the local network using mDNS
-//==============================================================================
-
 var mdns = require('./mdns/lib/mdns'),
     constants = require('../jamserver/constants'),
-    logger = require('../jamserver/jerrlog'),
     Registry = require('./registry');
 
 var sequence = [
@@ -15,21 +10,25 @@ var sequence = [
 ];
 
 function MDNSRegistry(app, machType, id, port) {
+
     Registry.call(this, app, machType, id, port);
     this.protocol = constants.globals.Protocol.MDNS;
+
     this.ads = {};
+    this.attrs = {};
+
     this.browsers = {
         device: {},
         fog: {},
         cloud: {}
     };
-    this.started = false;
-    this.attrs = {};
     this.attrsToDiscover = {
         device: {},
         fog: {},
         cloud: {}
     }
+
+    this.started = false;
 }
 
 /* MDNSRegistry inherits from Registry */
@@ -37,13 +36,16 @@ MDNSRegistry.prototype = Object.create(Registry.prototype);
 MDNSRegistry.prototype.constructor = MDNSRegistry;
 
 MDNSRegistry.prototype.registerAndDiscover = function() {
+
     this.started = true;
 
     // start any ads and browsers created before this function was called
     for (var attr in this.attrs) {
-        const attrObj = {};
-        attrObj[attr] = this.attrs[attr].payload;
-        this._createAdvertisements(attrObj, this.attrs[attr].dedupeId);
+        if(this.attrs.hasOwnProperty(attr)) {
+            const clone = {};
+            clone[attr] = this.attrs[attr].data;
+            this._createAdvertisements(clone, this.attrs[attr].seqval);
+        }
     }
     this.discoverAttributes(this.attrsToDiscover);
 }
@@ -258,15 +260,16 @@ MDNSRegistry.prototype._browseForStatus = function(self, machType, events) {
 /**
  * Adds attributes by starting ads.
  * attrs - an object of attributes
- * dedupeId - the ID to publish with the attributes for deduplication purposes
- *  on the receiving node
+ * seqval - the ID to publish with the attributes for deduplication purposes
+ *          on the receiving node
  */
-MDNSRegistry.prototype.addAttributes = function(attrs, dedupeId) {
+MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
     if (this.started) {
-        this._createAdvertisements(attrs, dedupeId);
+        this._createAdvertisements(attrs, seqval);
     } else {
-        for (var attr in attrs) {
-            this.attrs[attr] = { payload: attrs[attr], dedupeId: dedupeId };
+        for (var attr in attrs)
+            if(attrs.hasOwnProperty(attr))
+                this.attrs[attr] = { data: attrs[attr], seqval: seqval };
         }
     }
 }
@@ -299,15 +302,13 @@ MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
     if (this.started) {
         this._browseForAttributes(dattrs);
     } else {
-        for (var attr in dattrs.device) {
-            this.attrsToDiscover.device[attr] = dattrs.device[attr];
-        }
-        for (var attr in dattrs.fog) {
-            this.attrsToDiscover.fog[attr] = dattrs.fog[attr];
-        }
-        for (var attr in dattrs.cloud) {
-            this.attrsToDiscover.cloud[attr] = dattrs.cloud[attr];
-        }
+        ['device', 'fog', 'cloud'].map(
+                x => {
+                    for (var attr in dattrs[x])
+                        if(dattrs[x].hasOwnProperty(attr))
+                            this.attrsToDiscover[x][attr] = dattrs[x][attr];
+                }
+        );
     }
 }
 
@@ -315,49 +316,28 @@ MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
  * Stops making discoveries by stopping browsers
  */
 MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
-    if (dattrs.device) {
-        for (var i = 0; i < dattrs.device.length; i++) {
-            if (this.started) {
-                // stop the browser
-                if (this.browsers.device[dattrs.device[i]]) {
-                    this.browsers.device[dattrs.device[i]].stop();
+    ['device', 'fog', 'cloud'].map(
+            x => {
+                if (dattrs[x]) {
+                    for (var i = 0; i < dattrs[x].length; i++) {
+                        if (this.started) {
+                            // stop the browser
+                            if (this.browsers[x][dattrs[x][i]]) {
+                                this.browsers[x][dattrs[x][i]].stop();
+                            }
+                        } else {
+                            delete this.attrsToDiscover[x][dattrs[x][i]];
+                        }
+                    }
                 }
-            } else {
-                delete this.attrsToDiscover.device[dattrs.device[i]];
             }
-        }
-    }
-
-    if (dattrs.fog) {
-        for (var i = 0; i < dattrs.fog.length; i++) {
-            if (this.started) {
-                if (this.browsers.fog[dattrs.fog[i]]) {
-                    this.browsers.fog[dattrs.fog[i]].stop();
-                }
-            } else {
-                delete this.attrsToDiscover.fog[dattrs.fog[i]];
-            }
-        }
-    }
-
-    if (dattrs.cloud) {
-        for (var i = 0; i < dattrs.cloud.length; i++) {
-            if (this.started) {
-                if (this.browsers.cloud[dattrs.cloud[i]]) {
-                    this.browsers.cloud[dattrs.cloud[i]].stop();
-                }
-            } else {
-                delete this.attrsToDiscover.cloud[dattrs.cloud[i]];
-            }
-        }
-    }
+    );
 }
 
 /**
  * mDNS cleanup
- * stops all advertising and browsing
+ * Stop all advertising and browsing
  */
-/*
 MDNSRegistry.prototype.quit = function() {
     // stop ads
     for (var attr in this.ads) {
@@ -365,19 +345,14 @@ MDNSRegistry.prototype.quit = function() {
     }
 
     // stop browsers
-    for (var attr in this.browsers.device) {
-        this.browsers.device[attr].stop();
-    }
-
-    for (var attr in this.browsers.fog) {
-        this.browsers.fog[attr].stop();
-    }
-
-    for (var attr in this.browsers.cloud) {
-        this.browsers.cloud[attr].stop();
-    }
+    ['device', 'fog', 'cloud'].map(
+            x => {
+                for (var attr in this.browsers.device)
+                    if(this.browsers[x].hasOwnProperty(attr))
+                        this.browsers[x][attr].stop();
+            }
+    );
 }
-*/
 
 /* exports */
 module.exports = MDNSRegistry;
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 155f809f..0737eae1 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -193,9 +193,7 @@ MQTTRegistry.prototype._publishWithRetries = function(attr, data, seqval, retrie
     var self = this;
     this.client.publish(
         (this.app + '/' + this.type + '/' + this.id + '/' + attr),
-        (data instanceof Function)
-            ? JSON.stringify({ data: data(), seqval: seqval })
-            : JSON.stringify({ data: data, seqval: seqval }),
+        JSON.stringify({ data: data, seqval: seqval }),
         {qos: this.pubQos, retain: true},
         function (err) {
             if (err) {

From b4dd5edf54ddb11f0bc65e82b1ca9da2396282d0 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 8 Jun 2018 12:14:09 -0400
Subject: [PATCH 23/62] JDISC: Refactoring & implemented start lock in mqttreg

---
 lib/jdiscovery/jregistrar.js   |  74 +++++-----
 lib/jdiscovery/mdnsregistry.js |   4 +-
 lib/jdiscovery/mqttregistry.js | 248 +++++++++++++++++----------------
 3 files changed, 167 insertions(+), 159 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 1e1ac945..de8d4dce 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -5,11 +5,11 @@ var EventEmitter = require('events'),
     MDNSRegistry = require('./mdnsregistry'),
     os = require('os');
 
-//==============================================================================
-// Registrar Class
-// This class is the interface between the application
-// and the MQTT, mDNS registries
-//==============================================================================
+/**
+ * Registrar Class
+ *      This class is the interface between the application
+ *      and the MQTT, mDNS registries
+ */
 
 function Registrar(app, type, id, port, config) {
     // the name of the application
@@ -62,9 +62,6 @@ function Registrar(app, type, id, port, config) {
      */
     this.reservedAttrs = ['status', 'lastCheckIn', 'createdAt', 'updatedAt'];
 
-    // whether or not this registrar has been started
-    this.started = false;
-
     /**
      * config-specific set-up
      */
@@ -87,6 +84,30 @@ function Registrar(app, type, id, port, config) {
         throw new Error('a Registrar must use at least one protocol');
     }
 
+    // whether or not this registrar has been started
+    this.started = false;
+}
+
+/* Registrar inherits from EventEmitter */
+Registrar.prototype = Object.create(EventEmitter.prototype);
+Registrar.prototype.constructor = Registrar;
+
+/**
+ * REGISTRAR INTERFACE METHODS
+ * __JDISCOVERY_EXTERNAL_API__
+ */
+
+/**
+ * Register a node on the network, and discover other nodes.
+ * `options` is an optional parameter
+ * `options` include:
+ *   attrsToSet: key/value pair as in this.setAttributes
+ *   attrsToDiscover: as in this.discoverAttributes
+ */
+Registrar.prototype.registerAndDiscover = function(options) {
+    if (this.started)
+        return;
+
     /**
      * Set up default attributes, which are the same for devices, fogs, and clouds.
      * The only default attribute is 'status'.
@@ -136,7 +157,7 @@ function Registrar(app, type, id, port, config) {
     }
 
     /**
-     * SET PROPAGATABLE EVENTS
+     * Set propagatable events
      */
     [this.mqttRegistry, this.mdnsRegistry].filter(x => x).map(x =>
     {
@@ -152,27 +173,13 @@ function Registrar(app, type, id, port, config) {
             self._respondToRemovalEvent.call(self, attr, event, id, seqval, x.protocol);
         });
     });
-}
-
-/* Registrar inherits from EventEmitter */
-Registrar.prototype = Object.create(EventEmitter.prototype);
-Registrar.prototype.constructor = Registrar;
-
-//==============================================================================
-// API
-//==============================================================================
-
-/**
- * Register a node on the network, and discover other nodes.
- * `options` is an optional parameter
- * `options` include:
- *   attrsToSet: key/value pair as in this.setAttributes
- *   attrsToDiscover: as in this.discoverAttributes
- */
-Registrar.prototype.registerAndDiscover = function(options) {
-    if (this.started)
-        return;
 
+    /**
+     * Handle passed `options`
+     * `options` include:
+     *   attrsToSet: key/value pair as in this.setAttributes
+     *   attrsToDiscover: as in this.discoverAttributes
+     */
     if (options) {
         if (typeof options !== 'object') {
             throw new Error('options must be an object - see the docs');
@@ -265,10 +272,9 @@ Registrar.prototype._modifyDattributes = function(fun, dattrs) {
 }
 
 
-//==============================================================================
-// Helpers
-//==============================================================================
-
+/**
+ * _PRIVATE HELPERS
+ */
 /**
  * Upon receipt of a discovery event, pass it onto the rest of the application if it is not a duplicate
  */
@@ -300,7 +306,7 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
     // The above proposed solution is : NOT IMPLEMENTED due to how unlikely this is to happen.
     if (attr === 'status' && data === 'offline') {
         this.dt[id]['attrs']['status']['data'] = 'offline';
-        // Reset seqvals for all attributes in case the node crashed
+        // Set seqvals for all attributes of node to -1 in case the node crashed
         for(var key in this.dt[id]['attrs'])
             if(this.dt[id]['attrs'].hasOwnProperty(key))
                 this.dt[id]['attrs'][key]['seqval'] = -1;
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index d45615b6..4bbb08a1 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -9,9 +9,9 @@ var sequence = [
     mdns.rst.makeAddressesUnique()
 ];
 
-function MDNSRegistry(app, machType, id, port) {
+function MDNSRegistry(app, type, id, port) {
 
-    Registry.call(this, app, machType, id, port);
+    Registry.call(this, app, type, id, port);
     this.protocol = constants.globals.Protocol.MDNS;
 
     this.ads = {};
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 0737eae1..b5ff924a 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -3,8 +3,13 @@ var mqtt = require('mqtt'),
     Registry = require('./registry');
 
 function MQTTRegistry(app, type, id, port, subQos, pubQos) {
+
     Registry.call(this, app, type, id, port);
     this.protocol = constants.globals.Protocol.MQTT; 
+
+    // client will hold the mqtt client, initialized in registerAndDiscover
+    this.client = null;
+
     // the quality of service to use for subscriptions
     this.subQos = subQos;
     // the quality of service to use for publications
@@ -21,12 +26,115 @@ function MQTTRegistry(app, type, id, port, subQos, pubQos) {
         fog: {},
         cloud: {}
     };
+
+    // has this registry already been started w registerAndDiscover()?
+    this.started = false;
 }
 
 /* MQTTRegistry inherits from Registry */
 MQTTRegistry.prototype = Object.create(Registry.prototype);
 MQTTRegistry.prototype.constructor = MQTTRegistry;
 
+/**
+ * REGISTRY INTERFACE METHODS
+ */
+
+/**
+ * Performs basic registration and discovery
+ */
+MQTTRegistry.prototype.registerAndDiscover = function() {
+
+    if(this.started)
+        return;
+
+    // create an mqtt client
+    // in case of disconnection, client __automatically__ reconnects
+    this.client = mqtt.connect(constants.mqtt.brokerUrl, this._getConnectionOptions());
+
+    var self = this;
+
+    /* connect event emitted on successful connection or reconnection */
+    this.client.on('connect', function (connack) {
+		/*
+		 * session is not present - subscriptions start from scratch
+		 * publications persist across connections on the broker (retain flag)
+		 */
+		self._subscribeWithRetries.call(self, self.subscribedAttrs, constants.mqtt.retries);
+    });
+    /* message event received when client receives a published packet */
+    this.client.on('message', function (topic, message, packet) {
+        // parse the machine type, the machine id, and the attribute out of the topic
+        var [app, type, id, attr] = topic.split('/');
+
+        const parsed = JSON.parse(message.toString());
+        self._handleMessage.call(self, type, id, attr, parsed.data, parsed.seqval);
+    });
+    this.client.on('offline', function () {
+        console.log('WARNING: mqtt client is offline');
+    });
+    this.client.on('error', function (error) {
+        console.log('WARNING: mqtt client received an error');
+    });
+
+    this.started = true;
+}
+/**
+ * set/unset attributes discovered by this node
+ */
+MQTTRegistry.prototype.discoverAttributes = function(dattrs) {
+    if (this.client && this.client.connected) {
+        this._subscribeWithRetries(dattrs, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self.discoverAttributes.bind(self), 
+            constants.mqtt.retryInterval, dattrs);
+    }
+}
+MQTTRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
+    if (this.client && this.client.connected) {
+        this._unsubscribeWithRetries(dattrs, constants.mqtt.retries);
+    } else {
+        var self = this;
+        setTimeout(self.stopDiscoveringAttributes.bind(self), 
+            constants.mqtt.retryInterval, dattrs);
+    }
+}
+/**
+ * set/unset discoverable attributes for this node
+ */
+MQTTRegistry.prototype.setAttributes = function(attrs, seqval) {
+    for (var attr in attrs) {
+        this._publish(attr, attrs[attr], seqval);
+    }
+}
+MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
+    for (var attr in attrs) {
+        this._unpublish(attr, seqval);
+    }
+}
+/**
+ * Closes the mqtt registry
+ * --> Cleans up all publications on mqtt broker
+ * --> Kills connection to broker
+ */
+MQTTRegistry.prototype.quit = function() {
+    if (this.client && this.client.connected) {
+        // unpublish on all attributes
+        this.removeAttributes(this.publishedAttrs);
+        // end connection
+        this.client.end(false);
+    } else {
+        var self = this;
+        setTimeout(self.quit.bind(self), 
+            constants.mqtt.retryInterval);
+    }
+    this.client = null;
+}
+
+/**
+ * _PRIVATE HELPERS
+ */
+
 /**
  * Returns connection options to the mqtt broker contingent upon the connecting node
  * takes as arguments the name of the application, the type of the machine, and the
@@ -96,54 +204,38 @@ MQTTRegistry.prototype._subscribeWithRetries = function(dattrs, retries) {
     // create topic strings to be sent to the broker for subscription
     var topics = [];
     ['device', 'fog', 'cloud'].map(
-        x => {
+        (x) => {
             for (var attr in dattrs[x])
-                topics.push(this.app + '/' + x + '/+/' + attr);
+                if(dattrs[x].hasOwnProperty(attr))
+                    topics.push(this.app + '/' + x + '/+/' + attr);
         }
     );
     if (topics.length === 0)
         return;
 
-    // add new subscriptions to subscribedAttrs
-    ['device', 'fog', 'cloud'].map(x =>
-    {
-        for (var attr in dattrs[x]) {
-            this.subscribedAttrs[x][attr] = dattrs[x][attr];
-        }
-    });
-
     // perform subscriptions
     var self = this;
     this.client.subscribe(
         topics, 
-        { qos : this.subQos }, 
+        { qos : this.subQos },
         function (err, granted) {
             if (err) {
                 // Let -1 signify infinite retries
-                if (retries > 0 || retries == -1) {
+                if (retries > 0 || retries == -1)
                     setTimeout(self._subscribeWithRetries.bind(self),
                         constants.mqtt.retryInterval, dattrs,
                         (retries === -1) ? -1 : (retries-1));
-                } else {
-                    granted.map(
-                        x => {
-                            let topic = x.topic.split('/');
-                            let type = topic[1];
-                            let attr = topic[3];
-                            delete dattrs[type][attr];
-                        }
-                    );
-                    ['device', 'fog', 'cloud'].map(
-                        x => {
-                            for (var attr in dattrs[x])
-                                delete self.subscribedAttrs[x][attr];
-                        }
-                    );
-                    // notify user something fishy is going on, probably an attr name error..
-                    console.log('WARNING: MQTT Registry failed to subscribe to some topics');
-                }
+            } else {
+                // add new subscriptions to subscribedAttrs
+                granted.map(
+                    x => {
+                        let [ , type, , attr] = x.topic.split('/');
+                        self.subscribedAttrs[type][attr] = dattrs[type][attr];
+                    }
+                );
             }
-    });
+        }
+    );
 }
 /**
  * Unsubscribe from a series of topics
@@ -175,7 +267,8 @@ MQTTRegistry.prototype._unsubscribeWithRetries = function(dattrs, retries) {
                 ['device', 'fog', 'cloud'].map(
                     x => {
                         for (var attr in dattrs[x])
-                            delete self.subscribedAttrs[x][attr];
+                            if (dattrs[x].hasOwnProperty(attr))
+                                delete self.subscribedAttrs[x][attr];
                     }
                 );
             }
@@ -244,96 +337,5 @@ MQTTRegistry.prototype._unpublish = function(attr, seqval) {
     }
 }
 
-/**
- * REGISTRY INTERFACE METHODS
- */
-/**
- * Performs basic registration and discovery
- */
-MQTTRegistry.prototype.registerAndDiscover = function() {
-    // create an mqtt client
-    this.client = mqtt.connect(constants.mqtt.brokerUrl, this._getConnectionOptions());
-
-    var self = this;
-
-    /* connect event emitted on successful connection or reconnection */
-    this.client.on('connect', function (connack) {
-		/*
-		 * session is not present - subscriptions start from scratch
-		 * publications persist across connections on the broker (retain flag)
-		 */
-		self._subscribeWithRetries.call(self, self.subscribedAttrs, constants.mqtt.retries);
-    });
-
-    /* message event received when client receives a published packet */
-    this.client.on('message', function (topic, message, packet) {
-        // parse the machine type, the machine id, and the attribute out of the topic
-        var [app, type, id, attr] = topic.split('/');
-
-        const parsed = JSON.parse(message.toString());
-        self._handleMessage.call(self, type, id, attr, parsed.data, parsed.seqval);
-    });
-
-    this.client.on('offline', function () {
-        console.log('WARNING: mqtt client is offline');
-    });
-
-    this.client.on('error', function (error) {
-        console.log('WARNING: mqtt client received an error');
-    });
-}
-/**
- * set/unset attributes discovered by this node
- */
-MQTTRegistry.prototype.discoverAttributes = function(dattrs) {
-    if (this.client && this.client.connected) {
-        this._subscribeWithRetries(dattrs, constants.mqtt.retries);
-    } else {
-        var self = this;
-        setTimeout(self.discoverAttributes.bind(self), 
-            constants.mqtt.retryInterval, dattrs);
-    }
-}
-MQTTRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
-    if (this.client && this.client.connected) {
-        this._unsubscribeWithRetries(dattrs, constants.mqtt.retries);
-    } else {
-        var self = this;
-        setTimeout(self.stopDiscoveringAttributes.bind(self), 
-            constants.mqtt.retryInterval, dattrs);
-    }
-}
-/**
- * set/unset discoverable attributes for this node
- */
-MQTTRegistry.prototype.setAttributes = function(attrs, seqval) {
-    for (var attr in attrs) {
-        this._publish(attr, attrs[attr], seqval);
-    }
-}
-MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
-    for (var attr in attrs) {
-        this._unpublish(attr, seqval);
-    }
-}
-/**
- * Closes the mqtt registry
- * --> Cleans up all publications on mqtt broker
- * --> Kills connection to broker
- */
-MQTTRegistry.prototype.quit = function() {
-    if (this.client && this.client.connected) {
-        // unpublish on all attributes
-        this.removeAttributes(this.publishedAttrs);
-        // end connection
-        this.client.end(false);
-    } else {
-        var self = this;
-        setTimeout(self.quit.bind(self), 
-            constants.mqtt.retryInterval);
-    }
-    this.client = null;
-}
-
 /* exports */
 module.exports = MQTTRegistry;

From d203a2a7649d61bf08796c3b79a6049db2a84816 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 8 Jun 2018 12:36:17 -0400
Subject: [PATCH 24/62] JDISC: mdnsreg refactoring

---
 lib/jdiscovery/mdnsregistry.js | 275 +++++++++++++++------------------
 lib/jdiscovery/mqttregistry.js |   6 +
 2 files changed, 132 insertions(+), 149 deletions(-)

diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 4bbb08a1..80bd4901 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -35,20 +35,124 @@ function MDNSRegistry(app, type, id, port) {
 MDNSRegistry.prototype = Object.create(Registry.prototype);
 MDNSRegistry.prototype.constructor = MDNSRegistry;
 
+
+/**
+ * REGISTRY INTERFACE METHODS
+ */
+
+/**
+ * Start mDNS registry
+ */
 MDNSRegistry.prototype.registerAndDiscover = function() {
 
-    this.started = true;
+    if(this.started)
+        return;
 
     // start any ads and browsers created before this function was called
     for (var attr in this.attrs) {
         if(this.attrs.hasOwnProperty(attr)) {
-            const clone = {};
+            let clone = {};
             clone[attr] = this.attrs[attr].data;
             this._createAdvertisements(clone, this.attrs[attr].seqval);
         }
     }
     this.discoverAttributes(this.attrsToDiscover);
+
+    this.started = true;
+}
+/**
+ * Adds attributes by starting ads.
+ * attrs - an object of attributes
+ * seqval - the ID to publish with the attributes for deduplication purposes
+ *          on the receiving node
+ */
+MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
+    if (this.started) {
+        this._createAdvertisements(attrs, seqval);
+    } else {
+        for (var attr in attrs)
+            if(attrs.hasOwnProperty(attr))
+                this.attrs[attr] = { data: attrs[attr], seqval: seqval };
+        }
+    }
 }
+/**
+ * Removes attributes by stopping the advertisements
+ */
+MDNSRegistry.prototype.removeAttributes = function(attrs) {
+    if (this.started) {
+        for (var i = 0; i < attrs.length; i++) {
+            // stop and remove the advertisement
+            if (this.ads[attrs[i]]) {
+                this.ads[attrs[i]].stop();
+                // we delete the ad object because even if we start advertising with the
+                // same service name in the future, the value we advertise may be different
+                delete this.ads[attrs[i]];
+            }
+        }
+    } else {
+        for (var i = 0; i < attrs.length; i++) {
+            delete this.attrs[attrs[i]];
+        }
+    }
+}
+/**
+ * Discovers attributes by starting browsers
+ */
+MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
+    if (this.started) {
+        this._browseForAttributes(dattrs);
+    } else {
+        ['device', 'fog', 'cloud'].map(
+                x => {
+                    for (var attr in dattrs[x])
+                        if(dattrs[x].hasOwnProperty(attr))
+                            this.attrsToDiscover[x][attr] = dattrs[x][attr];
+                }
+        );
+    }
+}
+/**
+ * Stops making discoveries by stopping browsers
+ */
+MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
+    ['device', 'fog', 'cloud'].map(
+            x => {
+                if (dattrs[x]) {
+                    for (var i = 0; i < dattrs[x].length; i++) {
+                        if (this.started) {
+                            // stop the browser
+                            if (this.browsers[x][dattrs[x][i]]) {
+                                this.browsers[x][dattrs[x][i]].stop();
+                            }
+                        } else {
+                            delete this.attrsToDiscover[x][dattrs[x][i]];
+                        }
+                    }
+                }
+            }
+    );
+}
+/**
+ * mDNS cleanup
+ * Stop all advertising and browsing
+ */
+MDNSRegistry.prototype.quit = function() {
+    // stop ads
+    for (var attr in this.ads) {
+        this.ads[attr].stop();
+    }
+
+    // stop browsers
+    ['device', 'fog', 'cloud'].map(
+            x => {
+                for (var attr in this.browsers.device)
+                    if(this.browsers[x].hasOwnProperty(attr))
+                        this.browsers[x][attr].stop();
+            }
+    );
+}
+
 
 /* TO HANDLE INTERNALLY TODO
         this.mdnsRegistry.on('ad-error', function(attr, adName, txtRecord) {
@@ -67,9 +171,9 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
         });
 */
 
-//------------------------------------------------------------------------------
-// Advertisement creation
-//------------------------------------------------------------------------------
+/**
+ * _PRIVATE HELPERS
+ */
 
 /**
  * Creates advertisements for the provided attributes
@@ -101,7 +205,6 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, dedupeId) {
         this._createAdvertisementWithRetries(this, attr, adName, txtRecord, constants.mdns.retries);
     }
 }
-
 /**
  * Helper
  */
@@ -142,56 +245,33 @@ MDNSRegistry.prototype._handleError = function(self, err, ad, attr, adName, txtR
             self.emit('ad-error', attr, addName, txtRecord);
     }
 }
-
-//------------------------------------------------------------------------------
-// Service browsing
-//------------------------------------------------------------------------------
-
 /**
  * Browses for services
  */
 MDNSRegistry.prototype._browseForAttributes = function(dattrs) {
-    for (var attr in dattrs.device) {
-        if (!this.browsers.device[attr]) {
-            if (attr === 'status') {
-                this._browseForStatus(this, constants.globals.NodeType.DEVICE, dattrs.device.status);
-            } else {
-                this._browse(this, attr, constants.globals.NodeType.DEVICE, dattrs.device[attr]);
-            }
-        } else {
-            this.browsers.device[attr].start();
-        }
-    }
-
-    for (var attr in dattrs.fog) {
-        if (!this.browsers.fog[attr]) {
-            if (attr === 'status') {
-                this._browseForStatus(this, constants.globals.NodeType.FOG, dattrs.fog.status);
-            } else {
-                this._browse(this, attr, constants.globals.NodeType.FOG, dattrs.fog[attr]);
-            }
-        } else {
-            this.browsers.fog[attr].start();
-        }
-    }
-
-    for (var attr in dattrs.cloud) {
-        if (!this.browsers.cloud[attr]) {
-            if (attr === 'status') {
-                this._browseForStatus(this, constants.globals.NodeType.CLOUD, dattrs.cloud.status);
-            } else {
-                this._browse(this, attr, constants.globals.NodeType.CLOUD, dattrs.cloud[attr]);
+    ['device', 'fog', 'cloud'].map(
+        (x) => {
+            for (var attr in dattrs[x]) {
+                if(dattrs[x].hasOwnProperty(attr)) {
+                    if (!this.browsers[x][attr]) {
+                        if (attr === 'status') {
+                            this._browseForStatus(this, constants.globals.NodeType.DEVICE, dattrs[x].status);
+                        } else {
+                            this._browse(this, attr, constants.globals.NodeType.DEVICE, dattrs[x][attr]);
+                        }
+                    } else {
+                        this.browsers[x][attr].start();
+                    }
+                }
             }
-        } else {
-            this.browsers.cloud[attr].start();
         }
-    }
+    );
 }
-
 /**
  * Prep a browser to browse for any attibute except for status
  */
 MDNSRegistry.prototype._browse = function(self, attr, machType, events) {
+
     var browser = mdns.createBrowser(mdns.tcp(self.app + '-' + machType[0] + '-' + attr),
 					{resolverSequence: sequence});
 
@@ -207,11 +287,9 @@ MDNSRegistry.prototype._browse = function(self, attr, machType, events) {
         const parsedMsg = JSON.parse(service.txtRecord.msg);
         self.emit('discovery', attr, events.onAdd, service.name, parsedMsg.payload, parsedMsg.id);
     });
-
     browser.on('serviceDown', function(service) {
         self.emit('attr-removed', attr, events.onRemove, service.name);
     });
-
     browser.on('error', function(err) {
         browser.stop();
         self.emit('browser-error', attr, machType, events);
@@ -219,11 +297,11 @@ MDNSRegistry.prototype._browse = function(self, attr, machType, events) {
 
     browser.start();
 }
-
 /**
  * Prep a browser to browse for the status attribute
  */
 MDNSRegistry.prototype._browseForStatus = function(self, machType, events) {
+
     var browser = mdns.createBrowser(mdns.tcp(self.app + '-' + machType[0] + '-status'),
 					{resolverSequence: sequence});
 
@@ -253,106 +331,5 @@ MDNSRegistry.prototype._browseForStatus = function(self, machType, events) {
     browser.start();
 }
 
-//==============================================================================
-// Add and discover attributes
-//==============================================================================
-
-/**
- * Adds attributes by starting ads.
- * attrs - an object of attributes
- * seqval - the ID to publish with the attributes for deduplication purposes
- *          on the receiving node
- */
-MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
-    if (this.started) {
-        this._createAdvertisements(attrs, seqval);
-    } else {
-        for (var attr in attrs)
-            if(attrs.hasOwnProperty(attr))
-                this.attrs[attr] = { data: attrs[attr], seqval: seqval };
-        }
-    }
-}
-
-/**
- * Removes attributes by stopping the advertisements
- */
-MDNSRegistry.prototype.removeAttributes = function(attrs) {
-    if (this.started) {
-        for (var i = 0; i < attrs.length; i++) {
-            // stop and remove the advertisement
-            if (this.ads[attrs[i]]) {
-                this.ads[attrs[i]].stop();
-                // we delete the ad object because even if we start advertising with the
-                // same service name in the future, the value we advertise may be different
-                delete this.ads[attrs[i]];
-            }
-        }
-    } else {
-        for (var i = 0; i < attrs.length; i++) {
-            delete this.attrs[attrs[i]];
-        }
-    }
-}
-
-/**
- * Alias for _browseForAttributes, i.e. discovers attributes by starting browsers
- */
-MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
-    if (this.started) {
-        this._browseForAttributes(dattrs);
-    } else {
-        ['device', 'fog', 'cloud'].map(
-                x => {
-                    for (var attr in dattrs[x])
-                        if(dattrs[x].hasOwnProperty(attr))
-                            this.attrsToDiscover[x][attr] = dattrs[x][attr];
-                }
-        );
-    }
-}
-
-/**
- * Stops making discoveries by stopping browsers
- */
-MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
-    ['device', 'fog', 'cloud'].map(
-            x => {
-                if (dattrs[x]) {
-                    for (var i = 0; i < dattrs[x].length; i++) {
-                        if (this.started) {
-                            // stop the browser
-                            if (this.browsers[x][dattrs[x][i]]) {
-                                this.browsers[x][dattrs[x][i]].stop();
-                            }
-                        } else {
-                            delete this.attrsToDiscover[x][dattrs[x][i]];
-                        }
-                    }
-                }
-            }
-    );
-}
-
-/**
- * mDNS cleanup
- * Stop all advertising and browsing
- */
-MDNSRegistry.prototype.quit = function() {
-    // stop ads
-    for (var attr in this.ads) {
-        this.ads[attr].stop();
-    }
-
-    // stop browsers
-    ['device', 'fog', 'cloud'].map(
-            x => {
-                for (var attr in this.browsers.device)
-                    if(this.browsers[x].hasOwnProperty(attr))
-                        this.browsers[x][attr].stop();
-            }
-    );
-}
-
 /* exports */
 module.exports = MDNSRegistry;
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index b5ff924a..a837cf88 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -54,6 +54,12 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
     var self = this;
 
     /* connect event emitted on successful connection or reconnection */
+    /**
+     * TODO handle reconnect logic: on reconnect we aren t subscribed
+     * but when we do subscribe, we might get back our own LWT with offline status
+     * can this happen? if so, how should it be caught and handled?
+     * TODO
+     */
     this.client.on('connect', function (connack) {
 		/*
 		 * session is not present - subscriptions start from scratch

From 769f26b7bb28f0e0012d2b6d98b9f303fec3f49d Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 8 Jun 2018 14:46:38 -0400
Subject: [PATCH 25/62] JDISC: Modifiying mDNSreg (in progress), bug fix for
 jreg

---
 lib/jdiscovery/jregistrar.js   |   7 +-
 lib/jdiscovery/mdnsregistry.js | 234 ++++++++++-----------------------
 2 files changed, 72 insertions(+), 169 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index de8d4dce..3540a73d 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -105,6 +105,7 @@ Registrar.prototype.constructor = Registrar;
  *   attrsToDiscover: as in this.discoverAttributes
  */
 Registrar.prototype.registerAndDiscover = function(options) {
+
     if (this.started)
         return;
 
@@ -115,7 +116,7 @@ Registrar.prototype.registerAndDiscover = function(options) {
     this.setAttributes(
     {
         status: {
-            port: port,
+            port: this.port,
             ip: this._getIPv4Address()
         }
     });
@@ -166,11 +167,11 @@ Registrar.prototype.registerAndDiscover = function(options) {
             self.emit('error', info);
         });
         x.on('discovery', function(attr, event, id, data, seqval) {
-            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, x.protocol);
+            self._respondToDiscoveryEvent.call(self, attr, event, self.id, data, seqval, x.protocol);
         });
 
         x.on('attr-removed', function(attr, event, id, seqval) {
-            self._respondToRemovalEvent.call(self, attr, event, id, seqval, x.protocol);
+            self._respondToRemovalEvent.call(self, attr, event, self.id, seqval, x.protocol);
         });
     });
 
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 80bd4901..237a63f2 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -15,14 +15,14 @@ function MDNSRegistry(app, type, id, port) {
     this.protocol = constants.globals.Protocol.MDNS;
 
     this.ads = {};
-    this.attrs = {};
+    this.attrsToAdvertise = {};
 
     this.browsers = {
         device: {},
         fog: {},
         cloud: {}
     };
-    this.attrsToDiscover = {
+    this.attrsToBrowse = {
         device: {},
         fog: {},
         cloud: {}
@@ -47,21 +47,10 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
 
     if(this.started)
         return;
-
-    // start any ads and browsers created before this function was called
-    for (var attr in this.attrs) {
-        if(this.attrs.hasOwnProperty(attr)) {
-            let clone = {};
-            clone[attr] = this.attrs[attr].data;
-            this._createAdvertisements(clone, this.attrs[attr].seqval);
-        }
-    }
-    this.discoverAttributes(this.attrsToDiscover);
-
     this.started = true;
 }
 /**
- * Adds attributes by starting ads.
+ * Sets attributes by starting ads or modifying them.
  * attrs - an object of attributes
  * seqval - the ID to publish with the attributes for deduplication purposes
  *          on the receiving node
@@ -70,30 +59,24 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
     if (this.started) {
         this._createAdvertisements(attrs, seqval);
     } else {
-        for (var attr in attrs)
-            if(attrs.hasOwnProperty(attr))
-                this.attrs[attr] = { data: attrs[attr], seqval: seqval };
-        }
+        let self = this;
+        setTimeout(self.setAttributes.bind(self), 
+            constants.mdns.retryInterval,
+            attrs, seqval);
     }
 }
 /**
  * Removes attributes by stopping the advertisements
  */
-MDNSRegistry.prototype.removeAttributes = function(attrs) {
-    if (this.started) {
-        for (var i = 0; i < attrs.length; i++) {
-            // stop and remove the advertisement
-            if (this.ads[attrs[i]]) {
-                this.ads[attrs[i]].stop();
-                // we delete the ad object because even if we start advertising with the
-                // same service name in the future, the value we advertise may be different
-                delete this.ads[attrs[i]];
+MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
+    for (var attr in attrs) {
+        if (attrs.hasOwnProperty(attr)) {
+            if (this.attrsToAdvertise[attr] && this.ads[attr]) {
+                this.ads[attr].stop();
+                delete this.attrsToAdvertise[attr];
+                delete this.ads[attr];
             }
         }
-    } else {
-        for (var i = 0; i < attrs.length; i++) {
-            delete this.attrs[attrs[i]];
-        }
     }
 }
 /**
@@ -101,15 +84,24 @@ MDNSRegistry.prototype.removeAttributes = function(attrs) {
  */
 MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
     if (this.started) {
-        this._browseForAttributes(dattrs);
-    } else {
         ['device', 'fog', 'cloud'].map(
-                x => {
-                    for (var attr in dattrs[x])
-                        if(dattrs[x].hasOwnProperty(attr))
-                            this.attrsToDiscover[x][attr] = dattrs[x][attr];
+            (x) => {
+                for (var attr in dattrs[x]) {
+                    if(dattrs[x].hasOwnProperty(attr)) {
+                        if (!this.browsers[x][attr]) {
+                            this._createBrowse(attr, 
+                                constants.globals.NodeType.DEVICE, 
+                                dattrs[x][attr]);
+                        }
+                    }
                 }
+            }
         );
+    } else {
+        let self = this;
+        setTimeout(self.discoverAttributes.bind(self), 
+            constants.mdns.retryInterval,
+            dattrs);
     }
 }
 /**
@@ -117,20 +109,19 @@ MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
  */
 MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
     ['device', 'fog', 'cloud'].map(
-            x => {
-                if (dattrs[x]) {
-                    for (var i = 0; i < dattrs[x].length; i++) {
-                        if (this.started) {
-                            // stop the browser
-                            if (this.browsers[x][dattrs[x][i]]) {
-                                this.browsers[x][dattrs[x][i]].stop();
-                            }
-                        } else {
-                            delete this.attrsToDiscover[x][dattrs[x][i]];
+        x => {
+            if (dattrs[x]) {
+                for(var attr in dattrs[x]) {
+                    if (dattrs[x].hasOwnProperty(attr)) {
+                        if (this.attrsToBrowse[x][attr] && this.browsers[x][attr]) {
+                            this.browsers[x][attr].stop();
+                            delete this.attrsToBrowse[x][attr];
+                            delete this.browsers[x][attr];
                         }
                     }
                 }
             }
+        }
     );
 }
 /**
@@ -138,39 +129,24 @@ MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
  * Stop all advertising and browsing
  */
 MDNSRegistry.prototype.quit = function() {
+
     // stop ads
     for (var attr in this.ads) {
-        this.ads[attr].stop();
+        if(this.ads.hasOwnProperty(attr)) {
+            this.ads[attr].stop();
+        }
     }
 
     // stop browsers
     ['device', 'fog', 'cloud'].map(
-            x => {
-                for (var attr in this.browsers.device)
+            (x) => {
+                for (var attr in this.browsers[x])
                     if(this.browsers[x].hasOwnProperty(attr))
                         this.browsers[x][attr].stop();
             }
     );
 }
 
-
-/* TO HANDLE INTERNALLY TODO
-        this.mdnsRegistry.on('ad-error', function(attr, adName, txtRecord) {
-            // an ad failed - try again after some time
-            setTimeout(self.mdnsRegistry._createAdvertisementWithRetries, constants.mdns.longRetryInterval,
-                        self.mdnsRegistry, attr, adName, txtRecord, 0);
-        });
-
-        this.mdnsRegistry.on('browser-error', function(attr, type, events) {
-            // a browser failed - try again after some time
-            if (attr === 'status') {
-                setTimeout(self.mdnsRegistry._browseForStatus, constants.mdns.longRetryInterval, self.mdnsRegistry, type, events);
-            } else {
-                setTimeout(self.mdnsRegistry._browse, constants.mdns.longRetryInterval, self.mdnsRegistry, attr, type, events);
-            }
-        });
-*/
-
 /**
  * _PRIVATE HELPERS
  */
@@ -205,127 +181,53 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, dedupeId) {
         this._createAdvertisementWithRetries(this, attr, adName, txtRecord, constants.mdns.retries);
     }
 }
-/**
- * Helper
- */
 MDNSRegistry.prototype._createAdvertisementWithRetries = function(self, attr, adName, txtRecord, retries) {
-    var ad = mdns.createAdvertisement(mdns.tcp(adName), self.port, {name: this.id, txtRecord: txtRecord}, function(err, service) {
-        if (err) {
-            self._handleError(self, err, ad, attr, adName, txtRecord, retries);
-        } else {
-            self.ads[attr] = ad;
-        }
-    });
-    ad.start();
-}
-
-/**
- * helper function for handling advertisement errors
- */
-MDNSRegistry.prototype._handleError = function(self, err, ad, attr, adName, txtRecord, retries) {
-    switch (err.errorCode) {
-        // if the error is unknown, then the mdns daemon may currently be down,
-        // so try again after some time
-        case mdns.kDNSServiceErr_Unknown:
-            logger.log.error('Unknown service error: ' + err);
-            if (retries === 0) {
-                logger.log.warning('Exhaused all advertisement retries.');
-                // make sure the ad is stopped
-                ad.stop();
-                // emit the ad info, so the registrar can decide if it wants to retry later
-                self.emit('ad-error', attr, addName, txtRecord);
+    var ad = mdns.createAdvertisement(mdns.tcp(adName), 
+        self.port, {name: this.id, txtRecord: txtRecord}, 
+        function(err, service) {
+            if (err) {
+                self._handleError(self, err, ad, attr, adName, txtRecord, retries);
             } else {
-                setTimeout(self._createAdvertisementWithRetries, constants.mdns.retryInterval, self, attr, adName, txtRecord, retries - 1);
-            }
-            break;
-        default:
-            logger.log.error('Unhandled service error: ' + err);
-            // make sure the ad is stopped
-            ad.stop();
-            self.emit('ad-error', attr, addName, txtRecord);
-    }
-}
-/**
- * Browses for services
- */
-MDNSRegistry.prototype._browseForAttributes = function(dattrs) {
-    ['device', 'fog', 'cloud'].map(
-        (x) => {
-            for (var attr in dattrs[x]) {
-                if(dattrs[x].hasOwnProperty(attr)) {
-                    if (!this.browsers[x][attr]) {
-                        if (attr === 'status') {
-                            this._browseForStatus(this, constants.globals.NodeType.DEVICE, dattrs[x].status);
-                        } else {
-                            this._browse(this, attr, constants.globals.NodeType.DEVICE, dattrs[x][attr]);
-                        }
-                    } else {
-                        this.browsers[x][attr].start();
-                    }
-                }
+                self.ads[attr] = ad;
             }
         }
     );
+    ad.start();
 }
 /**
- * Prep a browser to browse for any attibute except for status
+ * Prep a browser to browse for an attibute
  */
-MDNSRegistry.prototype._browse = function(self, attr, machType, events) {
+MDNSRegistry.prototype._createBrowser = function(attr, type, events) {
 
-    var browser = mdns.createBrowser(mdns.tcp(self.app + '-' + machType[0] + '-' + attr),
+    let browser = mdns.createBrowser(
+                    mdns.tcp(this.app + '-' + type + '-' + attr),
 					{resolverSequence: sequence});
-
-    self.browsers[machType][attr] = browser;
+    this.browsers[type][attr] = browser;
 
     browser.on('serviceUp', function(service) {
         // ignore our own services
-        if (service.name == self.id) {
+        if (service.name == this.id) {
             return;
         }
 
         // emit a discovery event!
-        const parsedMsg = JSON.parse(service.txtRecord.msg);
-        self.emit('discovery', attr, events.onAdd, service.name, parsedMsg.payload, parsedMsg.id);
-    });
-    browser.on('serviceDown', function(service) {
-        self.emit('attr-removed', attr, events.onRemove, service.name);
-    });
-    browser.on('error', function(err) {
-        browser.stop();
-        self.emit('browser-error', attr, machType, events);
-    });
-
-    browser.start();
-}
-/**
- * Prep a browser to browse for the status attribute
- */
-MDNSRegistry.prototype._browseForStatus = function(self, machType, events) {
-
-    var browser = mdns.createBrowser(mdns.tcp(self.app + '-' + machType[0] + '-status'),
-					{resolverSequence: sequence});
-
-    self.browsers[machType].status = browser;
-
-    browser.on('serviceUp', function(service) {
-        // ignore our own services
-        if (service.name == self.id) {
-            return;
+        let parsed = JSON.parse(service.txtRecord.msg);
+        if(attr == 'status') {
+            this.emit('discovery', attr, events.online, service.name, parsed.data, parsed.id);
+        } else {
+            this.emit('discovery', attr, events.onAdd, service.name, parsed.data, parsed.id);
         }
-
-        // emit a discovery event!
-        const parsedMsg = JSON.parse(service.txtRecord.msg);
-        self.emit('discovery', 'status', events.online, service.name, parsedMsg.payload, parsedMsg.id);
     });
-
     browser.on('serviceDown', function(service) {
-        // pass a dedupeId of zero for node down events
-        self.emit('discovery', 'status', events.offline, service.name, 'offline', 0);
+        if(attr == 'status') { // TODO
+            this.emit('discovery', attr, events.offline, service.name, 'offline', 0);
+        } else {
+            this.emit('attr-removed', attr, events.onRemove, service.name);
+        }
     });
-
     browser.on('error', function(err) {
         browser.stop();
-        self.emit('browser-error', 'status', machType, events);
+        self.emit('browser-error', attr, machType, events);
     });
 
     browser.start();

From 335f58ac5bd2411750611084f5315e4feafcdf5c Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 11 Jun 2018 13:32:00 -0400
Subject: [PATCH 26/62] JDISC: mDNS registry structural update done, bug with
 advs..

---
 lib/jdiscovery/apps/tester.js  |  2 +-
 lib/jdiscovery/mdnsregistry.js | 72 ++++++++++++++--------------------
 2 files changed, 30 insertions(+), 44 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 6a10dc6a..015d20ee 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -18,7 +18,7 @@ console.log('-----------------------------------------------');
 console.log();
 
 var reggie = new Registrar(app, machType, id, port,
-                           { protocols: { mqtt: true, mdns: false } });
+                           { protocols: { mqtt: false, mdns: true } });
 
 // Default discoveries
 
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 237a63f2..2235a693 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -56,6 +56,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
  *          on the receiving node
  */
 MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
+    console.log('mDNS-reg: called setAttributes');
     if (this.started) {
         this._createAdvertisements(attrs, seqval);
     } else {
@@ -89,7 +90,7 @@ MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
                 for (var attr in dattrs[x]) {
                     if(dattrs[x].hasOwnProperty(attr)) {
                         if (!this.browsers[x][attr]) {
-                            this._createBrowse(attr, 
+                            this._createBrowser(attr, 
                                 constants.globals.NodeType.DEVICE, 
                                 dattrs[x][attr]);
                         }
@@ -136,7 +137,6 @@ MDNSRegistry.prototype.quit = function() {
             this.ads[attr].stop();
         }
     }
-
     // stop browsers
     ['device', 'fog', 'cloud'].map(
             (x) => {
@@ -154,45 +154,31 @@ MDNSRegistry.prototype.quit = function() {
 /**
  * Creates advertisements for the provided attributes
  */
-MDNSRegistry.prototype._createAdvertisements = function(attrs, dedupeId) {
+MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
+    console.log('mDNS-reg: _createAdvs called, this:', JSON.stringify(this));
     for (var attr in attrs) {
-        // stop advertisement of existing attr (we'll just replace it)
-        if (this.ads[attr]) {
-            this.ads[attr].stop();
-            delete this.ads[attr];
-        }
-        var adName = this.app + '-' + this.machType[0] + '-' + attr;
-        var txtRecord;
-        if (attrs[attr] instanceof Function) {
-            txtRecord = {
-                msg: JSON.stringify({
-                    payload: attrs[attr](),
-                    id: dedupeId
-                })
-            };
-        } else {
-            txtRecord = {
-                msg: JSON.stringify({
-                    payload: attrs[attr],
-                    id: dedupeId
-                })
-            };
-        }
-        this._createAdvertisementWithRetries(this, attr, adName, txtRecord, constants.mdns.retries);
-    }
-}
-MDNSRegistry.prototype._createAdvertisementWithRetries = function(self, attr, adName, txtRecord, retries) {
-    var ad = mdns.createAdvertisement(mdns.tcp(adName), 
-        self.port, {name: this.id, txtRecord: txtRecord}, 
-        function(err, service) {
-            if (err) {
-                self._handleError(self, err, ad, attr, adName, txtRecord, retries);
-            } else {
-                self.ads[attr] = ad;
+        if (attrs.hasOwnProperty(attr)) {
+            // stop advertisement of existing attr (we'll just replace it)
+            if (this.ads[attr]) {
+                this.ads[attr].stop();
+                delete this.ads[attr];
             }
+            let name = this.app + '-' + this.type + '-' + attr;
+            let txtRecord = {
+                    msg: JSON.stringify({
+                        data: attrs[attr],
+                        seqval: seqval
+                    })
+                };
+            this.ads[attr] = mdns.createAdvertisement(
+                mdns.tcp(name), 
+                this.port, 
+                {name: this.id, txtRecord: txtRecord});
+            console.log('Starting adv', JSON.stringify(txtRecord));
+            this.ads[attr].start();
+            console.log(JSON.stringify(this.ads[attr]));
         }
-    );
-    ad.start();
+    }
 }
 /**
  * Prep a browser to browse for an attibute
@@ -213,21 +199,21 @@ MDNSRegistry.prototype._createBrowser = function(attr, type, events) {
         // emit a discovery event!
         let parsed = JSON.parse(service.txtRecord.msg);
         if(attr == 'status') {
-            this.emit('discovery', attr, events.online, service.name, parsed.data, parsed.id);
+            this.emit('discovery', attr, events.online, service.name, parsed.data, parsed.seqval);
         } else {
-            this.emit('discovery', attr, events.onAdd, service.name, parsed.data, parsed.id);
+            this.emit('discovery', attr, events.onAdd, service.name, parsed.data, parsed.seqval);
         }
     });
     browser.on('serviceDown', function(service) {
-        if(attr == 'status') { // TODO
-            this.emit('discovery', attr, events.offline, service.name, 'offline', 0);
+        if(attr == 'status') {
+            this.emit('discovery', attr, events.offline, service.name, 'offline', undefined);
         } else {
             this.emit('attr-removed', attr, events.onRemove, service.name);
         }
     });
     browser.on('error', function(err) {
         browser.stop();
-        self.emit('browser-error', attr, machType, events);
+        self.emit('browser-error', attr, type, events);
     });
 
     browser.start();

From c6708373d11b4ba502b29ce594a010e1dc039656 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 12 Jun 2018 11:29:08 -0400
Subject: [PATCH 27/62] Basic service discovery working! Trouble with dup elim

... because all removals from the mdns registry and down statuses
... for mqtt cannot use seq #s in a meaningful way, these messages
... are still being delivered to the app multiple times
... SOLUTION TBD
... ow, it seems to work ;)
---
 lib/jdiscovery/apps/tester.js  |  2 +-
 lib/jdiscovery/jregistrar.js   |  3 +-
 lib/jdiscovery/mdnsregistry.js | 57 +++++++++++++++++++++-------------
 samples                        |  2 +-
 tools                          |  2 +-
 5 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 015d20ee..987a4809 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -18,7 +18,7 @@ console.log('-----------------------------------------------');
 console.log();
 
 var reggie = new Registrar(app, machType, id, port,
-                           { protocols: { mqtt: false, mdns: true } });
+                           { protocols: { mqtt: true, mdns: true } });
 
 // Default discoveries
 
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 3540a73d..37e0e3d3 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -345,7 +345,8 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
     if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
        || this.dt[id]['attrs'][attr]['seqval'] < seqval
        || (  (this.dt[id]['attrs'][attr]['seqval'] > (Number.MAX_SAFE_INTEGER/2))
-          && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
+          && (seqval < (Number.MAX_SAFE_INTEGER/1024)))
+       || seqval === -1) // XXX mdns attr-removed' bypass XXX
     {
         delete this.dt.id.attr;
         this.emit(event, id, protocol);
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 2235a693..6ea02e31 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -90,9 +90,9 @@ MDNSRegistry.prototype.discoverAttributes = function(dattrs) {
                 for (var attr in dattrs[x]) {
                     if(dattrs[x].hasOwnProperty(attr)) {
                         if (!this.browsers[x][attr]) {
-                            this._createBrowser(attr, 
-                                constants.globals.NodeType.DEVICE, 
-                                dattrs[x][attr]);
+                            this.attrsToBrowse[x][attr] = dattrs[x][attr];
+                            this._createBrowser(attr,
+                                constants.globals.NodeType[x.toUpperCase()]);
                         }
                     }
                 }
@@ -158,62 +158,75 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
     console.log('mDNS-reg: _createAdvs called, this:', JSON.stringify(this));
     for (var attr in attrs) {
         if (attrs.hasOwnProperty(attr)) {
-            // stop advertisement of existing attr (we'll just replace it)
+            // Update data structures
+            this.attrsToAdvertise[attr] = attrs[attr];
             if (this.ads[attr]) {
                 this.ads[attr].stop();
                 delete this.ads[attr];
             }
+            // Create adv formatted info
             let name = this.app + '-' + this.type + '-' + attr;
             let txtRecord = {
-                    msg: JSON.stringify({
-                        data: attrs[attr],
-                        seqval: seqval
-                    })
-                };
+                data: JSON.stringify(attrs[attr]),
+                seqval: seqval
+            };
             this.ads[attr] = mdns.createAdvertisement(
                 mdns.tcp(name), 
                 this.port, 
                 {name: this.id, txtRecord: txtRecord});
             console.log('Starting adv', JSON.stringify(txtRecord));
             this.ads[attr].start();
-            console.log(JSON.stringify(this.ads[attr]));
         }
     }
 }
 /**
  * Prep a browser to browse for an attibute
  */
-MDNSRegistry.prototype._createBrowser = function(attr, type, events) {
+MDNSRegistry.prototype._createBrowser = function(attr, type) {
 
     let browser = mdns.createBrowser(
                     mdns.tcp(this.app + '-' + type + '-' + attr),
 					{resolverSequence: sequence});
     this.browsers[type][attr] = browser;
 
+    var self = this;
     browser.on('serviceUp', function(service) {
+        // DEBUG
+        console.log("Service discovered: ", JSON.stringify(service));
+
         // ignore our own services
-        if (service.name == this.id) {
+        if (service.name == self.id) {
             return;
         }
-
         // emit a discovery event!
-        let parsed = JSON.parse(service.txtRecord.msg);
-        if(attr == 'status') {
-            this.emit('discovery', attr, events.online, service.name, parsed.data, parsed.seqval);
-        } else {
-            this.emit('discovery', attr, events.onAdd, service.name, parsed.data, parsed.seqval);
-        }
+        self.emit('discovery', attr,
+            (attr == 'status')
+                ? self.attrsToBrowse[type][attr].online
+                : self.attrsToBrowse[type][attr].onAdd,
+            service.name,
+            JSON.parse(service.txtRecord.data),
+            service.txtRecord.seqval);
     });
     browser.on('serviceDown', function(service) {
+        // DEBUG
+        console.log("Service lost: ", JSON.stringify(service));
+
         if(attr == 'status') {
-            this.emit('discovery', attr, events.offline, service.name, 'offline', undefined);
+            self.emit('discovery', attr,
+                self.attrsToBrowse[type][attr].offline,
+                service.name, 'offline', undefined);
         } else {
-            this.emit('attr-removed', attr, events.onRemove, service.name);
+            // Since we don't have seqvals for attr-removed
+            // messages with mdns, use -1 as a special signal
+            // to jregistar
+            self.emit('attr-removed', attr,
+                self.attrsToBrowse[type][attr].onRemove,
+                service.name, -1);
         }
     });
     browser.on('error', function(err) {
         browser.stop();
-        self.emit('browser-error', attr, type, events);
+        self.emit('error', undefined);
     });
 
     browser.start();
diff --git a/samples b/samples
index e82a13ff..935f3919 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit e82a13ff94f74829c0d2cde9f34e2a7d49b2fba8
+Subproject commit 935f3919737b861552f5f5e2c0d44bd56f673d54
diff --git a/tools b/tools
index 178046f0..493a2d9f 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit 178046f0d9def124806975b0f23ba66401a49618
+Subproject commit 493a2d9fe90ccc04baacaa78eb84e74191edf877

From c524484ec8da4e81b99233c2609426af0faf9f9f Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 14 Jun 2018 12:52:22 -0400
Subject: [PATCH 28/62] JDISC: Modifying mDNS attr update/removal policies (in
 prog)

---
 lib/jdiscovery/jregistrar.js   |  10 +++-
 lib/jdiscovery/mdnsregistry.js | 102 ++++++++++++++++++++++++++++++++-
 samples                        |   2 +-
 tools                          |   2 +-
 4 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 37e0e3d3..23bb5bac 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -306,8 +306,15 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
     // This could potentially be solved by subscribing to status topics with QoS 2
     // The above proposed solution is : NOT IMPLEMENTED due to how unlikely this is to happen.
     if (attr === 'status' && data === 'offline') {
+
+        // ignore this if it has already been detected
+        if(this.dt[id]['attrs']['status']['data'] == 'offline')
+            return;
+
+        // o.w.
         this.dt[id]['attrs']['status']['data'] = 'offline';
         // Set seqvals for all attributes of node to -1 in case the node crashed
+        // ... this means any attr update  will be accepted when the node does so
         for(var key in this.dt[id]['attrs'])
             if(this.dt[id]['attrs'].hasOwnProperty(key))
                 this.dt[id]['attrs'][key]['seqval'] = -1;
@@ -345,8 +352,7 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
     if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
        || this.dt[id]['attrs'][attr]['seqval'] < seqval
        || (  (this.dt[id]['attrs'][attr]['seqval'] > (Number.MAX_SAFE_INTEGER/2))
-          && (seqval < (Number.MAX_SAFE_INTEGER/1024)))
-       || seqval === -1) // XXX mdns attr-removed' bypass XXX
+          && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
     {
         delete this.dt.id.attr;
         this.emit(event, id, protocol);
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 6ea02e31..756a7b9b 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -28,6 +28,12 @@ function MDNSRegistry(app, type, id, port) {
         cloud: {}
     }
 
+    this.attrChangeBrowsers = {};
+    this.attrChangeCache = {};
+
+    this.attrRemovalBrowsers = {};
+    this.attrRemovalCache = {};
+
     this.started = false;
 }
 
@@ -35,7 +41,6 @@ function MDNSRegistry(app, type, id, port) {
 MDNSRegistry.prototype = Object.create(Registry.prototype);
 MDNSRegistry.prototype.constructor = MDNSRegistry;
 
-
 /**
  * REGISTRY INTERFACE METHODS
  */
@@ -47,6 +52,60 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
 
     if(this.started)
         return;
+    
+    var self = this;
+    ['device', 'fog', 'cloud'].map(
+        (x) => {
+            this.attrChangeBrowsers[x] = 
+                mdns.createBrowser(
+                    mdns.tcp(this.app + '-' + (x) + '-attrchg'),
+                    {resolverSequence: sequence});
+            this.attrChangeBrowsers[x].on('serviceUp', function(service) {
+                // ignore our own services
+                if (service.name == self.id) {
+                    return;
+                }
+                // update attrChangeCache
+                let attrs = JSON.parse(service.txtRecord.data;
+                let seqval = service.txtRecord.seqval;
+                for (var attr in attrs)
+                    if (attrs.hasOwnProperty(attr))
+                        this.attrChangeCache[attr] = seqval;
+            });
+            this.attrChangeBrowsers[x].on('serviceDown', function(service) {
+                // do nothing!
+            });
+            this.attrChangeBrowsers[x].on('error', function(err) {
+                browser.stop();
+                this.quit();
+            });
+            this.attrChangeBrowsers[x].start();
+
+            this.attrRemovalBrowsers[x] =
+                mdns.createBrowser(
+                    mdns.tcp(this.app + '-' + (x) + '-attrrem'),
+                    {resolverSequence: sequence});
+            this.attrRemovalBrowsers[x].on('serviceUp', function(service) {
+                // ignore our own services
+                if (service.name == self.id) {
+                    return;
+                }
+                // update attrChangeCache
+                let attrs = JSON.parse(service.txtRecord.data;
+                for (var attr in attrs)
+                    if (attrs.hasOwnProperty(attr))
+                        this.attrRemovalCache[attr] = '';
+            });
+            this.attrRemovalBrowsers[x].on('serviceDown', function(service) {
+                // do nothing!
+            });
+            this.attrRemovalBrowsers[x].on('error', function(err) {
+                browser.stop();
+                this.quit();
+            });
+            this.attrRemovalBrowsers[x].start();
+        }
+    );
     this.started = true;
 }
 /**
@@ -70,6 +129,17 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
  * Removes attributes by stopping the advertisements
  */
 MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
+    // create attrrem advertisement
+    let attrRemovalAd = mdns.createAdvertisement(
+        mdns.tcp(this.app + '-' + this.type + '-attrrem'),
+        this.port,
+        {
+            name: this.id, 
+            txtRecord: {
+                data: JSON.stringify(this.attrs.keys()),
+            }
+        }
+    );
     for (var attr in attrs) {
         if (attrs.hasOwnProperty(attr)) {
             if (this.attrsToAdvertise[attr] && this.ads[attr]) {
@@ -131,12 +201,25 @@ MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
  */
 MDNSRegistry.prototype.quit = function() {
 
+    // create attrrem advertisement
+    let attrRemovalAd = mdns.createAdvertisement(
+        mdns.tcp(this.app + '-' + this.type + '-attrrem'),
+        this.port,
+        {
+            name: this.id, 
+            txtRecord: {
+                data: JSON.stringify(this.attrsToAdvertise.keys()),
+            }
+        }
+    );
     // stop ads
     for (var attr in this.ads) {
         if(this.ads.hasOwnProperty(attr)) {
             this.ads[attr].stop();
         }
     }
+    // stop attrrem advertisement
+    attrRemovalAd.stop();
     // stop browsers
     ['device', 'fog', 'cloud'].map(
             (x) => {
@@ -155,7 +238,21 @@ MDNSRegistry.prototype.quit = function() {
  * Creates advertisements for the provided attributes
  */
 MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
+
     console.log('mDNS-reg: _createAdvs called, this:', JSON.stringify(this));
+    // create attrChange advertisement
+    let attrChangeAd = mdns.createAdvertisement(
+        mdns.tcp(this.app + '-' + this.type + '-attrchg'),
+        this.port,
+        {
+            name: this.id, 
+            txtRecord: {
+                data: JSON.stringify(attrs.keys()),
+                seqval: seqval
+            }
+        }
+    );
+    // stop old advertisements and create new ones for updated attrs
     for (var attr in attrs) {
         if (attrs.hasOwnProperty(attr)) {
             // Update data structures
@@ -174,10 +271,13 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
                 mdns.tcp(name), 
                 this.port, 
                 {name: this.id, txtRecord: txtRecord});
+
             console.log('Starting adv', JSON.stringify(txtRecord));
             this.ads[attr].start();
         }
     }
+    // stop attrChange advertisement
+    attrChangeAd.stop();
 }
 /**
  * Prep a browser to browse for an attibute
diff --git a/samples b/samples
index 935f3919..e82a13ff 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit 935f3919737b861552f5f5e2c0d44bd56f673d54
+Subproject commit e82a13ff94f74829c0d2cde9f34e2a7d49b2fba8
diff --git a/tools b/tools
index 493a2d9f..178046f0 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit 493a2d9fe90ccc04baacaa78eb84e74191edf877
+Subproject commit 178046f0d9def124806975b0f23ba66401a49618

From 9ba618cf80cf91532ac574483e0855a2ade6426f Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 14 Jun 2018 17:15:32 -0400
Subject: [PATCH 29/62] JDISC: Finished mDNS policy change (not tested)

---
 lib/jdiscovery/jregistrar.js   | 10 +++++-
 lib/jdiscovery/mdnsregistry.js | 61 +++++++++++++++++++++++-----------
 lib/jdiscovery/mqttregistry.js |  4 +--
 samples                        |  2 +-
 tools                          |  2 +-
 5 files changed, 54 insertions(+), 25 deletions(-)

diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index 23bb5bac..aa3f7a4e 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -271,7 +271,15 @@ Registrar.prototype._modifyDattributes = function(fun, dattrs) {
         x => {if(x) x[fun](dattrs);}
     );
 }
-
+/**
+ * Exit from network cleanly
+ */
+Registrar.prototype.quit = function() {
+    let seqval = this._getSeqVal();
+    [this.mqttRegistry, this.mdnsRegistry].map(
+        x => {x.quit(seqval);}
+    );
+}
 
 /**
  * _PRIVATE HELPERS
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 756a7b9b..55429c60 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -70,14 +70,14 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                 let seqval = service.txtRecord.seqval;
                 for (var attr in attrs)
                     if (attrs.hasOwnProperty(attr))
-                        this.attrChangeCache[attr] = seqval;
+                        self.attrChangeCache[attr] = seqval;
             });
             this.attrChangeBrowsers[x].on('serviceDown', function(service) {
                 // do nothing!
             });
             this.attrChangeBrowsers[x].on('error', function(err) {
                 browser.stop();
-                this.quit();
+                self.quit();
             });
             this.attrChangeBrowsers[x].start();
 
@@ -92,16 +92,17 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                 }
                 // update attrChangeCache
                 let attrs = JSON.parse(service.txtRecord.data;
+                let seqval = service.txtRecord.seqval;
                 for (var attr in attrs)
                     if (attrs.hasOwnProperty(attr))
-                        this.attrRemovalCache[attr] = '';
+                        self.attrRemovalCache[attr] = seqval;
             });
             this.attrRemovalBrowsers[x].on('serviceDown', function(service) {
                 // do nothing!
             });
             this.attrRemovalBrowsers[x].on('error', function(err) {
                 browser.stop();
-                this.quit();
+                self.quit();
             });
             this.attrRemovalBrowsers[x].start();
         }
@@ -137,6 +138,7 @@ MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
             name: this.id, 
             txtRecord: {
                 data: JSON.stringify(this.attrs.keys()),
+                seqval: seqval
             }
         }
     );
@@ -149,6 +151,7 @@ MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
             }
         }
     }
+    attrRemovalAd.stop();
 }
 /**
  * Discovers attributes by starting browsers
@@ -199,7 +202,7 @@ MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
  * mDNS cleanup
  * Stop all advertising and browsing
  */
-MDNSRegistry.prototype.quit = function() {
+MDNSRegistry.prototype.quit = function(seqval) {
 
     // create attrrem advertisement
     let attrRemovalAd = mdns.createAdvertisement(
@@ -209,6 +212,7 @@ MDNSRegistry.prototype.quit = function() {
             name: this.id, 
             txtRecord: {
                 data: JSON.stringify(this.attrsToAdvertise.keys()),
+                seqval : seqval
             }
         }
     );
@@ -298,30 +302,47 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
         if (service.name == self.id) {
             return;
         }
-        // emit a discovery event!
-        self.emit('discovery', attr,
-            (attr == 'status')
-                ? self.attrsToBrowse[type][attr].online
-                : self.attrsToBrowse[type][attr].onAdd,
-            service.name,
-            JSON.parse(service.txtRecord.data),
-            service.txtRecord.seqval);
+        // check if we have heard of this discovery
+        if (self.attrChangeCache.hasOwnProperty(attr)) { 
+            // emit a discovery event!
+            self.emit('discovery', attr,
+                (attr == 'status')
+                    ? self.attrsToBrowse[type][attr].online
+                    : self.attrsToBrowse[type][attr].onAdd,
+                service.name,
+                JSON.parse(service.txtRecord.data),
+                service.txtRecord.seqval);
+            // clear attrChangeCache entry
+            delete self.attrChangeCache[attr];
+        }
     });
     browser.on('serviceDown', function(service) {
         // DEBUG
-        console.log("Service lost: ", JSON.stringify(service));
+        console.log("MDNSr: Service lost: ", JSON.stringify(service));
+
+        // ignore our own services
+        if (service.name == self.id) {
+            return;
+        }
+        // ignore attr-removals from attr content changes
+        if (self.attrChangeCache.hasOwnProperty(attr)) {
+            return;
+        }
 
         if(attr == 'status') {
             self.emit('discovery', attr,
                 self.attrsToBrowse[type][attr].offline,
                 service.name, 'offline', undefined);
         } else {
-            // Since we don't have seqvals for attr-removed
-            // messages with mdns, use -1 as a special signal
-            // to jregistar
-            self.emit('attr-removed', attr,
-                self.attrsToBrowse[type][attr].onRemove,
-                service.name, -1);
+            // for m-o disconnections: do nothing
+            // for advertised attr removals: propagate
+            if (self.attrRemovalCache.hasOwnProperty(attr)) {
+                let seqval = self.attrRemovalCache[attr];
+                delete self.attrRemovalCache[attr];
+                self.emit('attr-removed', attr,
+                    self.attrsToBrowse[type][attr].onRemove,
+                    service.name, seqval);
+            }
         }
     });
     browser.on('error', function(err) {
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index a837cf88..749d4e5f 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -123,10 +123,10 @@ MQTTRegistry.prototype.removeAttributes = function(attrs, seqval) {
  * --> Cleans up all publications on mqtt broker
  * --> Kills connection to broker
  */
-MQTTRegistry.prototype.quit = function() {
+MQTTRegistry.prototype.quit = function(seqval) {
     if (this.client && this.client.connected) {
         // unpublish on all attributes
-        this.removeAttributes(this.publishedAttrs);
+        this.removeAttributes(this.publishedAttrs, seqval);
         // end connection
         this.client.end(false);
     } else {
diff --git a/samples b/samples
index e82a13ff..935f3919 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit e82a13ff94f74829c0d2cde9f34e2a7d49b2fba8
+Subproject commit 935f3919737b861552f5f5e2c0d44bd56f673d54
diff --git a/tools b/tools
index 178046f0..493a2d9f 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit 178046f0d9def124806975b0f23ba66401a49618
+Subproject commit 493a2d9fe90ccc04baacaa78eb84e74191edf877

From ea6ca83819c458cfba9c9125af637baa1f5422ea Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 15 Jun 2018 13:16:54 -0400
Subject: [PATCH 30/62] JDISC: Fixed syntax for Object.keys()

---
 lib/jdiscovery/mdnsregistry.js | 12 ++++++------
 samples                        |  2 +-
 tools                          |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 55429c60..d0cf2a34 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -66,7 +66,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                     return;
                 }
                 // update attrChangeCache
-                let attrs = JSON.parse(service.txtRecord.data;
+                let attrs = JSON.parse(service.txtRecord.data);
                 let seqval = service.txtRecord.seqval;
                 for (var attr in attrs)
                     if (attrs.hasOwnProperty(attr))
@@ -91,7 +91,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                     return;
                 }
                 // update attrChangeCache
-                let attrs = JSON.parse(service.txtRecord.data;
+                let attrs = JSON.parse(service.txtRecord.data);
                 let seqval = service.txtRecord.seqval;
                 for (var attr in attrs)
                     if (attrs.hasOwnProperty(attr))
@@ -137,7 +137,7 @@ MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
         {
             name: this.id, 
             txtRecord: {
-                data: JSON.stringify(this.attrs.keys()),
+                data: JSON.stringify(Object.keys(this.attrs)),
                 seqval: seqval
             }
         }
@@ -211,7 +211,7 @@ MDNSRegistry.prototype.quit = function(seqval) {
         {
             name: this.id, 
             txtRecord: {
-                data: JSON.stringify(this.attrsToAdvertise.keys()),
+                data: JSON.stringify(Object.keys(this.attrsToAdvertise)),
                 seqval : seqval
             }
         }
@@ -243,7 +243,7 @@ MDNSRegistry.prototype.quit = function(seqval) {
  */
 MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
 
-    console.log('mDNS-reg: _createAdvs called, this:', JSON.stringify(this));
+    console.log('mDNS-reg: _createAdvs called');
     // create attrChange advertisement
     let attrChangeAd = mdns.createAdvertisement(
         mdns.tcp(this.app + '-' + this.type + '-attrchg'),
@@ -251,7 +251,7 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
         {
             name: this.id, 
             txtRecord: {
-                data: JSON.stringify(attrs.keys()),
+                data: JSON.stringify(Object.keys(attrs)),
                 seqval: seqval
             }
         }
diff --git a/samples b/samples
index 935f3919..e82a13ff 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit 935f3919737b861552f5f5e2c0d44bd56f673d54
+Subproject commit e82a13ff94f74829c0d2cde9f34e2a7d49b2fba8
diff --git a/tools b/tools
index 493a2d9f..178046f0 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit 493a2d9fe90ccc04baacaa78eb84e74191edf877
+Subproject commit 178046f0d9def124806975b0f23ba66401a49618

From ebe1d32569db81215c75a8fa47393cccfacd70c3 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 18 Jun 2018 12:19:37 -0400
Subject: [PATCH 31/62] JDISC: MDNSRegistry: Added debug messages

---
 lib/jdiscovery/mdnsregistry.js | 10 ++++++----
 samples                        |  2 +-
 tools                          |  2 +-
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index d0cf2a34..bd3158ec 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -116,7 +116,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
  *          on the receiving node
  */
 MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
-    console.log('mDNS-reg: called setAttributes');
+    console.log('DEBUG: MDNSRegistry.setAttributes');
     if (this.started) {
         this._createAdvertisements(attrs, seqval);
     } else {
@@ -243,7 +243,9 @@ MDNSRegistry.prototype.quit = function(seqval) {
  */
 MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
 
-    console.log('mDNS-reg: _createAdvs called');
+    // DEBUG
+    console.log('DEBUG: MDNSRegistry._createAdvertisements');
+
     // create attrChange advertisement
     let attrChangeAd = mdns.createAdvertisement(
         mdns.tcp(this.app + '-' + this.type + '-attrchg'),
@@ -296,7 +298,7 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
     var self = this;
     browser.on('serviceUp', function(service) {
         // DEBUG
-        console.log("Service discovered: ", JSON.stringify(service));
+        console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service up");
 
         // ignore our own services
         if (service.name == self.id) {
@@ -318,7 +320,7 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
     });
     browser.on('serviceDown', function(service) {
         // DEBUG
-        console.log("MDNSr: Service lost: ", JSON.stringify(service));
+        console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service down");
 
         // ignore our own services
         if (service.name == self.id) {
diff --git a/samples b/samples
index e82a13ff..935f3919 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit e82a13ff94f74829c0d2cde9f34e2a7d49b2fba8
+Subproject commit 935f3919737b861552f5f5e2c0d44bd56f673d54
diff --git a/tools b/tools
index 178046f0..493a2d9f 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit 178046f0d9def124806975b0f23ba66401a49618
+Subproject commit 493a2d9fe90ccc04baacaa78eb84e74191edf877

From 0b9d7bc3227fcdd0e4d729b3167b043f552306c8 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Mon, 18 Jun 2018 13:09:44 -0400
Subject: [PATCH 32/62] JDISC: Removed attrChgCaching in MDNSReg, Updated
 tester

---
 lib/jdiscovery/apps/tester.js  | 52 +++++++++++-----------
 lib/jdiscovery/mdnsregistry.js | 79 +++++++---------------------------
 2 files changed, 41 insertions(+), 90 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 987a4809..22accf99 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -1,28 +1,22 @@
 var Registrar = require('../jregistrar'),
 globals = require('../../jamserver/constants').globals,
-events = require('events'),
-Random = require('random-js');
+events = require('events');
 
-var random = new Random(Random.engines.mt19937().autoSeed());
-
-var machType = process.argv[2],
-phoneType = process.argv[3],
-phoneNumber = process.argv[4],
-port = process.argv[5],
-id = process.argv[6],
+var id = process.argv[2],
+type = process.argv[3],
+port = process.argv[4],
 app = 'tester';
 
 console.log('_______________________________________________');
-console.log(machType + ' id: ' + id);
+console.log(' id: ' + id + ' type: ' + type + ' port: ' + port);
 console.log('-----------------------------------------------');
 console.log();
 
-var reggie = new Registrar(app, machType, id, port,
+var reggie = new Registrar(app, type, id, port,
                            { protocols: { mqtt: true, mdns: true } });
 
 // Default discoveries
-
-if (machType === 'device') {
+if (type === 'device') {
 
     reggie.on('fog-up', function(fogId, connInfo) {
         console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
@@ -30,8 +24,7 @@ if (machType === 'device') {
     reggie.on('fog-down', function(fogId) {
         console.log('FOG DOWN: id: ' + fogId);
     });
-
-} else if (machType === 'fog') {
+} else if (type === 'fog') {
 
     reggie.on('cloud-up', function(cloudId, connInfo) {
         console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
@@ -40,21 +33,28 @@ if (machType === 'device') {
         console.log('CLOUD DOWN: id: ' + cloudId);
     });
 }
-
 // on rare occasions, you might get an error
 reggie.on('error', function(err) {
-switch(err.name) {
-    case 'permissions_err':
-	console.log(err.message);
-	console.log('Subscriptions: ' + err.value);
-	break;
-    default:
-	console.log('unknown error');
-	break;
-}
+    console.log("TESTER: Registrar threw an error...");    
 });
-/*
+
 // Custom attributes/discoveries
+if (type === 'device') {
+    let f = (x) => {
+        reggie.setAttributes({ secret : Math.random().toString(16) });
+        setTimeout(f, 5000);
+    }
+    f();
+} else if (type === 'fog') {
+    reggie.on('new-secret', function(id, secret) {
+        console.log('NEW-SECRET: id: ' + id + ' secret: ' + secret);
+    });
+    reggie.on('no-more-secret', function(id) {
+        console.log('NO-MORE-SECRET: id: ' + id);
+    });
+    reggie.discoverAttributes({ device: { secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
+}    
+/*
 
 if (machType === 'device') {
 // we'll have devices announce if they are phones (iphone or android)
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index bd3158ec..b13280c6 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -28,9 +28,6 @@ function MDNSRegistry(app, type, id, port) {
         cloud: {}
     }
 
-    this.attrChangeBrowsers = {};
-    this.attrChangeCache = {};
-
     this.attrRemovalBrowsers = {};
     this.attrRemovalCache = {};
 
@@ -56,31 +53,6 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
     var self = this;
     ['device', 'fog', 'cloud'].map(
         (x) => {
-            this.attrChangeBrowsers[x] = 
-                mdns.createBrowser(
-                    mdns.tcp(this.app + '-' + (x) + '-attrchg'),
-                    {resolverSequence: sequence});
-            this.attrChangeBrowsers[x].on('serviceUp', function(service) {
-                // ignore our own services
-                if (service.name == self.id) {
-                    return;
-                }
-                // update attrChangeCache
-                let attrs = JSON.parse(service.txtRecord.data);
-                let seqval = service.txtRecord.seqval;
-                for (var attr in attrs)
-                    if (attrs.hasOwnProperty(attr))
-                        self.attrChangeCache[attr] = seqval;
-            });
-            this.attrChangeBrowsers[x].on('serviceDown', function(service) {
-                // do nothing!
-            });
-            this.attrChangeBrowsers[x].on('error', function(err) {
-                browser.stop();
-                self.quit();
-            });
-            this.attrChangeBrowsers[x].start();
-
             this.attrRemovalBrowsers[x] =
                 mdns.createBrowser(
                     mdns.tcp(this.app + '-' + (x) + '-attrrem'),
@@ -130,6 +102,7 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
  * Removes attributes by stopping the advertisements
  */
 MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
+
     // create attrrem advertisement
     let attrRemovalAd = mdns.createAdvertisement(
         mdns.tcp(this.app + '-' + this.type + '-attrrem'),
@@ -230,6 +203,7 @@ MDNSRegistry.prototype.quit = function(seqval) {
                 for (var attr in this.browsers[x])
                     if(this.browsers[x].hasOwnProperty(attr))
                         this.browsers[x][attr].stop();
+                this.attrRemovalBrowsers[x].stop();
             }
     );
 }
@@ -246,18 +220,6 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
     // DEBUG
     console.log('DEBUG: MDNSRegistry._createAdvertisements');
 
-    // create attrChange advertisement
-    let attrChangeAd = mdns.createAdvertisement(
-        mdns.tcp(this.app + '-' + this.type + '-attrchg'),
-        this.port,
-        {
-            name: this.id, 
-            txtRecord: {
-                data: JSON.stringify(Object.keys(attrs)),
-                seqval: seqval
-            }
-        }
-    );
     // stop old advertisements and create new ones for updated attrs
     for (var attr in attrs) {
         if (attrs.hasOwnProperty(attr)) {
@@ -278,12 +240,9 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
                 this.port, 
                 {name: this.id, txtRecord: txtRecord});
 
-            console.log('Starting adv', JSON.stringify(txtRecord));
             this.ads[attr].start();
         }
     }
-    // stop attrChange advertisement
-    attrChangeAd.stop();
 }
 /**
  * Prep a browser to browse for an attibute
@@ -301,42 +260,34 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
         console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service up");
 
         // ignore our own services
-        if (service.name == self.id) {
+        if (service.name === self.id) {
             return;
         }
-        // check if we have heard of this discovery
-        if (self.attrChangeCache.hasOwnProperty(attr)) { 
-            // emit a discovery event!
-            self.emit('discovery', attr,
-                (attr == 'status')
-                    ? self.attrsToBrowse[type][attr].online
-                    : self.attrsToBrowse[type][attr].onAdd,
-                service.name,
-                JSON.parse(service.txtRecord.data),
-                service.txtRecord.seqval);
-            // clear attrChangeCache entry
-            delete self.attrChangeCache[attr];
-        }
+        // emit a discovery event!
+        self.emit('discovery', attr,
+            (attr === 'status')
+                ? self.attrsToBrowse[type][attr].online
+                : self.attrsToBrowse[type][attr].onAdd,
+            service.name,
+            JSON.parse(service.txtRecord.data),
+            service.txtRecord.seqval
+        );
     });
     browser.on('serviceDown', function(service) {
         // DEBUG
         console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service down");
 
         // ignore our own services
-        if (service.name == self.id) {
-            return;
-        }
-        // ignore attr-removals from attr content changes
-        if (self.attrChangeCache.hasOwnProperty(attr)) {
+        if (service.name === self.id) {
             return;
         }
-
-        if(attr == 'status') {
+        if(attr === 'status') {
             self.emit('discovery', attr,
                 self.attrsToBrowse[type][attr].offline,
                 service.name, 'offline', undefined);
         } else {
             // for m-o disconnections: do nothing
+            // for service downs associated with attr value changes: do nothing
             // for advertised attr removals: propagate
             if (self.attrRemovalCache.hasOwnProperty(attr)) {
                 let seqval = self.attrRemovalCache[attr];

From d97d057a9adf86dbaff54ef8e32b76e39456e6d4 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 19 Jun 2018 11:41:48 -0400
Subject: [PATCH 33/62] JDISC: MDNSReg: Moved to pure js mdns impl, still
 single process bound

---
 lib/jdiscovery/apps/tester.js                 |    4 +-
 lib/jdiscovery/mdns/CHANGES                   |  102 --
 lib/jdiscovery/mdns/LICENSE                   |   23 -
 lib/jdiscovery/mdns/Makefile                  |   54 -
 lib/jdiscovery/mdns/README.md                 |   90 --
 lib/jdiscovery/mdns/binding.gyp               |   79 -
 lib/jdiscovery/mdns/examples/express_app.js   |   14 -
 lib/jdiscovery/mdns/examples/osc_devices.js   |   31 -
 lib/jdiscovery/mdns/lib/advertisement.js      |  103 --
 lib/jdiscovery/mdns/lib/avahi.js              |   32 -
 lib/jdiscovery/mdns/lib/browser.js            |  128 --
 lib/jdiscovery/mdns/lib/dns_sd.js             |   37 -
 lib/jdiscovery/mdns/lib/io_watcher.js         |    3 -
 lib/jdiscovery/mdns/lib/mdns.js               |   32 -
 lib/jdiscovery/mdns/lib/mdns_service.js       |   44 -
 lib/jdiscovery/mdns/lib/network_interface.js  |   86 --
 .../mdns/lib/resolver_sequence_tasks.js       |  211 ---
 lib/jdiscovery/mdns/lib/service_type.js       |  199 ---
 lib/jdiscovery/mdns/node-waf.bat              |    3 -
 lib/jdiscovery/mdns/out/reports/tests.json    | 1275 -----------------
 lib/jdiscovery/mdns/package.json              |   85 --
 lib/jdiscovery/mdns/src/demangle.cpp          |   31 -
 lib/jdiscovery/mdns/src/dns_sd.cpp            |  319 -----
 .../mdns/src/dns_service_browse.cpp           |  101 --
 .../src/dns_service_enumerate_domains.cpp     |   73 -
 .../mdns/src/dns_service_get_addr_info.cpp    |  127 --
 .../mdns/src/dns_service_process_result.cpp   |   29 -
 lib/jdiscovery/mdns/src/dns_service_ref.cpp   |  122 --
 lib/jdiscovery/mdns/src/dns_service_ref.hpp   |   48 -
 .../mdns/src/dns_service_ref_deallocate.cpp   |   29 -
 .../mdns/src/dns_service_ref_sock_fd.cpp      |   32 -
 .../mdns/src/dns_service_register.cpp         |  165 ---
 .../mdns/src/dns_service_resolve.cpp          |  113 --
 .../mdns/src/dns_service_update_record.cpp    |   73 -
 lib/jdiscovery/mdns/src/mdns.hpp              |   34 -
 lib/jdiscovery/mdns/src/mdns_settings.hpp     |   28 -
 lib/jdiscovery/mdns/src/mdns_utils.cpp        |  116 --
 lib/jdiscovery/mdns/src/mdns_utils.hpp        |   58 -
 lib/jdiscovery/mdns/src/network_interface.cpp |  122 --
 lib/jdiscovery/mdns/src/skeleton.cpp_         |   11 -
 lib/jdiscovery/mdns/src/socket_watcher.cpp    |  149 --
 lib/jdiscovery/mdns/src/socket_watcher.hpp    |   29 -
 .../mdns/src/txt_record_buffer_to_object.cpp  |   52 -
 lib/jdiscovery/mdns/src/txt_record_create.cpp |   36 -
 .../mdns/src/txt_record_deallocate.cpp        |   23 -
 .../mdns/src/txt_record_get_length.cpp        |   23 -
 lib/jdiscovery/mdns/src/txt_record_ref.cpp    |   37 -
 lib/jdiscovery/mdns/src/txt_record_ref.hpp    |   32 -
 .../mdns/src/txt_record_set_value.cpp         |   51 -
 lib/jdiscovery/mdns/tests/test_browser.js     |   98 --
 lib/jdiscovery/mdns/tests/test_dns_sd.js      |  970 -------------
 lib/jdiscovery/mdns/tests/test_functional.js  |  501 -------
 .../mdns/tests/test_mdns_service.js           |   29 -
 .../mdns/tests/test_network_interface.js      |   55 -
 lib/jdiscovery/mdns/tests/test_odd_ends.js    |   27 -
 .../mdns/tests/test_service_type.js           |  101 --
 lib/jdiscovery/mdns/utils/coverage            |   29 -
 lib/jdiscovery/mdns/utils/docpack             |  129 --
 lib/jdiscovery/mdns/utils/jsf                 |   63 -
 lib/jdiscovery/mdns/utils/lib/actors.js       |   63 -
 lib/jdiscovery/mdns/utils/lib/bunch.js        |   12 -
 lib/jdiscovery/mdns/utils/lib/compiler.js     |   70 -
 lib/jdiscovery/mdns/utils/lib/lcov.js         |  127 --
 lib/jdiscovery/mdns/utils/lib/mdns_test.js    |   55 -
 lib/jdiscovery/mdns/utils/lib/ncov.js         |  206 ---
 lib/jdiscovery/mdns/utils/lib/obj.js          |   12 -
 lib/jdiscovery/mdns/utils/lib/view.js         |   53 -
 lib/jdiscovery/mdns/utils/render_report       |  126 --
 lib/jdiscovery/mdns/utils/testrun             |  301 ----
 lib/jdiscovery/mdns/wscript                   |  101 --
 lib/jdiscovery/mdnsregistry.js                |  111 +-
 71 files changed, 39 insertions(+), 7898 deletions(-)
 delete mode 100644 lib/jdiscovery/mdns/CHANGES
 delete mode 100644 lib/jdiscovery/mdns/LICENSE
 delete mode 100644 lib/jdiscovery/mdns/Makefile
 delete mode 100644 lib/jdiscovery/mdns/README.md
 delete mode 100644 lib/jdiscovery/mdns/binding.gyp
 delete mode 100755 lib/jdiscovery/mdns/examples/express_app.js
 delete mode 100755 lib/jdiscovery/mdns/examples/osc_devices.js
 delete mode 100644 lib/jdiscovery/mdns/lib/advertisement.js
 delete mode 100644 lib/jdiscovery/mdns/lib/avahi.js
 delete mode 100644 lib/jdiscovery/mdns/lib/browser.js
 delete mode 100644 lib/jdiscovery/mdns/lib/dns_sd.js
 delete mode 100644 lib/jdiscovery/mdns/lib/io_watcher.js
 delete mode 100644 lib/jdiscovery/mdns/lib/mdns.js
 delete mode 100644 lib/jdiscovery/mdns/lib/mdns_service.js
 delete mode 100644 lib/jdiscovery/mdns/lib/network_interface.js
 delete mode 100644 lib/jdiscovery/mdns/lib/resolver_sequence_tasks.js
 delete mode 100644 lib/jdiscovery/mdns/lib/service_type.js
 delete mode 100644 lib/jdiscovery/mdns/node-waf.bat
 delete mode 100644 lib/jdiscovery/mdns/out/reports/tests.json
 delete mode 100644 lib/jdiscovery/mdns/package.json
 delete mode 100644 lib/jdiscovery/mdns/src/demangle.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_sd.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_browse.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_enumerate_domains.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_get_addr_info.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_process_result.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_ref.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_ref.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_ref_deallocate.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_ref_sock_fd.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_register.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_resolve.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/dns_service_update_record.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/mdns.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/mdns_settings.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/mdns_utils.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/mdns_utils.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/network_interface.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/skeleton.cpp_
 delete mode 100644 lib/jdiscovery/mdns/src/socket_watcher.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/socket_watcher.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_buffer_to_object.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_create.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_deallocate.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_get_length.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_ref.cpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_ref.hpp
 delete mode 100644 lib/jdiscovery/mdns/src/txt_record_set_value.cpp
 delete mode 100644 lib/jdiscovery/mdns/tests/test_browser.js
 delete mode 100755 lib/jdiscovery/mdns/tests/test_dns_sd.js
 delete mode 100755 lib/jdiscovery/mdns/tests/test_functional.js
 delete mode 100644 lib/jdiscovery/mdns/tests/test_mdns_service.js
 delete mode 100644 lib/jdiscovery/mdns/tests/test_network_interface.js
 delete mode 100644 lib/jdiscovery/mdns/tests/test_odd_ends.js
 delete mode 100644 lib/jdiscovery/mdns/tests/test_service_type.js
 delete mode 100755 lib/jdiscovery/mdns/utils/coverage
 delete mode 100755 lib/jdiscovery/mdns/utils/docpack
 delete mode 100755 lib/jdiscovery/mdns/utils/jsf
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/actors.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/bunch.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/compiler.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/lcov.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/mdns_test.js
 delete mode 100755 lib/jdiscovery/mdns/utils/lib/ncov.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/obj.js
 delete mode 100644 lib/jdiscovery/mdns/utils/lib/view.js
 delete mode 100755 lib/jdiscovery/mdns/utils/render_report
 delete mode 100755 lib/jdiscovery/mdns/utils/testrun
 delete mode 100644 lib/jdiscovery/mdns/wscript

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 22accf99..12c942f2 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -13,7 +13,7 @@ console.log('-----------------------------------------------');
 console.log();
 
 var reggie = new Registrar(app, type, id, port,
-                           { protocols: { mqtt: true, mdns: true } });
+                           { protocols: { mqtt: false, mdns: true } });
 
 // Default discoveries
 if (type === 'device') {
@@ -42,7 +42,7 @@ reggie.on('error', function(err) {
 if (type === 'device') {
     let f = (x) => {
         reggie.setAttributes({ secret : Math.random().toString(16) });
-        setTimeout(f, 5000);
+        setTimeout(f, 10000);
     }
     f();
 } else if (type === 'fog') {
diff --git a/lib/jdiscovery/mdns/CHANGES b/lib/jdiscovery/mdns/CHANGES
deleted file mode 100644
index 6e564cdc..00000000
--- a/lib/jdiscovery/mdns/CHANGES
+++ /dev/null
@@ -1,102 +0,0 @@
-CHANGES
-
-Version 2.2.3 to 2.2.4
-
-   - changed Readme from textile to markdown, since npmjs.org doesn't render textfile well (ronkorving)
-
-Version 2.2.2 to 2.2.3
-
-   - errors that could be thrown asynchronously are now emitted as "error" (achingbrain)
-
-Version 2.2.1 to 2.2.2
-
-   - node-mdns now uses NAN for compatibility across a wide range of Node.js versions (incl 0.11) (donpark)
-
-Version 2.2.0 to 2.2.1
-
-   - changed uncatchable error into "error" event on the browser (marcooliveira)
-
-Version 2.1.4 to 2.2.0
-
-   - moved the repository back from Wizcorp/node_mdns to agnat/node_mdns
-
-Version 2.1.3 to 2.1.4
-
-   - added service to errors from the resolver (bjornstar)
-
-Version 2.1.2 to 2.1.3
-
-   - applied the fix from 2.1.0 to remaining parts of the code base (achingbrain)
-
-Version 2.1.1 to 2.1.2
-
-   - fixed an exception that was thrown when a service went down along with its network interface (achingbrain)
-
-Version 2.1.0 to 2.1.1
-
-   - fixed the interfaceIndex patch by mrose17 as it was breaking on Node 0.8 (ronkorving)
-
-Version 2.0.0-dev to 2.1.0
-
-   - errno was broken in Node v0.10 (mrose17)
-   - allow interfaceIndex to be "really big" (mrose17)
-
-Version 1.1.0 to 2.0.0-dev
-
-   - empty version release (agnat)
-
-Version 1.0.0 to 1.1.0
-
-   - better handling of network interfaces
-   - error handling: exceptions/errors contain the numeric error code now
-
-Version 0.0.7 to 1.0.0
-
-   - replaced IOWatcher with a libuv based SocketWatcher (Tony Ealden)
-   - fixed an API typo: filterAdresses -> filterAddresses (major version bumped)
-   - first release that actually works on windows
-
-Version 0.0.6 to 0.0.7
-
-   - Bugfix: convert NULL strings to undefined values in callbacks
-
-Version 0.0.5 to 0.0.6
-
-   - use node-gyp in gyp based builds
-   - prepared a windows port
-
-Version 0.0.4 to 0.0.5
-
-   - Publish and receive TXT records
-
-   - Build universal binaries on Mac OS
-     This helps to work around a architecture mismatch between gyp-build nodes
-     and waf-build addons.
-
-   - Added experimental gyp-based build
-
-   - Continuous integration and coverage testing.
-
-   - Fixed FreeBSD build
-
-Version 0.0.3 to 0.0.4
-
-   - New service type objects:
-     The strings have been replaced with a small ServiceType object. Also, older
-     versions used TCP as the default protocol. The protocol is now mandatory.
-     However, the new syntax is much more flexible. See user guide at
-     http://agnat.github.com/node_mdns/user_guide.html#service_types.
-
-   - The 'info' object is now called 'service'
-
-   - The info.regtype property is now called service.type
-
-   - The info.serviceName property is now called service.name
-
-   - Callback signature changed:
-     flags is now a property of the service object (service.flags)
-
-   - possibly more
-
-
-vim: spell spelllang=en_us :
diff --git a/lib/jdiscovery/mdns/LICENSE b/lib/jdiscovery/mdns/LICENSE
deleted file mode 100644
index 50dea472..00000000
--- a/lib/jdiscovery/mdns/LICENSE
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 2010 David Siegel
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use,
-copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the
-Software is furnished to do so, subject to the following
-conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
diff --git a/lib/jdiscovery/mdns/Makefile b/lib/jdiscovery/mdns/Makefile
deleted file mode 100644
index a204aaed..00000000
--- a/lib/jdiscovery/mdns/Makefile
+++ /dev/null
@@ -1,54 +0,0 @@
-BUILDTYPE ?= Release
-
-GCOV_OUT = build/reports/coverage/cpp
-NCOV_OUT = build/reports/coverage
-
-TEST_OPTIONS=
-
-ifdef PULSE_BUILD_NUMBER
-  TEST_OPTIONS= --ascii --verbose
-endif
-
-all: bindings
-
-bindings:
-	$(MAKE) -C build BUILDTYPE=$(BUILDTYPE)
-
-test: bindings
-	node --expose_gc utils/testrun $(TEST_OPTIONS)
-
-coverage:
-	$(MAKE) coverage_run BUILDTYPE=Coverage
-
-coverage_build:
-	$(MAKE) -C build BUILDTYPE=Coverage
-
-jscoverage:
-	jscoverage -v --no-highlight lib/ build/Coverage/lib
-
-coverage_run: coverage_build jscoverage
-	lcov -d build/$(BUILDTYPE)/obj.target/dns_sd_bindings/src --zerocounters
-	mkdir -p $(GCOV_OUT)/html; 
-	NCOV_OUT=$(NCOV_OUT) node --expose_gc utils/testrun $(TEST_OPTIONS)
-	lcov --base-directory build \
-		 --directory      build/$(BUILDTYPE)/obj.target/dns_sd_bindings/src \
-		 --output-file    $(GCOV_OUT)/testrun_all.info \
-		 --capture
-	utils/ncov
-	lcov --output-file    $(GCOV_OUT)/testrun.info \
-		 --extract \
-		 $(GCOV_OUT)/testrun_all.info "$(abspath .)/*" \
-	   | tee $(GCOV_OUT)/lcov.log 
-	genhtml --output-directory $(GCOV_OUT)/html \
-	        --demangle-cpp \
-			$(GCOV_OUT)/testrun.info
-	tail -n 3 $(GCOV_OUT)/lcov.log | utils/coverage > $(GCOV_OUT)/coverage.properties
-
-doc:
-	utils/docpack
-
-website:
-	echo TODO
-
-.PHONY: test citest coverage coverage_build coverage_run bindings jscoverage doc website 
-
diff --git a/lib/jdiscovery/mdns/README.md b/lib/jdiscovery/mdns/README.md
deleted file mode 100644
index 8b1833fa..00000000
--- a/lib/jdiscovery/mdns/README.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# mdns -- node.js Service Discovery
-
-* Package: mdns
-* Description: multicast DNS service discovery
-* Installation: `npm install mdns` (see below)
-* Documentation: [mdns user guide](http://agnat.github.com/node_mdns/user_guide.html)
-* License: [MIT](http://github.com/agnat/node_mdns/blob/master/LICENSE)
-* Donations: [![Flattr this git repository](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=agnat&url=https://github.com/agnat/node_mdns&title=node_mdns&tags=github&category=software)
-
-mdns adds multicast DNS service discovery, also known as zeroconf or bonjour to Node.js. It provides an object based interface to announce and browse services on the local network.
-
-## Synopsis
-
-```js
-// import the module
-var mdns = require('mdns');
-
-// advertise a http server on port 4321
-var ad = mdns.createAdvertisement(mdns.tcp('http'), 4321);
-ad.start();
-
-// watch all http servers
-var browser = mdns.createBrowser(mdns.tcp('http'));
-browser.on('serviceUp', function(service) {
-  console.log("service up: ", service);
-});
-browser.on('serviceDown', function(service) {
-  console.log("service down: ", service);
-});
-browser.start();
-
-// discover all available service types
-var all_the_types = mdns.browseThemAll(); // all_the_types is just another browser...
-```
-
-## Installation
-
-On Linux and other systems using the avahi daemon the avahi dns_sd compat library and its header files are required.  On debianesque systems the package name is `libavahi-compat-libdnssd-dev`.  On other platforms Apple's [mDNSResponder](http://opensource.apple.com/tarballs/mDNSResponder/) is recommended. Care should be taken not to install more than one mDNS stack on a system.
-
-On Windows you are going to need Apples "Bonjour SDK for Windows". You can download it either from Apple (registration required) or various unofficial sources. Take your pick. After installing the SDK restart your computer and make sure the `BONJOUR_SDK_HOME` environment variable is set. You'll also need a compiler. Microsoft Visual Studio Express will do. On Windows node >=0.7.9 is required.
-
-mdns is available as a npm package:
-
-```sh
-npm install mdns
-```
-
-If you want to play with the latest source code, here is a more manual approach:
-
-```sh
-git clone http://github.com/agnat/node_mdns
-cd node_mdns
-npm link && npm test
-```
-
-In case you want to run or even publish your package using the development version of mdns you may set the version field to a tarball URL:
-
-```json
-{ "name": "discomvobulator"
-, "version": "0.0.1"
-, "description": "covers all your discomvobulation needs"
-, "dependencies":
-  { "mdns": "https://github.com/agnat/node_mdns/tarball/master"
-  }
-}
-```
-
-## Documentation
-
-See the [user guide](http://agnat.github.com/node_mdns/user_guide.html).
-
-## Contributors
-
-In random order:
-
-* Orlando Vazquez ([orlandov](https://github.com/orlandov))
-* Ryan Dahl ([ry](https://github.com/ry))
-* Dominic Tarr ([dominictarr](https://github.com/dominictarr))
-* Emil Stenqvist ([emilisto](https://github.com/emilisto))
-* Toby Ealden ([TobyEalden](https://github.com/TobyEalden))
-* Ron Korving ([ronkorving](https://github.com/ronkorving))
-* Don Park ([donpark](https://github.com/donpark))
-* Cong Liu ([ghostoy](https://github.com/ghostoy))
-* Tian Zhang ([KhaosT](https://github.com/KhaosT))
-
-Your name is missing on the list? Shame on me. Please open an issue.
-
-## Bugs and Contributions
-
-If you find a bug, please report it using the [issue tracker](http://github.com/agnat/node_mdns/issues).
diff --git a/lib/jdiscovery/mdns/binding.gyp b/lib/jdiscovery/mdns/binding.gyp
deleted file mode 100644
index 92dcea16..00000000
--- a/lib/jdiscovery/mdns/binding.gyp
+++ /dev/null
@@ -1,79 +0,0 @@
-{ 'targets': [
-    { 'target_name': 'dns_sd_bindings'
-    , 'sources': [ 'src/dns_sd.cpp'
-                 , 'src/dns_service_browse.cpp'
-                 , 'src/dns_service_enumerate_domains.cpp'
-                 , 'src/dns_service_get_addr_info.cpp'
-                 , 'src/dns_service_process_result.cpp'
-                 , 'src/dns_service_ref.cpp'
-                 , 'src/dns_service_ref_deallocate.cpp'
-                 , 'src/dns_service_ref_sock_fd.cpp'
-                 , 'src/dns_service_register.cpp'
-                 , 'src/dns_service_resolve.cpp'
-                 , 'src/dns_service_update_record.cpp'
-                 , 'src/mdns_utils.cpp'
-                 , 'src/network_interface.cpp'
-                 , 'src/socket_watcher.cpp'
-                 , 'src/txt_record_ref.cpp'
-                 , 'src/txt_record_create.cpp'
-                 , 'src/txt_record_deallocate.cpp'
-                 , 'src/txt_record_set_value.cpp'
-                 , 'src/txt_record_get_length.cpp'
-                 , 'src/txt_record_buffer_to_object.cpp'
-                 ]
-    , 'conditions': [
-        [ 'OS!="mac" and OS!="win"', {
-            'libraries': [ '-ldns_sd' ]
-        }]
-      , [ 'OS=="mac"', {
-            'defines': [ 'HAVE_DNSSERVICEGETADDRINFO' ]
-        }]
-      , ['OS=="freebsd"', {
-            'include_dirs': [ '/usr/local/include' ]
-          , 'libraries': [ '-L/usr/local/lib' ]
-        }]
-      , ['OS=="win"', {
-            'variables': {
-                'BONJOUR_SDK_DIR': '$(BONJOUR_SDK_HOME)', # Preventing path resolution problems by saving the env var in variable first 
-                'PLATFORM': '$(Platform)' # Set  the platform
-              }
-          , 'include_dirs': [ '<(BONJOUR_SDK_DIR)/Include' ]
-          , 'defines': [ 'HAVE_DNSSERVICEGETADDRINFO' ]
-          , 'libraries': [ '-l<(BONJOUR_SDK_DIR)/Lib/<(PLATFORM)/dnssd.lib'
-                         , '-lws2_32.lib'
-                         , '-liphlpapi.lib'
-                         ]
-        }]
-      ]
-    , "include_dirs": [ "<!(node -e \"require('nan')\")" ]
-    # The following breaks the debug build, so just ignore the warning for now.
-    #, 'msbuild_settings': {
-    #    'ClCompile': { 'ExceptionHandling': 'Sync' }
-    #  , 'Link'     : { 'IgnoreSpecificDefaultLibraries': [ 'LIBCMT' ] }
-    #  }
-    , 'configurations': {
-        'Release': {
-            'xcode_settings': { 'GCC_OPTIMIZATION_LEVEL': 3 }
-          , 'cflags': [ '-O3' ]
-          , 'ldflags': [ '-O3' ]
-        }
-      , 'Debug': {
-            'xcode_settings': { 'GCC_OPTIMIZATION_LEVEL': 0 }
-          , 'cflags': [ '-g', '-O0', ]
-          , 'ldflags': [ '-g', '-O0' ]
-        }
-      , 'Coverage': {
-            'xcode_settings': {
-                'GCC_OPTIMIZATION_LEVEL': 0
-              , 'OTHER_LDFLAGS': ['--coverage']
-              , 'OTHER_CFLAGS':  ['--coverage']
-            }
-          , 'cflags': [ '-O0', '--coverage' ]
-          , 'ldflags': [ '--coverage' ]
-        }
-      }
-    }
-  ]
-}
-
-# vim: filetype=python shiftwidth=2 softtabstop=2 :
diff --git a/lib/jdiscovery/mdns/examples/express_app.js b/lib/jdiscovery/mdns/examples/express_app.js
deleted file mode 100755
index b993fe06..00000000
--- a/lib/jdiscovery/mdns/examples/express_app.js
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env node
-var mdns    = require('../lib/mdns')
-    ,express = require('express');
-
-var app = express();
-app.get('/', function(req, res){
-  res.send('Hello World');
-});
-
-var listener = app.listen(4321, function() {
-    var port = listener.address().port;
-    mdns.createAdvertisement(mdns.tcp('http') , port).start();
-    console.log('Listening on port', port);
-});
diff --git a/lib/jdiscovery/mdns/examples/osc_devices.js b/lib/jdiscovery/mdns/examples/osc_devices.js
deleted file mode 100755
index 664de910..00000000
--- a/lib/jdiscovery/mdns/examples/osc_devices.js
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env node
-var mdns    = require('../lib/mdns')
-  , listOfOscDevices = { /*name: {adresses: ['192.168.0.24', 'fe80::0:18'], port: 10001}}*/ }
-  ;
-
-var mdnsBrowser = mdns.createBrowser(mdns.udp('osc'));
-
-mdnsBrowser.on('serviceUp', function(service) {
-  // ignore duplicate ups
-  if(listOfOscDevices[service.name]) return;
-
-  listOfOscDevices[service.name] = {'addresses': service.addresses, 'port': service.port};
-  var cnt = Object.keys(listOfOscDevices).length;
-
-  console.log('osc device "'+service.name+' up at '+service.addresses[0]+':'+service.port+', now '+cnt+' devices on the net');
-});
-
-mdnsBrowser.on('serviceDown', function(service) {
-// ignore duplicate downs
-  if(!listOfOscDevices[service.name]) return;
-
-  var device = listOfOscDevices[service.name];
-
-  delete listOfOscDevices[service.name];
-  var cnt = Object.keys(listOfOscDevices).length;
-
-  console.log('osc device "'+service.name+' up at '+device.addresses[0]+':'+device.port+', now '+cnt+' devices on the net');
-});
-
-console.log('listening for osc-compatible devices on the net')
-mdnsBrowser.start();
diff --git a/lib/jdiscovery/mdns/lib/advertisement.js b/lib/jdiscovery/mdns/lib/advertisement.js
deleted file mode 100644
index be85b089..00000000
--- a/lib/jdiscovery/mdns/lib/advertisement.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var dns_sd = require('./dns_sd')
-  , nif = require('./network_interface')
-  , util = require('util')
-  ;
-
-var MDNSService = require('./mdns_service').MDNSService
-  , makeServiceType = require('./service_type').makeServiceType
-  ;
-
-function Advertisement(serviceType, port, options, callback) {
-  MDNSService.call(this);
-  var self = this;
-
-  if ( ! callback) {
-    if (typeof options === 'function') {
-      callback = options;
-      options = {};
-    }
-  }
-  options = options || {};
-
-  serviceType = makeServiceType(serviceType);
-
-  var flags     = options.flags          || 0
-    , ifaceIdx  = nif.interfaceIndex(options)
-    , name      = options.name           || null
-    , domain    = options.domain         || null
-    , host      = options.host           || null
-    , txtRecord = options.txtRecord      || null
-    , context   = options.context        || null
-    ;
-
-  if (txtRecord && typeof txtRecord === 'object' &&
-      ! (txtRecord instanceof dns_sd.TXTRecordRef || Buffer.isBuffer(txtRecord)))
-  {
-    txtRecord = objectToTXTRecord(txtRecord);
-  }
-
-  function on_service_registered(serviceRef, flags, errorCode, name,
-      serviceType, domain, context)
-  {
-    var error = dns_sd.buildException(errorCode);
-    if (callback) {
-      callback.call(self, error, {
-          name:    name
-        , type:    makeServiceType(serviceType)
-        , domain:  domain
-        , flags:   flags
-      }, context);
-    }
-    if (error) {
-      self.emit('error', error);
-    }
-  }
-
-  dns_sd.DNSServiceRegister(self.serviceRef, flags, ifaceIdx, name,
-      '' + serviceType, domain, host, port, txtRecord, on_service_registered,
-      context);
-}
-util.inherits(Advertisement, MDNSService)
-exports.Advertisement = Advertisement;
-
-Advertisement.create = function create(serviceType, port, options, callback) {
-  return new Advertisement(serviceType, port, options, callback);
-}
-
-Advertisement.prototype.updateTXTRecord = function updateTXTRecord(txtRecord) {
-  if (txtRecord && typeof txtRecord === 'object' &&
-      ! (txtRecord instanceof dns_sd.TXTRecordRef || Buffer.isBuffer(txtRecord)))
-  {
-    txtRecord = objectToTXTRecord(txtRecord);
-  }
-
-  dns_sd.DNSServiceUpdateRecord(this.serviceRef, null, 0, txtRecord, 0);
-}
-
-function objectToTXTRecord(o) {
-  var record = new dns_sd.TXTRecordRef()
-    , value
-    ;
-  record.buffer = new Buffer(256);
-  dns_sd.TXTRecordCreate(record, record.buffer);
-  for (var p in o) {
-    ensure_legal_key(p);
-    if (o[p] === undefined || o[p] === null || Buffer.isBuffer(o[p])) {
-      value = o[p];
-    } else {
-      value = '' + o[p];
-    }
-    dns_sd.TXTRecordSetValue(record, p, value);
-  }
-  return record;
-}
-
-function ensure_legal_key(string) {
-  var i, c;
-  for (i = 0; i < string.length; ++i) {
-    c = string.charCodeAt(i);
-    if (c < 0x20 || c > 0x7e || c === 0x3d) {
-      throw new Error("key must be all printable ascii characters exluding '='");
-    }
-  }
-}
diff --git a/lib/jdiscovery/mdns/lib/avahi.js b/lib/jdiscovery/mdns/lib/avahi.js
deleted file mode 100644
index fb1b6955..00000000
--- a/lib/jdiscovery/mdns/lib/avahi.js
+++ /dev/null
@@ -1,32 +0,0 @@
-var dns_sd = require('./dns_sd');
-
-function supportsInterfaceIndexLocalOnly() {
-  try {
-    var sr = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = dns_sd.kDNSServiceInterfaceIndexLocalOnly
-      , name   = null
-      , type   = '_http._tcp'
-      , domain = null
-      , host   = null
-      , port   = 4321
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister( sr, flags, iface, name, type, domain,
-          host, port, txtRec, cb, ctx);
-  } catch (ex) {
-    if (ex.errorCode === dns_sd.kDNSServiceErr_Unsupported) {
-      if (sr && sr.initialized) {
-        dns_sd.DNSServiceRefDeallocate(sr);
-      }
-      return false;
-    }
-    console.warn('Unexpected result while probing for avahi:', ex);
-  }
-  dns_sd.DNSServiceRefDeallocate(sr);
-  return true;
-}
-
-module.exports = ! supportsInterfaceIndexLocalOnly();
diff --git a/lib/jdiscovery/mdns/lib/browser.js b/lib/jdiscovery/mdns/lib/browser.js
deleted file mode 100644
index 02b60440..00000000
--- a/lib/jdiscovery/mdns/lib/browser.js
+++ /dev/null
@@ -1,128 +0,0 @@
-var dns_sd = require('./dns_sd')
-  , nif = require('./network_interface')
-  , util = require('util')
-  , rst = require('./resolver_sequence_tasks')
-  , st = require('./service_type')
-  , MDNSService = require('./mdns_service').MDNSService
-  ;
-
-var Browser = exports.Browser = function Browser(serviceType, options) {
-  MDNSService.call(this);
-  var self = this;
-
-  options = options || {};
-  var flags = options.flags             || 0
-    , ifaceIdx  = nif.interfaceIndex(options)
-    , domain = options.domain           || null
-    , context = options.context         || null
-    , requested_type = st.makeServiceType( serviceType );
-    ;
-
-  var interfaceNames = [];
-
-  function on_service_changed(sdRef, flags, ifaceIdx, errorCode, serviceName,
-      serviceType, replyDomain, context)
-  {
-    function on_resolver_done(error, service) {
-      if (error) {
-        self.emit('error', error, service);
-      } else {
-        self.emit('serviceChanged', service, context);
-        self.emit('serviceUp', service, context);
-      }
-    }
-    if (errorCode == dns_sd.kDNSServiceErr_NoError) {
-      if (requested_type.isWildcard()) {
-        serviceType = serviceName + '.' + serviceType.split('.').shift();
-        serviceName = null;
-      }
-
-      var type;
-
-      try {
-        type = st.makeServiceType(serviceType);
-      } catch(e) {
-        self.emit('error', e);
-        return;
-      }
-
-      var service = {
-          interfaceIndex: ifaceIdx
-        , type: type
-        , replyDomain: replyDomain
-        , flags: flags
-      };
-      if (serviceName) service.name    = serviceName;
-
-      if (dns_sd.kDNSServiceInterfaceIndexLocalOnly === ifaceIdx) {
-        service.networkInterface = nif.loopbackName();
-      } else if (typeof dns_sd.if_indextoname !== 'undefined' && ifaceIdx > 0) {
-        try {
-          service.networkInterface = dns_sd.if_indextoname(ifaceIdx);
-
-          interfaceNames[ifaceIdx] = service.networkInterface;
-        } catch(e) {
-          if(typeof interfaceNames[ifaceIdx] !== "undefined") {
-            service.networkInterface = interfaceNames[ifaceIdx];
-          } else {
-            self.emit('error', e);
-          }
-        }
-      }
-
-      if (flags & dns_sd.kDNSServiceFlagsAdd) {
-        resolve(service,
-            options.resolverSequence || Browser.defaultResolverSequence,
-            on_resolver_done);
-      } else {
-        self.emit('serviceChanged', service, context);
-        self.emit('serviceDown', service, context);
-      }
-    } else {
-      self.emit('error', dns_sd.buildException(errorCode));
-    }
-  }
-
-  dns_sd.DNSServiceBrowse(self.serviceRef, flags, ifaceIdx, '' + requested_type,
-      domain, on_service_changed, context);
-}
-util.inherits(Browser, MDNSService);
-
-var resolve = exports.resolve = function resolve(service, sequence, callback) {
-  var step = 0;
-  if ( ! callback) {
-    callback = sequence;
-    sequence = Browser.defaultResolverSequence;
-  }
-
-  function next(error) {
-    if (error) {
-      callback(error, service);
-      return;
-    }
-    if (sequence.length === step) {
-      callback(undefined, service);
-      return;
-    }
-    sequence[step++](service, next);
-  }
-
-  next();
-}
-
-Browser.create = function create(serviceType, options) {
-  return new Browser(serviceType, options);
-}
-
-Browser.defaultResolverSequence = [
-  rst.DNSServiceResolve()
-, 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo()
-, rst.makeAddressesUnique()
-];
-
-exports.browseThemAll = function browseThemAll(options) {
-  options = options || {}
-  options.resolverSequence = options.resolverSequence || [];
-  return Browser.create(st.ServiceType.wildcard, options);
-}
-
diff --git a/lib/jdiscovery/mdns/lib/dns_sd.js b/lib/jdiscovery/mdns/lib/dns_sd.js
deleted file mode 100644
index 5668b04f..00000000
--- a/lib/jdiscovery/mdns/lib/dns_sd.js
+++ /dev/null
@@ -1,37 +0,0 @@
-var path = require('path');
-var major, minor, patch;
-
-if (process.version.match(/^v(\d+)\.(\d+)\.(\d+).*$/)) {
-  major = parseInt(RegExp.$1);
-  minor = parseInt(RegExp.$2);
-  patch = parseInt(RegExp.$3);
-}
-
-var default_dir = major === 0 && minor <= 4 ? 'default' : 'Release'
-  , buildtype = process.env.BUILDTYPE || default_dir
-  ;
-
-//console.log(major, minor, patch, default_dir, buildtype);
-
-function product(type) { 
-  if (type === 'Coverage') {
-    return path.join('..', 'dns_sd_bindings') 
-  }
-  return path.join('..', 'build', type, 'dns_sd_bindings') 
-}
-
-try {
-  module.exports = require(product(buildtype));
-} catch (ex) {
-  if (! ex.code) {
-    if (/not find/.test(ex)) {
-      ex.code = 'MODULE_NOT_FOUND';
-    }
-  }
-  if (ex.code === 'MODULE_NOT_FOUND') {
-    module.exports = require(product(default_dir));
-    console.warn('dns_sd: failed to load requested ', buildtype, 'build. using', default_dir, 'instead.');
-  } else {
-    throw ex;
-  }
-}
diff --git a/lib/jdiscovery/mdns/lib/io_watcher.js b/lib/jdiscovery/mdns/lib/io_watcher.js
deleted file mode 100644
index 5153003c..00000000
--- a/lib/jdiscovery/mdns/lib/io_watcher.js
+++ /dev/null
@@ -1,3 +0,0 @@
-var dns_sd = require('./dns_sd');
-exports.IOWatcher = typeof dns_sd.SocketWatcher !== 'undefined' ?
-    dns_sd.SocketWatcher : process.binding('io_watcher').IOWatcher;
diff --git a/lib/jdiscovery/mdns/lib/mdns.js b/lib/jdiscovery/mdns/lib/mdns.js
deleted file mode 100644
index a695774d..00000000
--- a/lib/jdiscovery/mdns/lib/mdns.js
+++ /dev/null
@@ -1,32 +0,0 @@
-var dns_sd  = require('./dns_sd')
-  , ad      = require('./advertisement')
-  , browser = require('./browser')
-  , st      = require('./service_type')
-  , nif     = require('./network_interface')
-  ;
-
-exports.dns_sd = dns_sd;
-
-exports.Advertisement       = ad.Advertisement;
-exports.createAdvertisement = ad.Advertisement.create;
-
-exports.Browser       = browser.Browser;
-exports.createBrowser = browser.Browser.create;
-exports.browseThemAll = browser.browseThemAll;
-exports.resolve       = browser.resolve;
-
-exports.MDNSService = require('./mdns_service').MDNSService;
-
-exports.ServiceType = st.ServiceType;
-exports.makeServiceType = st.makeServiceType;
-exports.tcp = st.protocolHelper('tcp');
-exports.udp = st.protocolHelper('udp');
-
-exports.loopbackInterface = nif.loopbackInterface;
-
-exports.dns_sd.exportConstants(exports);
-
-exports.rst = require('./resolver_sequence_tasks');
-
-exports.isAvahi = require('./avahi.js');
-
diff --git a/lib/jdiscovery/mdns/lib/mdns_service.js b/lib/jdiscovery/mdns/lib/mdns_service.js
deleted file mode 100644
index c6f6dccc..00000000
--- a/lib/jdiscovery/mdns/lib/mdns_service.js
+++ /dev/null
@@ -1,44 +0,0 @@
-var dns_sd    = require('./dns_sd')
-  , util      = require('util')
-  , events    = require('events')
-  , IOWatcher = require('./io_watcher').IOWatcher;
-  ;
-
-function MDNSService() {
-  events.EventEmitter.call(this);
-  var self = this;
-
-  self._watcherStarted = false;
-  self.serviceRef = new dns_sd.DNSServiceRef();
-  self.watcher = new IOWatcher();
-  self.watcher.host = self; // TODO: Find out what this is for ...
-  self.watcher.callback = function() {
-    if (self._watcherStarted) {
-      try {
-        dns_sd.DNSServiceProcessResult.call(self, self.serviceRef);
-      } catch (error) {
-        self.emit("error", error);
-      }
-    }
-  };
-}
-util.inherits(MDNSService, events.EventEmitter);
-exports.MDNSService = MDNSService;
-
-MDNSService.prototype.start = function start() {
-  if (this._watcherStarted) {
-    throw new Error("mdns service already started");
-  }
-  this.watcher.set(this.serviceRef.fd, true, false);
-  this.watcher.start();
-  this._watcherStarted = true;
-}
-
-MDNSService.prototype.stop = function stop() {
-  if (this._watcherStarted) {
-    this.watcher.stop();
-    dns_sd.DNSServiceRefDeallocate(this.serviceRef);
-    this.serviceRef = null;
-    this._watcherStarted = false;
-  }
-}
diff --git a/lib/jdiscovery/mdns/lib/network_interface.js b/lib/jdiscovery/mdns/lib/network_interface.js
deleted file mode 100644
index 7c6800ff..00000000
--- a/lib/jdiscovery/mdns/lib/network_interface.js
+++ /dev/null
@@ -1,86 +0,0 @@
-var net = require('net')
-  , os = require('os')
-  , dns_sd = require('./dns_sd')
-  , interfaceIndexDeprecatedWarningPrinted = false
-  ;
-
-var have_if_nametoindex = typeof dns_sd.if_nametoindex !== 'undefined';
-
-exports.interfaceIndex = function interfaceIndex(options) {
-  var networkInterface
-    , index = 0;
-  if (typeof options.interfaceIndex !== 'undefined') {
-    if( ! interfaceIndexDeprecatedWarningPrinted) {
-      console.warn("WARNING: 'interfaceIndex' is deprecated and will be " +
-          "removed. Please use 'networkInterface' instead and see the " +
-          "documentation on why it's cool.");
-      interfaceIndexDeprecatedWarningPrinted = true;
-    }
-    networkInterface = options.interfaceIndex;
-  } else {
-    networkInterface = options.networkInterface;
-  }
-  if (typeof networkInterface !== 'undefined') {
-    if (net.isIP(networkInterface)) {
-      if ( ! have_if_nametoindex) {
-        throw new Error('IP address to interface index conversion is not ' +
-            'supported on this platform');
-      }
-      index = dns_sd.if_nametoindex(addressToName(networkInterface));
-      //console.log('got IP', networkInterface, '->', index);
-    } else if (isString(networkInterface)) {
-      if ( ! have_if_nametoindex) {
-        throw new Error('interface name to index conversion is not supported ' +
-            'on this platform');
-      }
-      index = dns_sd.if_nametoindex(networkInterface);
-      //console.log('got if name', networkInterface, '->', index);
-    } else {
-      //console.log('got index', networkInterface);
-      index = networkInterface;
-    }
-  }
-  //console.log('interface index:', index);
-  return index;
-}
-
-exports.loopbackInterface = function loopbackInterface() {
-  if (typeof dns_sd.kDNSServiceInterfaceIndexLocalOnly !== 'undefined') {
-    return dns_sd.kDNSServiceInterfaceIndexLocalOnly;
-  } else {
-    return loopbackName();
-  }
-}
-
-var loopbackName = exports.loopbackName = function loopbackName() {
-  var interfaces = os.networkInterfaces();
-  for (var name in interfaces) {
-    for (var i = 0; i < interfaces[name].length; ++i) {
-      if (interfaces[name][i].address === '127.0.0.1') {
-        return name;
-      }
-    }
-  }
-  throw new Error('failed to find loopback interface');
-}
-
-function addressToName(address) {
-  if (typeof os.networkInterfaces === 'undefined') {
-    throw new Error('IP address to interface index conversion is not ' +
-        'supported with this version of node');
-  }
-  var addresses = os.networkInterfaces();
-  for (var name in addresses) {
-    for (var i = 0; i < addresses[name].length; ++i) {
-      if (addresses[name][i].address === address) {
-        return name;
-      }
-    }
-  }
-  throw new Error('interface with address ' + address + ' does not exist');
-}
-
-function isString(s) {
-  return toString.call(s) == '[object String]';
-}
-
diff --git a/lib/jdiscovery/mdns/lib/resolver_sequence_tasks.js b/lib/jdiscovery/mdns/lib/resolver_sequence_tasks.js
deleted file mode 100644
index f017038d..00000000
--- a/lib/jdiscovery/mdns/lib/resolver_sequence_tasks.js
+++ /dev/null
@@ -1,211 +0,0 @@
-var dns_sd = require('./dns_sd')
-  , util = require('util')
-  , MDNSService = require('./mdns_service').MDNSService
-  ;
-
-exports.DNSServiceResolve = function DNSServiceResolve(options) {
-  options = options || {};
-  options.flags = options.flags || 0;
-  options.unwrapTxtRecord =
-    'unwrapTxtRecord' in options ? options.unwrapTxtRecord : true;
-  return function DNSServiceResolve(service, next) {
-    try {
-      var resolver = new MDNSService();
-
-      function on_resolver_done(sdRef, flags, iface, errorCode, fullname,
-          hosttarget, port, txtRecord, context)
-      {
-        try {
-          var error = dns_sd.buildException(errorCode);
-          if ( ! error && service.interfaceIndex === iface) {
-            if (fullname)       service.fullname = fullname;
-            if (hosttarget)     service.host = hosttarget;
-            if (port)           service.port = port;
-            // all services have a TXT record. ignore the empty ones.
-            if (txtRecord.length > 1) {
-              service.rawTxtRecord = txtRecord;
-              if (options.unwrapTxtRecord) {
-                service.txtRecord = dns_sd.txtRecordBufferToObject(txtRecord)
-              }
-            }
-          }
-          resolver.stop();
-          next(error);
-        } catch (ex) {
-          resolver.stop();
-          next(ex);
-        }
-      }
-
-      dns_sd.DNSServiceResolve(resolver.serviceRef, options.flags,
-          service.interfaceIndex, service.name, '' + service.type,
-          service.replyDomain, on_resolver_done, null);
-      resolver.start();
-    } catch (ex) { 
-      resolver.stop();
-      next(ex);
-    }
-  };
-}
-
-exports.DNSServiceGetAddrInfo = function DNSServiceGetAddrInfo(options) {
-  options = options || {};
-  var family_flags = 0;
-  if ('families' in options) {
-    if (options.families.indexOf(4) !== -1) {
-      family_flags |= dns_sd.kDNSServiceProtocol_IPv4;
-    }
-    if (options.families.indexOf(6) !== -1) {
-      family_flags |= dns_sd.kDNSServiceProtocol_IPv6;
-    }
-  }
-  return function DNSServiceGetAddrInfo(service, next) {
-    try {
-      var adr_getter = new MDNSService()
-        , addresses = []
-        ;
-
-      function on_get_addr_info_done(sdRef, flags, iface, errorCode, hostname,
-          address, context)
-      {
-        try {
-          var error = dns_sd.buildException(errorCode);
-          if (error) {
-            adr_getter.stop()
-            next(error);
-          } else {
-            if (iface === service.interfaceIndex) {
-              addresses.push(address);
-            }
-            if ( ! (dns_sd.kDNSServiceFlagsMoreComing & flags)) {
-              service.addresses = addresses;
-              adr_getter.stop()
-              next();
-            }
-          }
-        } catch (ex) {
-          adr_getter.stop();
-          next(ex);
-        }
-      }
-
-      dns_sd.DNSServiceGetAddrInfo(
-          adr_getter.serviceRef, 0, service.interfaceIndex, family_flags,
-          service.host, on_get_addr_info_done, null);
-      adr_getter.start();
-    } catch (ex) {
-      adr_getter.stop();
-      next(ex);
-    }
-  }
-}
-
-var _getaddrinfo;
-try {
-  var cares = process.binding('cares_wrap');
-  function getaddrinfo_complete(err, addresses, cb) {
-    if (addresses) {
-      cb(undefined, addresses);
-    } else if (err === 'ENOENT') {
-      cb(undefined, []);
-    } else {
-      cb(errnoException(err, 'getaddrinfo'));
-    }
-  }
-  function getaddrinfo_0_11(host, family, cb) {
-    var req = new cares.GetAddrInfoReqWrap()
-      , err = cares.getaddrinfo(req, host, family, 0, false)
-      ;
-    req.oncomplete = function oncomplete(err, addresses) {
-        getaddrinfo_complete(err, addresses, cb);
-    }
-    if (err) throw errnoException(err, 'getaddrinfo', host);
-  }
-  function getaddrinfo_0_10(host, family, cb) {
-    var wrap = cares.getaddrinfo(host, family);
-    if ( ! wrap) {
-      throw errnoException(process._errno || global.errno, 'getaddrinfo');
-    }
-    wrap.oncomplete = function (addresses) {
-      getaddrinfo_complete((process._errno || global.errno), addresses, cb);
-    }
-  }
-  // node 0.11+ cares.getaddrinfo function uses request object.
-  // use appropriate version based on node version number
-  if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) > 0.1) {
-    _getaddrinfo = getaddrinfo_0_11;
-  } else {
-    _getaddrinfo = getaddrinfo_0_10;
-  }
-} catch (ex) {
-  _getaddrinfo = process.binding('net').getaddrinfo;
-}
-
-exports.getaddrinfo = function getaddrinfo(options) {
-  options = options || {};
-  var families = options.families || [4, 6];
-  return function getaddrinfo(service, next) {
-    var last_error
-      , counter = 0
-      ;
-    // XXX in older versions of node a zero family value is not supported
-    families.forEach(function(family) {
-      _getaddrinfo(service.host, family, function(error, addresses) {
-        if (error) {
-          last_error = error
-        } else {
-          service.addresses = (service.addresses || []).concat(addresses);
-        }
-        if (++counter === families.length) {
-          next(last_error);
-        }
-      });
-    });
-  }
-}
-
-function unique(array) {
-  var o = {} , p , r = [] ;
-  array.forEach(function(e) { o[e] = undefined });
-  for (p in o) { r.push(p) }
-  return r;
-}
-
-exports.makeAddressesUnique = function makeAddressesUnique() {
-  return function makeAddressesUnique(service, next) {
-    service.addresses = unique(service.addresses);
-    next();
-  }
-}
-
-exports.filterAddresses = function filterAddresses(filter_function) {
-  return function filterAddresses(service, next) {
-    service.addresses = (service.addresses || []).filter(filter_function);
-    next();
-  }
-}
-
-exports.logService = function logService() {
-  return function logService(service, next) {
-    console.log(service);
-    next();
-  }
-}
-
-function errnoException(errorno, syscall) {
-  // TODO make this more compatible with ErrnoException from src/node.cc
-  // Once all of Node is using this function the ErrnoException from
-  // src/node.cc should be removed.
-  var e = new Error(syscall + ' ' + errorno);
-
-  // For backwards compatibility. libuv returns ENOENT on NXDOMAIN.
-  if (errorno == 'ENOENT') {
-    errorno = 'ENOTFOUND'
-  }
-
-  e.errno = e.code = errorno;
-  e.syscall = syscall;
-  return e;
-}
-
-
diff --git a/lib/jdiscovery/mdns/lib/service_type.js b/lib/jdiscovery/mdns/lib/service_type.js
deleted file mode 100644
index c75ea32e..00000000
--- a/lib/jdiscovery/mdns/lib/service_type.js
+++ /dev/null
@@ -1,199 +0,0 @@
-var ServiceType = exports.ServiceType = function ServiceType(/* ... */) {
-  this.name = '';
-  this.protocol = '';
-  this.subtypes = [];
-
-  var args;
-  if (arguments.length === 1) {
-    args = Array.isArray(arguments[0]) ? arguments[0] : [arguments[0]];
-  } else if (arguments.length > 1) {
-    args = Array.prototype.slice.call(arguments);
-  }
-  if (args) {
-    if (args.length === 1) {
-      if (typeof args[0] === 'string') {
-        this.fromString(args[0]);
-      } else if (Array.isArray(args[0])) {
-        this.fromArray(args[0]);
-      } else if (typeof args[0] === 'object') {
-        this.fromJSON(args[0]);
-      } else {
-        throw new Error('argument must be a string, array or object');
-      }
-    } else if (args.length >= 2) {
-      this.fromArray(args);
-    } else { // zero arguments
-      // uninitialized ServiceType ... fine with me
-    }
-  }
-}
-
-ServiceType.wildcard = '_services._dns-sd._udp.';
-ServiceType.prototype.isWildcard = function isWildcard() {
-  return this.toString() === ServiceType.wildcard;
-}
-
-ServiceType.prototype.toString = function() {
-  var type_string = _u(this.name) + "." + _u(this.protocol);
-  if (this.fullyQualified) {
-    type_string += '.'
-  }
-  if (this.subtypes.length > 0) {
-    var subtypes = this.subtypes.map(function(t) { return _u(t) });
-    subtypes.unshift(type_string);
-    type_string = subtypes.join(',');
-  }
-  return type_string;
-}
-
-ServiceType.prototype.fromString = function fromString(string) {
-  var is_wildcard = ServiceType.prototype.isWildcard.call(string)
-    , subtypes = string.split(',')
-    , primary_string = subtypes.shift()
-    , service_tokens = primary_string.split('.')
-    , service_type = service_tokens.shift()
-    , protocol
-    ;
-  if (is_wildcard) {
-    service_type += '.' + service_tokens.shift();
-  }
-  protocol = service_tokens.shift()
-
-  checkProtocolU(protocol);
-  if ( ! is_wildcard) {
-    checkFormat(service_type);
-  }
-  subtypes.forEach(function(t) { checkFormat(t) });
-  if (service_tokens.length === 1 && service_tokens[0] === '') {
-    // trailing dot
-    this.fullyQualified = true
-  } else if (service_tokens.length > 0) {
-    throw new Error("trailing tokens '" + service_tokens.join('.') + "' in " +
-        "service type string '" + string + "'");
-  }
-
-  this.name = service_type.substr(1);
-  this.protocol = protocol.substr(1);
-  this.subtypes = subtypes.map(function(t) { return t.substr(1) });
-}
-
-ServiceType.prototype.toArray = function toArray() {
-  return [this.name, this.protocol].concat(this.subtypes);
-}
-
-ServiceType.prototype.fromArray = function fromArray(array) {
-  var service_type = _uu(array.shift())
-    , protocol = _uu(array.shift())
-    , subtypes = array.map(function(t) { return _uu(t) })
-    ;
-  checkLengthAndCharset(service_type);
-  checkProtocol(protocol);
-  subtypes.forEach(function(t) { checkLengthAndCharset(t) });
-
-  this.name = service_type;
-  this.protocol = protocol;
-  this.subtypes = subtypes
-}
-
-ServiceType.prototype.fromJSON = function fromJSON(obj) {
-  if ( ! ('name' in obj)) {
-    throw new Error('required property name is missing');
-  }
-  if ( ! ('protocol' in obj)) {
-    throw new Error('required property protocol is missing');
-  }
-
-  var service_type   = _uu(obj.name)
-    , protocol       = _uu(obj.protocol)
-    , subtypes       = 'subtypes' in obj ?
-                        obj.subtypes.map(function(t) { return _uu(t) }) : []
-    ;
-
-  checkLengthAndCharset(service_type);
-  checkProtocol(protocol);
-  subtypes.forEach(function(t) { checkLengthAndCharset(t) });
-
-  this.name = service_type;
-  this.protocol = protocol;
-  this.subtypes = subtypes;
-  if ('fullyQualified' in obj) {
-    this.fullyQualified = obj.fullyQualified;
-  }
-}
-
-ServiceType.prototype.matches = function matches(other) {
-  return this.name === other.name && this.protocol === other.protocol;
-  // XXX handle subtypes
-}
-
-exports.makeServiceType = function makeServiceType() {
-  if (arguments.length === 1 && arguments[0] instanceof ServiceType) {
-    return arguments[0];
-  }
-  return new ServiceType(Array.prototype.slice.call(arguments));
-}
-
-exports.protocolHelper = function protocolHelper(protocol) {
-  return function() {
-    var args = Array.prototype.slice.call(arguments);
-    if (isProtocol(args[1])) {
-      throw new Error("duplicate protocol '" + args[1] + "' in arguments");
-    }
-    args.splice(1,0, protocol);
-    return exports.makeServiceType.apply(this, args);
-  }
-}
-
-function isProtocol(str) {
-  return str === 'tcp' || str === '_tcp' || str === 'udp' || str === '_udp';
-}
-
-function _u(str) { return "_" + str; }
-function _uu(str) { return str[0] === '_' ? str.substr(1) : str; }
-
-var charset_regex = /[^-a-zA-Z0-9]/;
-function checkLengthAndCharset(str) {
-  if (str.length === 0) {
-    throw new Error('type ' + str + ' must not be empty');
-  }
-/*
-  if (str.length > 15) {
-    throw new Error('type ' + str + ' has more than 15 characters');
-  }
-  if (str.match(charset_regex)) {
-    throw new Error('type ' + str + ' may only contain alphanumeric ' +
-        'characters and hyphens');
-  }
-*/
-}
-
-var format_regex = /_[-a-zA-Z0-9]+/;
-function checkFormat(str) {
-  if (str.length === 0) {
-    throw new Error('type string must not be empty');
-  }
-/*
-  if (str.length > 16) { // 16 is correct because we have a leading underscore
-    throw new Error('type ' + _uu(str) + ' has more than 15 characters');
-  }
-  if ( ! str.match(format_regex)) {
-    throw new Error('type ' + str + ' must start with an underscore ' +
-        'followed by alphanumeric characters and hyphens only');
-  }
-*/
-}
-
-function checkProtocolU(str) {
-  if ( ! (str === '_tcp' || str === '_udp')) {
-    throw new Error("protocol must be either '_tcp' or '_udp' but is '" +
-        str + "'");
-  }
-}
-
-function checkProtocol(str) {
-  if ( ! (str === 'tcp' || str === 'udp')) {
-    throw new Error("protocol must be either '_tcp' or '_udp' but is '" +
-        str + "'");
-  }
-}
-
diff --git a/lib/jdiscovery/mdns/node-waf.bat b/lib/jdiscovery/mdns/node-waf.bat
deleted file mode 100644
index caf61c47..00000000
--- a/lib/jdiscovery/mdns/node-waf.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@echo off
-REM A creative way to make 'npm install' succeed on windows
-REM without breaking the package
diff --git a/lib/jdiscovery/mdns/out/reports/tests.json b/lib/jdiscovery/mdns/out/reports/tests.json
deleted file mode 100644
index cbc33a08..00000000
--- a/lib/jdiscovery/mdns/out/reports/tests.json
+++ /dev/null
@@ -1,1275 +0,0 @@
-{
-  "test_browser": {
-    "Browser": {
-      "Should retrieve interface name from cache when interface index is no longer valid": [
-        {
-          "test": "equal"
-        },
-        {
-          "test": "ok"
-        },
-        {
-          "test": "ok"
-        },
-        {
-          "test": "equal"
-        }
-      ],
-      "Should survive invalid MDNS advert": [
-        {
-          "test": "equal"
-        }
-      ]
-    }
-  },
-  "test_dns_sd": {
-    "DNSServiceRef": [
-      {
-        "test": "ok",
-        "message": "DNSServiceRef must be truthy"
-      },
-      {
-        "test": "strictEqual",
-        "message": "File descriptor must be -1"
-      },
-      {
-        "test": "strictEqual",
-        "message": "DNSServiceRef must not be initialized"
-      }
-    ],
-    "DNSServiceRegister()": [
-      {
-        "test": "doesNotThrow",
-        "message": "Call with minimal argumentss must succeed."
-      },
-      {
-        "test": "notStrictEqual",
-        "message": "File descriptor must not be -1 after initialization"
-      },
-      {
-        "test": "strictEqual",
-        "message": "DNSServiceRef must be initialized"
-      },
-      {
-        "test": "throws",
-        "message": "Duplicate initialization of DNSServiceRef must throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "Call with all arguments on must succed."
-      },
-      {
-        "test": "throws",
-        "message": "Call with zero arguments must throw."
-      },
-      {
-        "test": "throws",
-        "message": "Call with eight arguments must throw."
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be a number, not a string."
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be a number, not a string."
-      },
-      {
-        "test": "throws",
-        "message": "'interfaceIndex' must be a number, not a string."
-      },
-      {
-        "test": "throws",
-        "message": "'name' must be a string, not a number."
-      },
-      {
-        "test": "throws",
-        "message": "'regtype' must be a string, not null."
-      },
-      {
-        "test": "throws",
-        "message": "'regtype' has to be a string, not a number."
-      },
-      {
-        "test": "throws",
-        "message": "'domain' must not be a string, not a number."
-      },
-      {
-        "test": "throws",
-        "message": "'host' must be a string, not a number."
-      },
-      {
-        "test": "throws",
-        "message": "'port' must be a number, not a string."
-      },
-      {
-        "test": "throws",
-        "message": "'txtRecord' must be a TXTRecordRef or buffer, not a number."
-      },
-      {
-        "test": "throws",
-        "message": "'callback' must be a function, not a string."
-      },
-      {
-        "test": "throws",
-        "message": "port number must be <= 65535."
-      },
-      {
-        "test": "throws",
-        "message": "port number must >= 0."
-      }
-    ],
-    "DNSServiceProcessResult()": [
-      {
-        "test": "throws"
-      },
-      {
-        "test": "throws"
-      },
-      {
-        "test": "throws"
-      },
-      {
-        "test": "throws"
-      },
-      {
-        "test": "strictEqual",
-        "message": "serviceRef must be identical"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be of type number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'errorCode' must be of type number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'errorCode' must be kDNSServiceErr_NoError"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'name' must be of type string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'serviceType' must be of type string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'domain' must be of type string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'context' must be of type string (in this test)"
-      },
-      {
-        "test": "strictEqual",
-        "message": "expected 'foobar' but got 'foobar'"
-      }
-    ],
-    "DNSServiceRefSockFD()": [
-      {
-        "test": "throws",
-        "message": "call with uninitialized serviceRef must throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceRefSockFD() must not throw"
-      },
-      {
-        "test": "notEqual",
-        "message": "file descriptor must not be -1"
-      },
-      {
-        "test": "strictEqual",
-        "message": "result of DNSServiceRefSockFD() and fd getter must be the same"
-      },
-      {
-        "test": "throws",
-        "message": "argument must be a DNSServiceRef"
-      },
-      {
-        "test": "throws",
-        "message": "must throw when called with not enough arguments"
-      }
-    ],
-    "DNSServiceBrowse()": [
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceBrowse() must not throw"
-      },
-      {
-        "test": "throws",
-        "message": "serviceRef already initialized"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments"
-      },
-      {
-        "test": "throws",
-        "message": "'serviceRef' must not be a string"
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be a number, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'interfaceIndex' must be a number, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'regtype' must be a string, not null"
-      },
-      {
-        "test": "throws",
-        "message": "'regtype' must be a string, not a number"
-      },
-      {
-        "test": "throws",
-        "message": "'domain' must be a string, not a number"
-      },
-      {
-        "test": "throws",
-        "message": "'callback' must be a function, not a number"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceBrowse() must not throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceBrowse() must not throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceBrowse() must not throw"
-      }
-    ],
-    "DNSServiceRefDeallocate()": [
-      {
-        "test": "strictEqual",
-        "message": "'initialized' must be true after inititalization"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'initialized' must be false after deallocation"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'fd' must be -1 after deallocation"
-      },
-      {
-        "test": "throws",
-        "message": "serviceRef is already deallocated"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments"
-      },
-      {
-        "test": "throws",
-        "message": "argument must be DNSServiceRef, not undefined"
-      },
-      {
-        "test": "throws",
-        "message": "to many arguments"
-      },
-      {
-        "test": "throws",
-        "message": "call with non serviceRef object must throw"
-      }
-    ],
-    "DNSServiceResolve()": [
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceResolve() must not throw"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'initialized' must be true after inititalization"
-      },
-      {
-        "test": "throws",
-        "message": "duplicate initialization must throw"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments"
-      },
-      {
-        "test": "throws",
-        "message": "serviceRef must be DNSServiceRef object"
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be a number, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'interfaceIndex' must be a number, not null"
-      },
-      {
-        "test": "throws",
-        "message": "'name' must be a string, not null"
-      },
-      {
-        "test": "throws",
-        "message": "'regtype' must be a string, not null"
-      },
-      {
-        "test": "throws",
-        "message": "'domain' must be a string, not null"
-      },
-      {
-        "test": "throws",
-        "message": "'callback' must be a function, not null"
-      }
-    ],
-    "DNSServiceEnumerateDomains()": [
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceEnumerateDomains() must not throw"
-      },
-      {
-        "test": "notEqual",
-        "message": "'fd' must not be -1 after inititalization"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'initialized' must be true after inititalization"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceEnumerateDomains() must not throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceEnumerateDomains() must not throw"
-      },
-      {
-        "test": "throws",
-        "message": "dupliate inititalization of serviceRef must throw"
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be a number, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'flags' must be kDNSServiceFlagsBrowseDomains or kDNSServiceFlagsRegistrationDomains"
-      },
-      {
-        "test": "throws",
-        "message": "'interfaceIndex' must be number, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'callback' must be function, not a string"
-      },
-      {
-        "test": "throws",
-        "message": "'callback' must be function, not null"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments"
-      },
-      {
-        "test": "throws",
-        "message": "serviceRef must be a DNSServiceRef object"
-      }
-    ],
-    "DNSServiceGetAddrInfo": [
-      {
-        "test": "doesNotThrow",
-        "message": "DNSServiceGetAddrInfo() must not throw"
-      }
-    ],
-    "TXTRecordRef": [
-      {
-        "test": "strictEqual",
-        "message": "length must be 8 bytes after adding 'foo=bar'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "length must be 22 bytes after adding 'foobar=foobar'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "length must be 33 bytes after adding 'buffer=raw'"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordCreate() must throw when called without arguments"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordCreate() must throw when called with a string"
-      },
-      {
-        "test": "throws",
-        "message": "duplicate call to TXTRecordCreate() must throw"
-      },
-      {
-        "test": "throws",
-        "message": "txtRecord must be a TXTRecordRef object"
-      },
-      {
-        "test": "throws",
-        "message": "txtRecord must be a TXTRecordRef object"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "TXTRecordCreate() with undefined buffer must succeed"
-      },
-      {
-        "test": "throws",
-        "message": "illegal buffer argument must throw"
-      },
-      {
-        "test": "throws",
-        "message": "illegal buffer argument must throw"
-      },
-      {
-        "test": "throws",
-        "message": "illegal argument must throw"
-      },
-      {
-        "test": "throws",
-        "message": "illegal argument must throw"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called without arguments"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with non TXTRecordRef object"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with non TXTRecordRef object"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with non TXTRecordRef object"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "TXTRecordSetValue() must not throw when called with null value"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "TXTRecordSetValue() must not throw when called with undefined value"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with non string key"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with strange value"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordSetValue() must throw when called with strange value"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments must throw"
-      },
-      {
-        "test": "throws",
-        "message": "illegal arguments must throw"
-      },
-      {
-        "test": "throws",
-        "message": "illegal arguments must throw"
-      },
-      {
-        "test": "doesNotThrow",
-        "message": "deallocating a txtRecord must not throw"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordDeallocate() must throw when called without arguments"
-      },
-      {
-        "test": "throws",
-        "message": "TXTRecordDeallocate() must throw when called with more than one argument"
-      },
-      {
-        "test": "throws",
-        "message": "txtRecordBufferToObject() must throw when called with no arguments"
-      },
-      {
-        "test": "throws",
-        "message": "txtRecordBufferToObject() must throw when called with a non-object"
-      },
-      {
-        "test": "throws",
-        "message": "txtRecordBufferToObject() must throw when called with strange objects"
-      }
-    ],
-    "buildException()": [
-      {
-        "test": "strictEqual",
-        "message": "buildException(kDNSServiceErr_NoError) must return undefined"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Unknwon) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NoSuchName) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NoMemory) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException() must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_BadReference) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_BadState) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_BadFlags) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Unsupported) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NotInitialized) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_AlreadyRegistered) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NameConflict) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Invalid) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Firewall) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Incompatible) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_BadInterfaceIndex) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_Refused) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NoSuchRecord) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NoAuth) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NoSuchKey) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_NATTraversal) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_DoubleNAT) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok",
-        "message": "buildException(kDNSServiceErr_BadTime) must return an Error object"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "throws",
-        "message": "not enough arguments"
-      },
-      {
-        "test": "throws",
-        "message": "argument must be a string"
-      }
-    ],
-    "exportConstants()": [
-      {
-        "test": "ok"
-      },
-      {
-        "test": "throws"
-      },
-      {
-        "test": "throws"
-      }
-    ]
-  },
-  "test_functional": {
-    "simple browsing": [
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "ok",
-        "message": "'flags' must have kDNSServiceFlagsAdd set"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'fullname' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'host' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'port' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'port' must match the advertisement"
-      },
-      {
-        "test": "ok",
-        "message": "'service' must have a address property"
-      },
-      {
-        "test": "ok",
-        "message": "'addresses' must be an array"
-      },
-      {
-        "test": "ok",
-        "message": "addresses must not be empty"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'interfaceIndex' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'name' must be a string"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be a service type object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "type must match the target type"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must match 'local.'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "must have a networkInterface"
-      },
-      {
-        "test": "ok",
-        "message": "must have context"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property must match input"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'interfaceIndex' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'name' must be a string"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be ServiceType object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'type' must match target type"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must match 'local.'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'fullname' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'host' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'port' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'port' must match"
-      },
-      {
-        "test": "ok",
-        "message": "'service' must have a addresses property"
-      },
-      {
-        "test": "ok",
-        "message": "'addresses' must be a string"
-      },
-      {
-        "test": "ok",
-        "message": "'addresses' must not be empty"
-      },
-      {
-        "test": "ok",
-        "message": "'service' must have a rawTxtRecord property"
-      },
-      {
-        "test": "ok",
-        "message": "'rawTxtRecord' must be truthy"
-      },
-      {
-        "test": "ok",
-        "message": "'txtRecord' must be truthy"
-      },
-      {
-        "test": "strictEqual",
-        "message": "must have a networkInterface"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property type in txtRecord must match"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property chunky in txtRecord must match"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property strips in txtRecord must match"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property buffer in txtRecord must match"
-      },
-      {
-        "test": "ok",
-        "message": "must have context"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property must match input"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'interfaceIndex' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'name' must be a string"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be a service type object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "type must match the target type"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must match 'local.'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "must have a networkInterface"
-      },
-      {
-        "test": "ok",
-        "message": "must have context"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property must match input"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'interfaceIndex' must be a number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'name' must be a string"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be a ServiceType object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'type' must match target aervice type"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must be a string"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'replyDomain' must match 'local.'"
-      },
-      {
-        "test": "strictEqual",
-        "message": "must have a networkInterface"
-      },
-      {
-        "test": "ok",
-        "message": "must have context"
-      },
-      {
-        "test": "strictEqual",
-        "message": "property must match input"
-      }
-    ],
-    "create ads": [
-      {
-        "test": "ok",
-        "message": "service must have a flags property"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be a ServiceType object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'type' must be as advertised"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a flags property"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be a number"
-      },
-      {
-        "test": "ok",
-        "message": "'type' must be a ServiceType object"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'type' must be as advertised"
-      }
-    ],
-    "update ad record": [
-      {
-        "test": "ok",
-        "message": "'service' must have a rawTxtRecord property"
-      },
-      {
-        "test": "ok",
-        "message": "'rawTxtRecord' must be truthy"
-      },
-      {
-        "test": "ok",
-        "message": "'txtRecord' must be truthy"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'txtRecord' doesn't match"
-      },
-      {
-        "test": "ok",
-        "message": "'service' must have a rawTxtRecord property"
-      },
-      {
-        "test": "ok",
-        "message": "'rawTxtRecord' must be truthy"
-      },
-      {
-        "test": "ok",
-        "message": "'txtRecord' must be truthy"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'txtRecord' doesn't match"
-      }
-    ],
-    "browseThemAll()": [],
-    "resolver sequence": [
-      {
-        "test": "ok",
-        "message": "service must not have a 'host' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must not have a 'port' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must not have a 'fullname' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must not have an 'addresses' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'host' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'port' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'fullname' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must not have an 'addresses' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'host' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'port' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'fullname' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have an 'addresses' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'host' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'port' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have a 'fullname' property"
-      },
-      {
-        "test": "ok",
-        "message": "service must have an 'addresses' property"
-      }
-    ],
-    "local advertisement invisible on external interfaces": [
-      {
-        "test": "strictEqual",
-        "message": "service must have the loopback interface index"
-      },
-      {
-        "test": "strictEqual",
-        "message": "service must have the loopback interface name"
-      },
-      {
-        "test": "strictEqual",
-        "message": "service must have the loopback interface index"
-      },
-      {
-        "test": "strictEqual",
-        "message": "service must have the loopback interface name"
-      },
-      {
-        "test": "strictEqual",
-        "message": "browser on external interface must not receive events"
-      }
-    ]
-  },
-  "test_mdns_service": {
-    "MDNSService": {
-      "DNS errors should propagate": [
-        {
-          "test": "equal"
-        }
-      ]
-    }
-  },
-  "test_network_interface": {
-    "if_nametoindex <-> if_indextoname": [
-      {
-        "test": "ok"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok"
-      },
-      {
-        "test": "strictEqual"
-      }
-    ],
-    "interfaceIndex": [
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "strictEqual"
-      },
-      {
-        "test": "ok"
-      },
-      {
-        "test": "ok"
-      },
-      {
-        "test": "strictEqual"
-      }
-    ]
-  },
-  "test_odd_ends": {
-    "DNSServiceEnumerateDomains": [
-      {
-        "test": "strictEqual",
-        "message": "error must be NoError"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'flags' must be of type number"
-      },
-      {
-        "test": "strictEqual",
-        "message": "'iface' must be of type number"
-      }
-    ]
-  },
-  "test_service_type": {
-    "protocol helpers": [
-      {
-        "test": "strictEqual",
-        "message": "comparing simple udp service type"
-      },
-      {
-        "test": "strictEqual",
-        "message": "comparing tcp service type with subtype"
-      },
-      {
-        "test": "strictEqual",
-        "message": "comparing tcp service types with two subtypes"
-      }
-    ],
-    "makeServiceType()": [
-      {
-        "test": "strictEqual",
-        "message": "construct from string form"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from tokens"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from tokens with underscores"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from array"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct with protocol helper"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from object (json)"
-      }
-    ],
-    "json round-trip": [
-      {
-        "test": "strictEqual",
-        "message": "construct from result of JSON.parse(JSON.stringify(...))"
-      }
-    ],
-    "ServiceType() constructor": [
-      {
-        "test": "strictEqual",
-        "message": "construct from tokens"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from array"
-      },
-      {
-        "test": "strictEqual",
-        "message": "construct from object"
-      }
-    ],
-    "illegal arguments": [
-      {
-        "test": "throws",
-        "message": "service type to long"
-      },
-      {
-        "test": "throws",
-        "message": "illegal characters in primary type"
-      },
-      {
-        "test": "throws",
-        "message": "subtype to long"
-      },
-      {
-        "test": "throws",
-        "message": "illegal characters in subtype"
-      },
-      {
-        "test": "throws",
-        "message": "illegal chars in subtype"
-      },
-      {
-        "test": "throws",
-        "message": "type token already present"
-      },
-      {
-        "test": "throws",
-        "message": "missing properties"
-      },
-      {
-        "test": "throws",
-        "message": "illegal protocol"
-      },
-      {
-        "test": "throws",
-        "message": "attempt to construct from number"
-      },
-      {
-        "test": "throws",
-        "message": "aatempt to construct with an empty string"
-      }
-    ]
-  }
-}
\ No newline at end of file
diff --git a/lib/jdiscovery/mdns/package.json b/lib/jdiscovery/mdns/package.json
deleted file mode 100644
index b1ac8a64..00000000
--- a/lib/jdiscovery/mdns/package.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "name": "mdns",
-  "version": "2.4.0",
-  "description": "multicast DNS service discovery",
-  "main": "./lib/mdns.js",
-  "scripts": {
-    "test": "node utils/testrun"
-  },
-  "keywords": [
-    "zeroconf",
-    "bonjour",
-    "dns_sd",
-    "mDNSResponder"
-  ],
-  "devDependencies": {
-    "ejs": "*",
-    "less": "*",
-    "mkdirp": "*",
-    "nopt": "*",
-    "slide": "*",
-    "glob": "*",
-    "ncp": "*",
-    "minimatch": "*",
-    "proxyquire": "^1.7.3"
-  },
-  "repository": {
-    "type": "git",
-    "url": "http://github.com/agnat/node_mdns.git"
-  },
-  "homepage": "http://agnat.github.com/node_mdns",
-  "bugs": {
-    "url": "http://github.com/agnat/node_mdns/issues"
-  },
-  "licenses": [
-    {
-      "type": "MIT",
-      "url": "http://github.com/agnat/node_mdns/raw/master/LICENSE"
-    }
-  ],
-  "author": {
-    "name": "David Siegel",
-    "email": "agnat@me.com",
-    "github": "agnat"
-  },
-  "contributors": [
-    {
-      "name": "Orlando Vazquez",
-      "email": "ovazquez@gmail.com",
-      "url": "http://or.lan.do/",
-      "github": "orlandov"
-    },
-    {
-      "name": "Ryan Dahl",
-      "email": "ry at tiny clouds dot org",
-      "url": "http://four.livejournal.com/",
-      "github": "ry"
-    },
-    {
-      "name": "Dominic Tarr",
-      "url": "http://twitter.com/dominictarr",
-      "github": "dominictarr"
-    },
-    {
-      "name": "Emil Stenqvist",
-      "github": "emilisto"
-    },
-    {
-      "name": "Toby Ealden",
-      "github": "TobyEalden"
-    },
-    {
-      "name": "Cong Liu",
-      "github": "ghostoy"
-    },
-    {
-      "name": "Tian Zhang",
-      "github": "KhaosT"
-    }
-  ],
-  "dependencies": {
-    "bindings": "~1.2.1",
-    "nan": "^2.10.0"
-  },
-  "gypfile": true
-}
diff --git a/lib/jdiscovery/mdns/src/demangle.cpp b/lib/jdiscovery/mdns/src/demangle.cpp
deleted file mode 100644
index 8704832e..00000000
--- a/lib/jdiscovery/mdns/src/demangle.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include <v8.h>
-
-#ifdef __GNUC__
-#   include <cstdlib>
-#   include <cxxabi.h>
-#endif
-using namespace v8;
-
-namespace {
-
-Handle<Value>
-demangle(Arguments const& info) {
-    Nan::HandleScope scope;  
-    String::Utf8Value str(info[0]->ToString());
-#ifdef __GNUC__
-    int status;
-    char * ret = abi::__cxa_demangle(*str, NULL, NULL, & status);
-    Local<String> demangled = Nan::New(ret);
-    ::free(ret);
-#endif
-    info.GetReturnValue().Set(demangled);
-}
-
-} // end of anaonymous namespace
-
-extern "C"
-void
-init(Handle<Object> target) {
-    Nan::Set(target, Nan::New("demangle").ToLocalChecked(),
-            Nan::GetFunction(Nan::New<FunctionTemplate>(demangle)));
-}
diff --git a/lib/jdiscovery/mdns/src/dns_sd.cpp b/lib/jdiscovery/mdns/src/dns_sd.cpp
deleted file mode 100644
index eaa8fdbe..00000000
--- a/lib/jdiscovery/mdns/src/dns_sd.cpp
+++ /dev/null
@@ -1,319 +0,0 @@
-#include "mdns.hpp"
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-#include "txt_record_ref.hpp"
-#ifdef NODE_MDNS_USE_SOCKET_WATCHER
-# include "socket_watcher.hpp"
-#endif
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-// === dns_sd ===========================================
-NAN_METHOD(DNSServiceRegister);
-NAN_METHOD(DNSServiceUpdateRecord);
-NAN_METHOD(DNSServiceRefSockFD);
-NAN_METHOD(DNSServiceProcessResult);
-NAN_METHOD(DNSServiceBrowse);
-NAN_METHOD(DNSServiceRefDeallocate);
-NAN_METHOD(DNSServiceResolve);
-NAN_METHOD(DNSServiceEnumerateDomains);
-#ifdef HAVE_DNSSERVICEGETADDRINFO
-NAN_METHOD(DNSServiceGetAddrInfo); 
-#endif
-NAN_METHOD(TXTRecordCreate); 
-NAN_METHOD(TXTRecordDeallocate);
-//NAN_METHOD(TXTRecordGetCount); 
-NAN_METHOD(TXTRecordSetValue); 
-NAN_METHOD(TXTRecordGetLength);
-
-// === posix ============================================
-#ifdef NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-NAN_METHOD(if_nametoindex); 
-NAN_METHOD(if_indextoname);
-#endif
-
-// === additions ========================================
-NAN_METHOD(txtRecordBufferToObject);
-NAN_METHOD(exportConstants);
-NAN_METHOD(buildException);
-
-// === locals ===========================================
-
-void defineFunction(Local<Object> target, const char * name, Nan::FunctionCallback f);
-void addConstants(Local<Object> target);
-
-void
-init(Local<Object> target) {
-    Nan::HandleScope scope;
-
-    ServiceRef::Initialize( target );
-    TxtRecordRef::Initialize( target );
-#ifdef NODE_MDNS_USE_SOCKET_WATCHER
-    SocketWatcher::Initialize( target );
-#endif
-
-    defineFunction(target, "DNSServiceRegister", DNSServiceRegister);
-    defineFunction(target, "DNSServiceUpdateRecord", DNSServiceUpdateRecord);
-    defineFunction(target, "DNSServiceRefSockFD", DNSServiceRefSockFD);
-    defineFunction(target, "DNSServiceProcessResult", DNSServiceProcessResult);
-    defineFunction(target, "DNSServiceBrowse", DNSServiceBrowse);
-    defineFunction(target, "DNSServiceRefDeallocate", DNSServiceRefDeallocate);
-    defineFunction(target, "DNSServiceResolve", DNSServiceResolve);
-    defineFunction(target, "DNSServiceEnumerateDomains",
-            DNSServiceEnumerateDomains);
-#ifdef HAVE_DNSSERVICEGETADDRINFO
-    defineFunction(target, "DNSServiceGetAddrInfo", DNSServiceGetAddrInfo);
-#endif
-    defineFunction(target, "TXTRecordCreate", TXTRecordCreate);
-    defineFunction(target, "TXTRecordDeallocate", TXTRecordDeallocate);
-    //defineFunction(target, "TXTRecordGetCount", TXTRecordGetCount);
-    defineFunction(target, "TXTRecordSetValue", TXTRecordSetValue);
-    defineFunction(target, "TXTRecordGetLength", TXTRecordGetLength);
-
-#ifdef NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-    defineFunction(target, "if_nametoindex", if_nametoindex);
-    defineFunction(target, "if_indextoname", if_indextoname);
-#endif
-
-    defineFunction(target, "txtRecordBufferToObject", txtRecordBufferToObject);
-    defineFunction(target, "buildException", buildException);
-    defineFunction(target, "exportConstants", exportConstants);
-
-    addConstants(target);
-}
-
-inline
-void
-defineFunction(Local<Object> target, const char * name, Nan::FunctionCallback f) {
-    Nan::Set(target, Nan::New(name).ToLocalChecked(),
-            Nan::GetFunction(Nan::New<FunctionTemplate>(f)).ToLocalChecked());
-}
-
-NAN_METHOD(buildException) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsInt32()) {
-        return throwTypeError("argument 1 must be an integer " 
-                "(DNSServiceErrorType)");
-    }
-
-    DNSServiceErrorType error = Nan::To<int32_t>(info[0]).FromJust();
-    info.GetReturnValue().Set<v8::Value>(buildException(error));
-}
-
-void
-addConstants(Local<Object> target) {
-    // DNS Classes
-    NODE_DEFINE_CONSTANT(target, kDNSServiceClass_IN);
-
-    // DNS Error Codes
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoError);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Unknown);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoSuchName);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoMemory);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadParam);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadReference);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadState);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadFlags);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Unsupported);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NotInitialized);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_AlreadyRegistered);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NameConflict);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Invalid);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Firewall);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Incompatible);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadInterfaceIndex);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Refused);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoSuchRecord);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoAuth);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoSuchKey);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NATTraversal);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_DoubleNAT);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadTime);
-#ifdef kDNSServiceErr_BadSig
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadSig);
-#endif
-#ifdef kDNSServiceErr_BadKey
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_BadKey);
-#endif
-#ifdef kDNSServiceErr_Transient
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_Transient);
-#endif
-#ifdef kDNSServiceErr_ServiceNotRunning
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_ServiceNotRunning);
-#endif
-#ifdef kDNSServiceErr_NATPortMappingUnsupported
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NATPortMappingUnsupported);
-#endif
-#ifdef kDNSServiceErr_NATPortMappingDisabled
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NATPortMappingDisabled);
-#endif
-#ifdef kDNSServiceErr_NoRouter
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_NoRouter);
-#endif
-#ifdef kDNSServiceErr_PollingMode
-    NODE_DEFINE_CONSTANT(target, kDNSServiceErr_PollingMode);
-#endif
-
-    // Interface Index
-#ifdef kDNSServiceInterfaceIndexAny
-    NODE_DEFINE_CONSTANT(target, kDNSServiceInterfaceIndexAny);
-#endif
-#ifdef kDNSServiceInterfaceIndexLocalOnly
-    NODE_DEFINE_CONSTANT(target, kDNSServiceInterfaceIndexLocalOnly);
-#endif
-#ifdef kDNSServiceInterfaceIndexP2P
-    NODE_DEFINE_CONSTANT(target, kDNSServiceInterfaceIndexP2P);
-#endif
-#ifdef kDNSServiceInterfaceIndexUnicast
-    NODE_DEFINE_CONSTANT(target, kDNSServiceInterfaceIndexUnicast);
-#endif
-
-    // DNS Service Types
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_A);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NS);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MD);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MF);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_CNAME);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SOA);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MB);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MG);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NULL);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_WKS);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_PTR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_HINFO);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MINFO);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MX);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_TXT);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_RP);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_AFSDB);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_X25);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_ISDN);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_RT);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NSAP);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NSAP_PTR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SIG);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_KEY);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_PX);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_GPOS);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_AAAA);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_LOC);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NXT);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_EID);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NIMLOC);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SRV);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_ATMA);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NAPTR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_KX);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_CERT);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_A6);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_DNAME);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SINK);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_OPT);
-#ifdef kDNSServiceType_APL
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_APL);
-#endif
-#ifdef kDNSServiceType_DS
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_DS);
-#endif
-#ifdef kDNSServiceType_SSHFP
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SSHFP);
-#endif
-#ifdef kDNSServiceType_IPSECKEY
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_IPSECKEY);
-#endif
-#ifdef kDNSServiceType_RRSIG
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_RRSIG);
-#endif
-#ifdef kDNSServiceType_NSEC
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NSEC);
-#endif
-#ifdef kDNSServiceType_DNSKEY
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_DNSKEY);
-#endif
-#ifdef kDNSServiceType_DHCID
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_DHCID);
-#endif
-#ifdef kDNSServiceType_NSEC3
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NSEC3);
-#endif
-#ifdef kDNSServiceType_NSEC3PARAM
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_NSEC3PARAM);
-#endif
-#ifdef kDNSServiceType_HIP
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_HIP);
-#endif
-#ifdef kDNSServiceType_SPF
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_SPF);
-#endif
-#ifdef kDNSServiceType_UINFO
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_UINFO);
-#endif
-#ifdef kDNSServiceType_UID
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_UID);
-#endif
-#ifdef kDNSServiceType_GID
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_GID);
-#endif
-#ifdef kDNSServiceType_UNSPEC
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_UNSPEC);
-#endif
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_TKEY);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_TSIG);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_IXFR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_AXFR);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MAILB);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_MAILA);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceType_ANY);
-
-    // General Flags
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsMoreComing);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsAdd);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsDefault);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsNoAutoRename);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsShared);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsUnique);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsBrowseDomains);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsRegistrationDomains);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsLongLivedQuery);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsAllowRemoteQuery);
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsForceMulticast);
-#ifdef kDNSServiceFlagsForce
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsForce);
-#endif
-#ifdef kDNSServiceFlagsReturnIntermediates
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsReturnIntermediates);
-#endif
-#ifdef kDNSServiceFlagsNonBrowsable
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsNonBrowsable);
-#endif
-#ifdef kDNSServiceFlagsShareConnection
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsShareConnection);
-#endif
-#ifdef kDNSServiceFlagsSuppressUnusable
-    NODE_DEFINE_CONSTANT(target, kDNSServiceFlagsSuppressUnusable);
-#endif
-}
-
-NAN_METHOD(exportConstants) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject()) {
-        return throwTypeError("argument 1 must be an object.");
-    }
-
-    addConstants(info[0]->ToObject());
-}
-
-} // end of namespace node_mdns
-
-NODE_MODULE(dns_sd_bindings,node_mdns::init);
diff --git a/lib/jdiscovery/mdns/src/dns_service_browse.cpp b/lib/jdiscovery/mdns/src/dns_service_browse.cpp
deleted file mode 100644
index 04967abc..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_browse.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-#include "mdns.hpp"
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-static
-void
-DNSSD_API
-OnServiceChanged(DNSServiceRef sdRef, DNSServiceFlags flags, 
-        uint32_t interfaceIndex, DNSServiceErrorType errorCode, 
-        const char * serviceName, const char * serviceType,
-        const char * replyDomain, void * context)
-{
-    Nan::HandleScope scope;
-    ServiceRef * serviceRef = static_cast<ServiceRef*>(context);
-    Local<Function> callback = serviceRef->GetCallback();
-    Local<Object> this_ = serviceRef->GetThis();
-
-    const size_t argc(8);
-    Local<Value> info[argc];
-    info[0] = serviceRef->handle();
-    info[1] = Nan::New<Integer>(flags);
-    info[2] = Nan::New<Uint32>(interfaceIndex);
-    info[3] = Nan::New<Integer>(errorCode);
-    info[4] = stringOrUndefined(serviceName);
-    info[5] = stringOrUndefined(serviceType);
-    info[6] = stringOrUndefined(replyDomain);
-    if (serviceRef->GetContext().IsEmpty()) {
-        info[7] = Nan::Undefined();
-    } else {
-        info[7] = serviceRef->GetContext();
-    }
-    Nan::MakeCallback(this_, callback, argc, info);
-}
-
-NAN_METHOD(DNSServiceBrowse) { 
-    if (argumentCountMismatch(info, 7)) {
-        return throwArgumentCountMismatchException(info, 7);
-    }
-
-    if ( ! ServiceRef::HasInstance(info[0])) {
-        return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if (serviceRef->IsInitialized()) {
-        return throwError("DNSServiceRef is already initialized");
-    }
-
-    if ( ! info[1]->IsInt32()) {
-        return throwError("argument 2 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[1]->ToInteger()->Int32Value();
-
-    if ( ! info[2]->IsUint32() && ! info[2]->IsInt32()) {
-        return throwTypeError("argument 3 must be an integer (interfaceIndex)");
-    }
-    uint32_t interfaceIndex = info[2]->ToInteger()->Uint32Value();
-
-    if ( ! info[3]->IsString()) {
-        return throwTypeError("argument 4 must be a string (service type)");
-    }
-    String::Utf8Value serviceType(info[3]->ToString());
-
-    bool has_domain = false;
-    if ( ! info[4]->IsNull() && ! info[4]->IsUndefined()) {
-        if ( ! info[4]->IsString()) {
-            return throwTypeError("argument 5 must be a string (domain)");
-        }
-        has_domain = true;
-    }
-    String::Utf8Value domain(info[4]);
-
-    if ( ! info[5]->IsFunction()) {
-        return throwTypeError("argument 6 must be a function (callBack)");
-    }
-    serviceRef->SetCallback(Local<Function>::Cast(info[5]));
-
-    if ( ! info[6]->IsNull() && ! info[6]->IsUndefined()) {
-        serviceRef->SetContext(info[6]);
-    }
-
-    DNSServiceErrorType error = DNSServiceBrowse( & serviceRef->GetServiceRef(),
-            flags, interfaceIndex, *serviceType, has_domain ? *domain : NULL,
-            OnServiceChanged, serviceRef);
-
-    if (error != kDNSServiceErr_NoError) {
-        return throwMdnsError(error);
-    }
-
-    if ( ! serviceRef->SetSocketFlags()) {
-        return throwError("Failed to set socket flags (O_NONBLOCK, FD_CLOEXEC)");
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_enumerate_domains.cpp b/lib/jdiscovery/mdns/src/dns_service_enumerate_domains.cpp
deleted file mode 100644
index fa1ce542..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_enumerate_domains.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "mdns.hpp"
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-void
-DNSSD_API
-OnEnumeration(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex,
-        DNSServiceErrorType errorCode, const char * replyDomain, void * context)
-{
-    Nan::HandleScope scope;
-    ServiceRef * serviceRef = static_cast<ServiceRef*>(context);
-    Local<Function> callback = serviceRef->GetCallback();
-    Local<Object> this_ = serviceRef->GetThis();
-
-    const size_t argc(6);
-    Local<Value> info[argc];
-    info[0] = serviceRef->handle();
-    info[1] = Nan::New<Integer>(flags);
-    info[2] = Nan::New<Uint32>(interfaceIndex);
-    info[3] = Nan::New<Integer>(errorCode);
-    info[4] = stringOrUndefined(replyDomain);
-    info[5] = serviceRef->GetContext();
-    Nan::MakeCallback(this_, callback, argc, info);
-}
-
-NAN_METHOD(DNSServiceEnumerateDomains) {
-    if (argumentCountMismatch(info, 5)) {
-        return throwArgumentCountMismatchException(info, 5);
-    }
-    
-    if ( ! ServiceRef::HasInstance(info[0])) {
-      return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if (serviceRef->IsInitialized()) {
-      return throwError("DNSServiceRef is already initialized");
-    }
-
-    if ( ! info[1]->IsInt32()) {
-      return throwError("argument 2 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[1]->ToInteger()->Int32Value();
-
-    if ( ! info[2]->IsUint32() && ! info[2]->IsInt32()) {
-      return throwTypeError("argument 3 must be an integer (interfaceIndex)");
-    }
-    uint32_t interfaceIndex = info[2]->ToInteger()->Uint32Value();
-
-    if ( ! info[3]->IsFunction()) {
-      return throwTypeError("argument 4 must be a function (callBack)");
-    }
-    serviceRef->SetCallback(Local<Function>::Cast(info[3]));
-
-    serviceRef->SetContext(info[4]);
-
-    DNSServiceErrorType error = DNSServiceEnumerateDomains( & serviceRef->GetServiceRef(),
-            flags, interfaceIndex, OnEnumeration, serviceRef);
-
-    if (error != kDNSServiceErr_NoError) {
-      return throwMdnsError(error);
-    }
-    if ( ! serviceRef->SetSocketFlags()) {
-      return throwError("Failed to set socket flags (O_NONBLOCK, FD_CLOEXEC)");
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_get_addr_info.cpp b/lib/jdiscovery/mdns/src/dns_service_get_addr_info.cpp
deleted file mode 100644
index f711c400..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_get_addr_info.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-#include "mdns.hpp"
-
-#ifndef WIN32 // XXX
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/socket.h> // AF_INET and AF_INET6 on freebsd
-#endif
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-#ifdef HAVE_DNSSERVICEGETADDRINFO
-
-void
-DNSSD_API
-OnAddressInfo(DNSServiceRef sdRef, DNSServiceFlags flags, 
-        uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-        const char * hostname, const struct sockaddr * address,
-        uint32_t ttl, void * context)
-{
-    if ( ! context) return;
-
-    Nan::HandleScope scope;
-    ServiceRef * serviceRef = static_cast<ServiceRef*>(context);
-    Local<Function> callback = serviceRef->GetCallback();
-    Local<Object> this_ = serviceRef->GetThis();
-
-    const size_t argc(8);
-    Local<Value> info[argc];
-    info[0] = serviceRef->handle();
-    info[1] = Nan::New<Integer>(flags);
-    info[2] = Nan::New<Uint32>(interfaceIndex);
-    info[3] = Nan::New<Integer>(errorCode);
-    info[4] = stringOrUndefined(hostname);
-    info[5] = Nan::New<String>().ToLocalChecked();
-    char ip[INET6_ADDRSTRLEN];
-    struct sockaddr_in *a4;
-    struct sockaddr_in6 *a6;
-    switch (address->sa_family) {
-        case AF_INET6:
-            a6 = (struct sockaddr_in6*)(address);
-            inet_ntop(AF_INET6, &(a6->sin6_addr), ip, INET6_ADDRSTRLEN);
-            info[5] = Nan::New(ip).ToLocalChecked();
-            break;
-        case AF_INET:
-            a4 = (struct sockaddr_in*)(address);
-            inet_ntop(AF_INET, &(a4->sin_addr), ip, INET6_ADDRSTRLEN);
-            info[5] = Nan::New(ip).ToLocalChecked();
-            break;
-        default:
-            break;
-    }
-
-    info[6] = Nan::New<Integer>(ttl);
-
-    if (serviceRef->GetContext().IsEmpty()) {
-        info[7] = Nan::Undefined();
-    } else {
-        info[7] = serviceRef->GetContext();
-    }
-    Nan::MakeCallback(this_, callback, argc, info);
-}
-
-NAN_METHOD(DNSServiceGetAddrInfo) {
-
-    if (argumentCountMismatch(info, 7)) {
-        return throwArgumentCountMismatchException(info, 7);
-    }
-
-    if ( ! ServiceRef::HasInstance(info[0])) {
-        return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if (serviceRef->IsInitialized()) {
-        return throwError("DNSServiceRef is already initialized");
-    }
-
-    if ( ! info[1]->IsInt32()) {
-        return throwError("argument 2 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[1]->ToInteger()->Int32Value();
-
-    if ( ! info[2]->IsUint32() && ! info[2]->IsInt32()) {
-       return throwTypeError("argument 3 must be an integer (interfaceIndex)");
-    }
-    uint32_t interfaceIndex = info[2]->ToInteger()->Uint32Value();
-
-    if ( ! info[3]->IsInt32()) {
-        return throwTypeError("argument 4 must be an integer (DNSServiceProtocol)");
-    }
-    uint32_t protocol = info[3]->ToInteger()->Int32Value();
-
-    if ( ! info[4]->IsString()) {
-        return throwTypeError("argument 5 must be a string (hostname)");
-    }
-    String::Utf8Value hostname(info[4]->ToString());
-
-    if ( ! info[5]->IsFunction()) {
-        return throwTypeError("argument 6 must be a function (callBack)");
-    }
-    serviceRef->SetCallback(Local<Function>::Cast(info[5]));
-
-    if ( ! info[6]->IsNull() && ! info[6]->IsUndefined()) {
-        serviceRef->SetContext(info[6]);
-    }
-
-    DNSServiceErrorType error = DNSServiceGetAddrInfo( & serviceRef->GetServiceRef(),
-            flags, interfaceIndex, protocol, *hostname, OnAddressInfo, serviceRef);
-
-    if (error != kDNSServiceErr_NoError) {
-        return throwMdnsError(error);
-    }
-    if ( ! serviceRef->SetSocketFlags()) {
-        return throwError("Failed to set socket flags (O_NONBLOCK, FD_CLOEXEC)");
-    }
-}
-
-#endif // HAVE_DNSSERVICEGETADDRINFO
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_process_result.cpp b/lib/jdiscovery/mdns/src/dns_service_process_result.cpp
deleted file mode 100644
index 0e751771..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_process_result.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "mdns.hpp"
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(DNSServiceProcessResult) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject()) {
-        return throwTypeError("argument 1 must be a DNSServiceRef object");
-    }
-
-    ServiceRef * ref = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    ref->SetThis(info.This());
-    DNSServiceErrorType error = DNSServiceProcessResult(ref->GetServiceRef());
-    if (error != kDNSServiceErr_NoError) {
-        return throwMdnsError(error);
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_ref.cpp b/lib/jdiscovery/mdns/src/dns_service_ref.cpp
deleted file mode 100644
index 5f522706..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_ref.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#include "mdns.hpp"
-
-#include "dns_service_ref.hpp"
-#include "mdns_utils.hpp"
-
-using namespace v8;
-
-namespace node_mdns {
-
-Nan::Persistent<FunctionTemplate> ServiceRef::constructor_template;
-
-ServiceRef::ServiceRef() : ref_(), callback_(), context_() {}
-
-ServiceRef::~ServiceRef() {
-    // First, dispose the serice ref. This cancels all asynchronous operations.
-    if (ref_) {
-        DNSServiceRefDeallocate(ref_);
-    }
-    // Then release the js objects.
-    if ( ! callback_.IsEmpty()) {
-        callback_.Reset();
-    }
-    if ( ! context_.IsEmpty()) {
-        context_.Reset();
-    }
-}
-
-void
-ServiceRef::Initialize(Local<Object> target) {
-    Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
-    constructor_template.Reset(t);
-    t->InstanceTemplate()->SetInternalFieldCount(1);
-    t->SetClassName(Nan::New("DNSServiceRef").ToLocalChecked());
-    
-    Nan::SetAccessor(t->InstanceTemplate(), Nan::New("fd").ToLocalChecked(), fd_getter);
-    Nan::SetAccessor(t->InstanceTemplate(),  Nan::New("initialized").ToLocalChecked(), initialized_getter);
-    Nan::Set(target, Nan::New("DNSServiceRef").ToLocalChecked(), Nan::GetFunction(t).ToLocalChecked());
-}
-
-NAN_METHOD(ServiceRef::New) {
-    if (argumentCountMismatch(info, 0)) {
-        return throwArgumentCountMismatchException(info, 0);
-    }
-    ServiceRef * o = new ServiceRef();
-    o->Wrap(info.Holder());
-    info.GetReturnValue().Set(info.This());
-}
-
-bool
-ServiceRef::IsInitialized() const { return ref_ != NULL; }
-
-bool
-ServiceRef::HasInstance(v8::Local<v8::Value> value) {
-  if ( ! value->IsObject() ) return false;
-  v8::Local<v8::Object> object = value->ToObject();
-  return Nan::New(constructor_template)->HasInstance( object );
-}
-
-void
-ServiceRef::SetCallback(v8::Local<v8::Function> callback) {
-  if ( ! callback_.IsEmpty()) {
-    callback_.Reset();
-  }
-  callback_.Reset<v8::Function>(callback);
-}
-
-v8::Local<v8::Function>
-ServiceRef::GetCallback() const { return Nan::New(callback_); }
-
-DNSServiceRef &
-ServiceRef::GetServiceRef() { return ref_; }
-
-void
-ServiceRef::SetServiceRef(DNSServiceRef ref) { ref_ = ref; }
-
-v8::Local<v8::Value>
-ServiceRef::GetContext() { return Nan::New(context_); }
-
-void
-ServiceRef::SetContext(v8::Local<v8::Value> context) {
-  if ( ! context_.IsEmpty()) {
-    context_.Reset();
-  }
-  context_.Reset<v8::Value>(context);
-}
-
-v8::Local<v8::Object>
-ServiceRef::GetThis() { return this_; }
-
-void
-ServiceRef::SetThis(v8::Local<v8::Object> This) { this_ = This; }
-
-bool
-ServiceRef::SetSocketFlags() {
-    return true;
-#if 0 // XXX I think IOWatcher does the right thing. TODO: check!
-  int fd = DNSServiceRefSockFD(ref_);
-  if (fd == -1) return false;
-  return fcntl(fd, F_SETFD, FD_CLOEXEC) != -1 &&
-    fcntl(fd, F_SETFL, O_NONBLOCK) != -1;
-#endif
-}
-
-NAN_PROPERTY_GETTER(ServiceRef::fd_getter) {
-    ServiceRef * service_ref = Nan::ObjectWrap::Unwrap<ServiceRef>(info.This());
-    int fd = -1;
-    if (service_ref->ref_) {
-        fd = DNSServiceRefSockFD(service_ref->ref_);
-        if (fd == -1) {
-            return Nan::ThrowError("DNSServiceRefSockFD() failed");
-        }
-    }
-    Local<Integer> v = Nan::New<Integer>(fd);
-    info.GetReturnValue().Set(v);
-}
-
-NAN_PROPERTY_GETTER(ServiceRef::initialized_getter) {
-    ServiceRef * service_ref = Nan::ObjectWrap::Unwrap<ServiceRef>(info.This());
-    info.GetReturnValue().Set(Nan::New<Boolean>(service_ref->IsInitialized()));
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_ref.hpp b/lib/jdiscovery/mdns/src/dns_service_ref.hpp
deleted file mode 100644
index 36b30dee..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_ref.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef NODE_MDNS_SERVICE_REF_INCLUDED
-#define NODE_MDNS_SERVICE_REF_INCLUDED
-
-#include <fcntl.h>
-
-namespace node_mdns {
-
-class ServiceRef : public Nan::ObjectWrap {
-    public:
-        ServiceRef();
-        ~ServiceRef();
-
-        static void Initialize(v8::Local<v8::Object> target);
-        static NAN_METHOD(New);
-
-        bool IsInitialized() const;
-
-        static bool HasInstance(v8::Local<v8::Value> value);
-
-        void SetCallback(v8::Local<v8::Function> callback);
-        v8::Local<v8::Function> GetCallback() const;
-
-
-        DNSServiceRef & GetServiceRef();
-        void SetServiceRef(DNSServiceRef ref);
-
-        v8::Local<v8::Value> GetContext();
-        void SetContext(v8::Local<v8::Value> context);
-
-        v8::Local<v8::Object> GetThis();
-        void SetThis(v8::Local<v8::Object> This);
-
-        bool SetSocketFlags();
-
-    private:
-        static NAN_PROPERTY_GETTER(fd_getter);
-        static NAN_PROPERTY_GETTER(initialized_getter);
-
-        DNSServiceRef ref_;
-        Nan::Persistent<v8::Function> callback_;
-        v8::Local<v8::Object>        this_;
-        Nan::Persistent<v8::Value>    context_;
-
-        static Nan::Persistent<v8::FunctionTemplate> constructor_template;
-};
-
-} // end of namespace node_mdns
-#endif // NODE_MDNS_SERVICE_REF_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/dns_service_ref_deallocate.cpp b/lib/jdiscovery/mdns/src/dns_service_ref_deallocate.cpp
deleted file mode 100644
index 31f74e26..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_ref_deallocate.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include "mdns.hpp"
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(DNSServiceRefDeallocate) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject() || ! ServiceRef::HasInstance(info[0]->ToObject())) {
-        return throwTypeError("argument 1 must be a DNSServiceRef object");
-    }
-
-    ServiceRef * ref = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if ( ! ref->IsInitialized()) {
-        return throwError("DNSServiceRef is not initialized");
-    }
-    DNSServiceRefDeallocate( ref->GetServiceRef());
-    ref->SetServiceRef(NULL);
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_ref_sock_fd.cpp b/lib/jdiscovery/mdns/src/dns_service_ref_sock_fd.cpp
deleted file mode 100644
index 307aeb90..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_ref_sock_fd.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#include "mdns.hpp"
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(DNSServiceRefSockFD) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject() || ! ServiceRef::HasInstance(info[0]->ToObject())) {
-      return throwTypeError("argument 1 must be a DNSServiceRef object");
-    }
-
-    ServiceRef * ref = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if ( ! ref->IsInitialized()) {
-      return throwError("DNSServiceRef is not initialized");
-    }
-    int fd = DNSServiceRefSockFD( ref->GetServiceRef());
-    if (fd == -1) {
-      return throwError("failed to get socket file descriptor");
-    }
-    info.GetReturnValue().Set( Nan::New<Integer>( fd ));
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_register.cpp b/lib/jdiscovery/mdns/src/dns_service_register.cpp
deleted file mode 100644
index 5f2b253b..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_register.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-#include "mdns.hpp"
-
-#include <limits>
-
-#ifndef WIN32 // XXX
-#include <arpa/inet.h>
-#endif
-
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-static
-void
-DNSSD_API
-OnServiceRegistered(DNSServiceRef sdRef, DNSServiceFlags flags,
-        DNSServiceErrorType errorCode, const char * name,
-        const char * serviceType, const char * domain, void * context)
-{
-    if ( ! context) return;
-
-    Nan::HandleScope scope;
-    ServiceRef * serviceRef = static_cast<ServiceRef*>(context);
-    Local<Function> callback = serviceRef->GetCallback();
-    Local<Object> this_ = serviceRef->GetThis();
-
-    if ( ! callback.IsEmpty() && ! this_.IsEmpty()) {
-        const size_t argc(7);
-        Local<Value> info[argc];
-        info[0] = serviceRef->handle();
-        info[1] = Nan::New<Integer>(flags);
-        info[2] = Nan::New<Integer>(errorCode);
-        info[3] = stringOrUndefined(name);
-        info[4] = stringOrUndefined(serviceType);
-        info[5] = stringOrUndefined(domain);
-        if (serviceRef->GetContext().IsEmpty()) {
-            info[6] = Nan::Undefined();
-        } else {
-            info[6] = serviceRef->GetContext();
-        }
-        Nan::MakeCallback(this_, callback, argc, info);
-    }
-}
-
-NAN_METHOD(DNSServiceRegister) {
-    if (argumentCountMismatch(info, 11)) {
-      return throwArgumentCountMismatchException(info, 11);
-    }
-
-    if ( ! ServiceRef::HasInstance(info[0])) {
-      return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if (serviceRef->IsInitialized()) {
-      return throwError("DNSServiceRef is already initialized");
-    }
-
-    if ( ! info[1]->IsInt32()) {
-      return throwError("argument 2 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[1]->ToInteger()->Int32Value();
-
-    if ( ! info[2]->IsUint32() && ! info[2]->IsInt32()) {
-      return throwTypeError("argument 3 must be an integer (interfaceIndex)");
-    }
-    uint32_t interfaceIndex = info[2]->ToInteger()->Uint32Value();
-
-    bool has_name = false;
-    if ( ! info[3]->IsNull() && ! info[3]->IsUndefined()) {
-        if ( ! info[3]->IsString()) {
-          return throwTypeError("argument 4 must be a string (name)");
-        }
-        has_name = true;
-    }
-    String::Utf8Value name(info[3]);
-
-    if ( ! info[4]->IsString()) {
-      return throwTypeError("argument 5 must be a string (service type)");
-    }
-    String::Utf8Value serviceType(info[4]->ToString());
-
-    bool has_domain = false;
-    if ( ! info[5]->IsNull() && ! info[5]->IsUndefined()) {
-        if ( ! info[5]->IsString()) {
-          return throwTypeError("argument 6 must be a string (domain)");
-        }
-        has_domain = true;
-    }
-    String::Utf8Value domain(info[5]);
-
-    bool has_host = false;
-    if ( ! info[6]->IsNull() && ! info[6]->IsUndefined()) {
-        if ( ! info[6]->IsString()) {
-          return throwTypeError("argument 7 must be a string (host)");
-        }
-        has_host = true;
-    }
-    String::Utf8Value host(info[6]);
-
-    if ( ! info[7]->IsInt32()) {
-      return throwTypeError("argument 8 must be an integer (port)");
-    }
-    int raw_port = info[7]->ToInteger()->Int32Value();
-    if (raw_port > std::numeric_limits<uint16_t>::max() || raw_port < 0) {
-      return throwError("argument 8: port number is out of bounds.");
-    }
-    uint16_t port = static_cast<uint16_t>(raw_port);
-
-    uint16_t txtLen(0);
-    const void * txtRecord(NULL);
-    if ( ! info[8]->IsNull() && ! info[8]->IsUndefined()) {
-        if (Buffer::HasInstance(info[8])) {
-            Local<Object> bufferObject = info[8]->ToObject();
-            txtRecord = Buffer::Data(bufferObject);
-            txtLen = Buffer::Length(bufferObject);
-        } else if (TxtRecordRef::HasInstance(info[8])) {
-            TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[8]->ToObject());
-            txtLen = TXTRecordGetLength( & ref->GetTxtRecordRef());
-            txtRecord = TXTRecordGetBytesPtr( & ref->GetTxtRecordRef());
-        } else {
-          return throwTypeError("argument 9 must be a buffer or a dns_sd.TXTRecordRef");
-        }
-    }
-
-    if ( ! info[9]->IsNull() && ! info[9]->IsUndefined()) {
-        if ( ! info[9]->IsFunction()) {
-          return throwTypeError("argument 10 must be a function (callBack)");
-        }
-        serviceRef->SetCallback(Local<Function>::Cast(info[9]));
-    }
-
-    if ( ! info[10]->IsNull() && ! info[10]->IsUndefined()) {
-        serviceRef->SetContext(info[10]);
-    }
-
-    // eleven arguments ... srsly?
-    DNSServiceErrorType error = DNSServiceRegister(
-            & serviceRef->GetServiceRef(),
-            flags,
-            interfaceIndex,
-            has_name ? * name : NULL,
-            *serviceType,
-            has_domain ? * domain : NULL,
-            has_host ? * host : NULL,
-            htons(port),
-            txtLen,
-            txtRecord,
-            info[9]->IsFunction() ? OnServiceRegistered : NULL,
-            serviceRef);
-    if (error != kDNSServiceErr_NoError) {
-      return throwMdnsError(error);
-    }
-    if ( ! serviceRef->SetSocketFlags()) {
-      return throwError("Failed to set socket flags (O_NONBLOCK, FD_CLOEXEC)");
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_resolve.cpp b/lib/jdiscovery/mdns/src/dns_service_resolve.cpp
deleted file mode 100644
index 29e06ac9..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_resolve.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "mdns.hpp"
-
-#include <string.h>
-
-#ifndef WIN32 // XXX
-#include <arpa/inet.h>
-#endif
-
-#include <v8.h>
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-void
-DNSSD_API
-OnResolve(DNSServiceRef sdRef, DNSServiceFlags flags,
-        uint32_t interfaceIndex, DNSServiceErrorType errorCode,
-        const char * fullname, const char * hosttarget, uint16_t port,
-        uint16_t txtLen, const unsigned char * txtRecord, void * context)
-{
-
-    Nan::HandleScope scope;
-    ServiceRef * serviceRef = static_cast<ServiceRef*>(context);
-    Local<Function> callback = serviceRef->GetCallback();
-    Local<Object> this_ = serviceRef->GetThis();
-
-    const size_t argc(9);
-    Local<Value> info[argc];
-    info[0] = serviceRef->handle();
-    info[1] = Nan::New<Integer>(flags);
-    info[2] = Nan::New<Uint32>(interfaceIndex);
-    info[3] = Nan::New<Integer>(errorCode);
-    info[4] = stringOrUndefined(fullname);
-    info[5] = stringOrUndefined(hosttarget);
-    info[6] = Nan::New<Integer>( ntohs(port) );
-    Local<Object> buffer = Nan::NewBuffer(txtLen).ToLocalChecked();
-    memcpy(Buffer::Data(buffer), txtRecord, txtLen);
-    info[7] = buffer;
-    if (serviceRef->GetContext().IsEmpty()) {
-        info[8] = Nan::Undefined();
-    } else {
-        info[8] = serviceRef->GetContext();
-    }
-    Nan::MakeCallback(this_, callback, argc, info);
-}
-
-NAN_METHOD(DNSServiceResolve) {
-
-    if (argumentCountMismatch(info, 8)) {
-        return throwArgumentCountMismatchException(info, 8);
-    }
-
-    if ( ! ServiceRef::HasInstance(info[0])) {
-        return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-    if (serviceRef->IsInitialized()) {
-        return throwError("DNSServiceRef is already initialized");
-    }
-
-    if ( ! info[1]->IsInt32()) {
-        return throwError("argument 2 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[1]->ToInteger()->Int32Value();
-
-    if ( ! info[2]->IsUint32() && ! info[2]->IsInt32()) {
-        return throwTypeError("argument 3 must be an integer (interfaceIndex)");
-    }
-    uint32_t interfaceIndex = info[2]->ToInteger()->Uint32Value();
-
-    if ( ! info[3]->IsString()) {
-        return throwTypeError("argument 4 must be a string (name)");
-    }
-    String::Utf8Value name(info[3]->ToString());
-
-    if ( ! info[4]->IsString()) {
-        return throwTypeError("argument 5 must be a string (service type)");
-    }
-    String::Utf8Value serviceType(info[4]->ToString());
-
-    if ( ! info[5]->IsString()) {
-        return throwTypeError("argument 6 must be a string (domain)");
-    }
-    String::Utf8Value domain(info[5]->ToString());
-
-    if ( ! info[6]->IsFunction()) {
-        return throwTypeError("argument 7 must be a function (callBack)");
-    }
-    serviceRef->SetCallback(Local<Function>::Cast(info[6]));
-
-    if ( ! info[7]->IsNull() && ! info[7]->IsUndefined()) {
-        serviceRef->SetContext(info[7]);
-    }
-
-    DNSServiceErrorType error = DNSServiceResolve( & serviceRef->GetServiceRef(),
-            flags, interfaceIndex, *name, *serviceType, *domain, OnResolve, serviceRef);
-
-    if (error != kDNSServiceErr_NoError) {
-        return throwMdnsError(error);
-    }
-    if ( ! serviceRef->SetSocketFlags()) {
-        return throwError("Failed to set socket flags (O_NONBLOCK, FD_CLOEXEC)");
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/dns_service_update_record.cpp b/lib/jdiscovery/mdns/src/dns_service_update_record.cpp
deleted file mode 100644
index ff152ac6..00000000
--- a/lib/jdiscovery/mdns/src/dns_service_update_record.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#include "mdns.hpp"
-
-#include <limits>
-
-#ifndef WIN32 // XXX
-#include <arpa/inet.h>
-#endif
-
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(DNSServiceUpdateRecord) {
-    if (argumentCountMismatch(info, 5)) {
-      return throwArgumentCountMismatchException(info, 5);
-    }
-
-    if ( ! ServiceRef::HasInstance(info[0])) {
-      return throwTypeError("argument 1 must be a DNSServiceRef (sdRef)");
-    }
-    ServiceRef * serviceRef = Nan::ObjectWrap::Unwrap<ServiceRef>(info[0]->ToObject());
-
-    if ( ! info[1]->IsNull()) {
-      return throwError("argument 2 must be zero. Custom records are not supported yet.");
-    }
-
-    if ( ! info[2]->IsInt32()) {
-      return throwError("argument 3 must be an integer (DNSServiceFlags)");
-    }
-    DNSServiceFlags flags = info[2]->ToInteger()->Int32Value();
-
-    uint16_t txtLen(0);
-    const void * txtRecord(NULL);
-    if ( ! info[3]->IsNull() && ! info[3]->IsUndefined()) {
-        if (Buffer::HasInstance(info[3])) {
-            Local<Object> bufferObject = info[3]->ToObject();
-            txtRecord = Buffer::Data(bufferObject);
-            txtLen = Buffer::Length(bufferObject);
-        } else if (TxtRecordRef::HasInstance(info[3])) {
-            TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[3]->ToObject());
-            txtLen = TXTRecordGetLength( & ref->GetTxtRecordRef());
-            txtRecord = TXTRecordGetBytesPtr( & ref->GetTxtRecordRef());
-        } else {
-          return throwTypeError("argument 4 must be a buffer or a dns_sd.TXTRecordRef");
-        }
-    }
-
-    if ( ! info[4]->IsInt32()) {
-      return throwError("argument 5 must be an integer (ttl)");
-    }
-    int ttl = info[4]->ToInteger()->Int32Value();
-
-    DNSServiceErrorType error = DNSServiceUpdateRecord(
-            serviceRef->GetServiceRef(),
-            NULL,
-            flags,
-            txtLen,
-            txtRecord,
-            ttl
-        );
-    if (error != kDNSServiceErr_NoError) {
-      return throwMdnsError(error);
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/mdns.hpp b/lib/jdiscovery/mdns/src/mdns.hpp
deleted file mode 100644
index df2ce73f..00000000
--- a/lib/jdiscovery/mdns/src/mdns.hpp
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef NODE_MDNS_INCLUDED
-#define NODE_MDNS_INCLUDED
-
-#include "mdns_settings.hpp"
-
-#ifdef WIN32
-# pragma warning( push )
-# pragma warning( disable: 4251 )
-#endif
-
-#include <node.h>
-#include "nan.h"
-
-#ifdef WIN32
-# pragma warning( pop )
-#endif
-
-#ifndef NODE_VERSION_AT_LEAST
-# include <node_version.h>
-#endif
-
-// XXX It would be better to test UV_VERSION_MAJOR and UV_VERSION_MINOR.
-//     However, libuv didn't bump the version when uv_poll_t was introduced.
-#if NODE_VERSION_AT_LEAST(0, 7, 9)
-# define NODE_MDNS_USE_SOCKET_WATCHER
-//# warning Using SocketWatcher
-#else
-//# warning Using IOWatcher
-#endif
-
-
-#include <dns_sd.h>
-
-#endif // NODE_MDNS_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/mdns_settings.hpp b/lib/jdiscovery/mdns/src/mdns_settings.hpp
deleted file mode 100644
index e5e0df8b..00000000
--- a/lib/jdiscovery/mdns/src/mdns_settings.hpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef NODE_MDNS_SETTINGS_INCLUDED
-# define NODE_MDNS_SETTINGS_INCLUDED
-
-# ifdef WIN32
-#  define _WINSOCKAPI_
-#  include <windows.h>
-
-   // Microsoft namespace pollution. A macro called 'max'? Srsly?
-#  ifdef max
-#   undef max
-#  endif
-
-#  ifndef NTDDI_VISTA
-#   define NTDDI_VISTA 0x6000000
-#  endif
-
-#  if NTDDI_VERSION >= NTDDI_VISTA
-#   define NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-#  endif
-
-# else // Unices
-
-#  define NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-
-# endif // WIN32
-
-
-#endif // NODE_MDNS_SETTINGS_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/mdns_utils.cpp b/lib/jdiscovery/mdns/src/mdns_utils.cpp
deleted file mode 100644
index 2a843688..00000000
--- a/lib/jdiscovery/mdns/src/mdns_utils.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-#include "mdns.hpp"
-
-#include "mdns_utils.hpp"
-
-#include <string>
-
-namespace node_mdns {
-
-using namespace v8;
-
-Local<Value>
-buildException(DNSServiceErrorType error_code) {
-    if (error_code == kDNSServiceErr_NoError) {
-        return Nan::Undefined();
-    }
-
-    std::string error_str("dns service error: ");
-    error_str += errorString(error_code);
-    Local<String> error_msg = Nan::New(error_str.c_str()).ToLocalChecked();
-    Local<Value> error_v = Exception::Error(error_msg);
-    Local<Object> error = error_v->ToObject();
-    Nan::Set(error, Nan::New("errorCode").ToLocalChecked(), Nan::New<Integer>(error_code));
-    return error_v;
-}
-
-const char *
-errorString(DNSServiceErrorType error) {
-    switch (error) {
-        case kDNSServiceErr_NoError:
-            return "no error";
-        case kDNSServiceErr_Unknown:
-            return "unknown";
-        case kDNSServiceErr_NoSuchName:
-            return "no such name";
-        case kDNSServiceErr_NoMemory:
-            return "no memory";
-        case kDNSServiceErr_BadParam:
-            return "bad param";
-        case kDNSServiceErr_BadReference:
-            return "bad reference";
-        case kDNSServiceErr_BadState:
-            return "bad state";
-        case kDNSServiceErr_BadFlags:
-            return "bad flags";
-        case kDNSServiceErr_Unsupported:
-            return "unsupported";
-        case kDNSServiceErr_NotInitialized:
-            return "not initialized";
-        case kDNSServiceErr_AlreadyRegistered:
-            return "already registered";
-        case kDNSServiceErr_NameConflict:
-            return "name conflict";
-        case kDNSServiceErr_Invalid:
-            return "invalid";
-        case kDNSServiceErr_Firewall:
-            return "firewall";
-        case kDNSServiceErr_Incompatible:
-            return "incompatible";
-        case kDNSServiceErr_BadInterfaceIndex:
-            return "bad interface index";
-        case kDNSServiceErr_Refused:
-            return "refused";
-        case kDNSServiceErr_NoSuchRecord:
-            return "no such record";
-        case kDNSServiceErr_NoAuth:
-            return "no auth";
-        case kDNSServiceErr_NoSuchKey:
-            return "no such key";
-        case kDNSServiceErr_NATTraversal:
-            return "NAT traversal";
-        case kDNSServiceErr_DoubleNAT:
-            return "double NAT";
-        case kDNSServiceErr_BadTime:
-            return "bad time";
-#ifdef kDNSServiceErr_BadSig
-        case kDNSServiceErr_BadSig:
-            return "bad sig";
-#endif
-#ifdef kDNSServiceErr_BadKey
-        case kDNSServiceErr_BadKey:
-            return "bad key";
-#endif
-#ifdef kDNSServiceErr_Transient
-        case kDNSServiceErr_Transient:
-            return "transient";
-#endif
-#ifdef kDNSServiceErr_ServiceNotRunning
-        case kDNSServiceErr_ServiceNotRunning:
-            return "service not running";
-#endif
-#ifdef kDNSServiceErr_NATPortMappingUnsupported
-        case kDNSServiceErr_NATPortMappingUnsupported:
-            return "NAT port mapping unsupported";
-#endif
-#ifdef kDNSServiceErr_NATPortMappingDisabled
-        case kDNSServiceErr_NATPortMappingDisabled:
-            return "NAT port mapping disabled";
-#endif
-#ifdef kDNSServiceErr_NoRouter
-        case kDNSServiceErr_NoRouter:
-            return "no router";
-#endif
-#ifdef kDNSServiceErr_PollingMode
-        case kDNSServiceErr_PollingMode:
-            return "polling mode";
-#endif
-#ifdef kDNSServiceErr_Timeout
-        case kDNSServiceErr_Timeout:
-            return "timeout";
-#endif
-        default:
-            return "unknown error code";
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/mdns_utils.hpp b/lib/jdiscovery/mdns/src/mdns_utils.hpp
deleted file mode 100644
index eb01af29..00000000
--- a/lib/jdiscovery/mdns/src/mdns_utils.hpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef NODE_MDNS_UTILS_INCLUDED
-#define NODE_MDNS_UTILS_INCLUDED
-
-#include <sstream>
-#include <errno.h>
-#include <fcntl.h>
-
-namespace node_mdns {
-
-const char * errorString(DNSServiceErrorType error);
-v8::Local<v8::Value> buildException(DNSServiceErrorType error_code);
-
-inline
-void
-throwError(const char * message) {
-    Nan::ThrowError( Nan::Error(message) );
-}
-
-inline
-void
-throwTypeError(const char * message) {
-    Nan::ThrowTypeError( message );
-}
-
-inline
-void
-throwMdnsError(DNSServiceErrorType error_code) {
-    Nan::ThrowError( buildException(error_code) );
-}
-
-inline
-bool
-argumentCountMismatch(Nan::NAN_METHOD_ARGS_TYPE info, int expectedCount) {
-    return info.Length() != expectedCount;
-}
-
-inline
-void
-throwArgumentCountMismatchException(Nan::NAN_METHOD_ARGS_TYPE info, size_t expectedCount) {
-    std::ostringstream msg;
-    msg << "argument count mismatch: expected " << expectedCount 
-        << ", but got " <<  info.Length() << " arguments.";
-    return throwError(msg.str().c_str());
-}
-
-inline
-v8::Local<v8::Value>
-stringOrUndefined(const char * str) {
-    if (str) {
-        return Nan::New<v8::String>(str).ToLocalChecked();
-    } else {
-        return Nan::Undefined();
-    }
-}
-
-} // end of namespace node_mdns
-
-#endif // NODE_MDNS_UTILS_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/network_interface.cpp b/lib/jdiscovery/mdns/src/network_interface.cpp
deleted file mode 100644
index 1cbbb44f..00000000
--- a/lib/jdiscovery/mdns/src/network_interface.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef NODE_MDNS_NETWORK_INTERFACE_INCLUDED
-#define NODE_MDNS_NETWORK_INTERFACE_INCLUDED
-
-#include "mdns.hpp"
-
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-
-#ifdef NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-
-# ifdef WIN32
-#  include <netioapi.h>
-# else
-#  include <sys/types.h>
-#  include <sys/socket.h>
-#  include <net/if.h> // if_nametoindex()
-# endif
-
-using namespace v8;
-
-namespace node_mdns {
-
-NAN_METHOD(if_nametoindex) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsString()) {
-        return throwTypeError("argument 1 must be a string (interface name)");
-    }
-    String::Utf8Value interfaceName(info[0]->ToString());
-
-#ifdef WIN32
-    DWORD aliasLength = MultiByteToWideChar(CP_UTF8, 0, *interfaceName, -1,
-            NULL, 0);
-    if (aliasLength == 0) {
-        return throwError("failed to determine buffer size");
-    }
-
-    wchar_t * alias = new wchar_t[aliasLength];
-    if ( ! alias) {
-        return throwError("failed to allocate alias buffer");
-    }
-
-    if (MultiByteToWideChar(CP_UTF8, 0, *interfaceName, -1, alias,
-                aliasLength) == 0)
-    {
-        delete [] alias;
-        return throwError("failed to convert utf8 to unicode");
-    }
-
-    NET_LUID luid;
-    if (ConvertInterfaceAliasToLuid(alias, &luid) != NO_ERROR) {
-        delete [] alias;
-        return throwError("failed to convert interface alias to luid");
-    }
-
-    delete [] alias;
-
-    NET_IFINDEX index = 0;
-    if (ConvertInterfaceLuidToIndex(&luid, &index) != NO_ERROR) {
-        return throwError("failed to convert interface luid to index");
-    }
-#else
-    unsigned int index = ::if_nametoindex(*interfaceName);
-#endif
-    if (index == 0) {
-        return throwError((std::string("interface '") + *interfaceName +
-                    "' does not exist").c_str());
-    }
-    info.GetReturnValue().Set( Nan::New<Integer>(static_cast<uint32_t>(index)));
-}
-
-NAN_METHOD(if_indextoname) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsUint32()) {
-        return throwTypeError("argument 1 must be a positive integer "
-                "(interface index)");
-    }
-#ifdef WIN32
-    NET_LUID luid;
-    if (ConvertInterfaceIndexToLuid(Nan::To<uint32_t>(info[0]).FromJust(), &luid) != NO_ERROR)
-    {
-        return throwError("failed to convert interface index to luid");
-    }
-    enum { size = NDIS_IF_MAX_STRING_SIZE + 1 };
-    wchar_t alias[size];
-    if (ConvertInterfaceLuidToAlias(&luid, alias, size) != NO_ERROR) {
-        return throwError("failed to convert interface luid to alias");
-    }
-    int utf8Length = WideCharToMultiByte(CP_UTF8, 0, alias, -1,
-            NULL, 0, NULL, NULL);
-    if (utf8Length == 0) {
-        return throwError("failed to determine buffer size");
-    }
-    char * nameBuffer = new char[utf8Length];
-
-    if (WideCharToMultiByte(CP_UTF8, 0, alias, -1, nameBuffer, utf8Length,
-                NULL, NULL) == 0)
-    {
-        delete [] nameBuffer;
-        return throwError("failed to convert unicode to utf8");
-    }
-    Local<String> name = Nan::New(nameBuffer).ToLocalChecked();
-    delete [] nameBuffer;
-#else
-    char nameBuffer[IFNAMSIZ];
-    if ( ! ::if_indextoname(Nan::To<uint32_t>(info[0]).FromJust(), nameBuffer)) {
-        return throwError("index has no corresponding interface");
-    }
-    Local<String> name = Nan::New(nameBuffer).ToLocalChecked();
-#endif
-    info.GetReturnValue().Set(name);
-}
-
-} // end of namespace node_mdns
-
-#endif // NODE_MDNS_HAVE_INTERFACE_NAME_CONVERSION
-
-#endif // NODE_MDNS_NETWORK_INTERFACE_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/skeleton.cpp_ b/lib/jdiscovery/mdns/src/skeleton.cpp_
deleted file mode 100644
index 4dea5c2e..00000000
--- a/lib/jdiscovery/mdns/src/skeleton.cpp_
+++ /dev/null
@@ -1,11 +0,0 @@
-#include <v8.h>
-
-#include "mdns_utils.hpp"
-#include "dns_service_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/socket_watcher.cpp b/lib/jdiscovery/mdns/src/socket_watcher.cpp
deleted file mode 100644
index 8883cf2e..00000000
--- a/lib/jdiscovery/mdns/src/socket_watcher.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-#include "mdns.hpp"
-
-// poor mans conditional compilation. is there a better way to do this with gyp?
-#ifdef NODE_MDNS_USE_SOCKET_WATCHER
-
-#include "socket_watcher.hpp"
-
-#include <string.h> // needed for memset() with node v0.7.9 on Mac OS
-#include <node.h>
-#include <node_version.h>
-
-using namespace v8;
-
-#if ! NODE_VERSION_AT_LEAST(0, 7, 8)
-namespace node {
-
-Handle<Value>
-MakeCallback(const Handle<Object> object, const Handle<Function> callback,
-        int argc, Handle<Value> argv[])
-{
-    Nan::HandleScope scope;
-
-    // TODO Hook for long stack traces to be made here.
-
-    NanTryCatch try_catch;
-    
-    Local<Value> ret = Nan::MakeCallback(object, callback, argc, info);
-
-    if (try_catch.HasCaught()) {
-        FatalException(try_catch);
-        return;
-    }
-
-    info.GetReturnValue().Set(ret);
-}
-
-}  // end of namespace node
-#endif
-
-namespace node_mdns {
-
-    SocketWatcher::SocketWatcher() : poll_(NULL), fd_(0), events_(0) {
-    }
-
-    void
-    SocketWatcher::Initialize(Handle<Object> target) {
-        Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
-
-        Local<String> symbol = Nan::New("SocketWatcher").ToLocalChecked();
-        t->SetClassName(symbol);
-        t->InstanceTemplate()->SetInternalFieldCount(1);
-
-        Nan::SetPrototypeMethod(t, "set", SocketWatcher::Set);
-        Nan::SetPrototypeMethod(t, "start", SocketWatcher::Start);
-        Nan::SetPrototypeMethod(t, "stop", SocketWatcher::Stop);
-
-        Nan::Set(target, symbol, Nan::GetFunction(t).ToLocalChecked());
-    }
-
-    NAN_METHOD(SocketWatcher::Start) {
-        SocketWatcher *watcher = Nan::ObjectWrap::Unwrap<SocketWatcher>(info.Holder());
-        watcher->Start();
-    }
-
-    void
-    SocketWatcher::Start() {
-        if (poll_ == NULL) {
-            poll_ = new uv_poll_t;
-            memset(poll_,0,sizeof(uv_poll_t));
-            poll_->data = this;
-            uv_poll_init_socket(uv_default_loop(), poll_, fd_);
-
-            Ref();
-        }
-
-        if (!uv_is_active((uv_handle_t*)poll_)) {
-            uv_poll_start(poll_, events_, &SocketWatcher::Callback);
-        }
-    }
-
-    void
-    SocketWatcher::Callback(uv_poll_t *w, int status, int revents) {
-        Nan::HandleScope scope;
-
-        SocketWatcher *watcher = static_cast<SocketWatcher*>(w->data);
-        assert(w == watcher->poll_);
-
-        Local<Value> callback_v = Nan::Get(watcher->handle(), Nan::New("callback").ToLocalChecked()).ToLocalChecked();
-        if (!callback_v->IsFunction()) {
-            watcher->Stop();
-            return;
-        }
-
-        Local<Function> callback = Local<Function>::Cast(callback_v);
-
-        Local<Value> argv[2];
-        argv[0] = revents & UV_READABLE ? Nan::True() : Nan::False();
-        argv[1] = revents & UV_WRITABLE ? Nan::True() : Nan::False();
-
-        Nan::MakeCallback(watcher->handle(), callback, 2, argv);
-    }
-
-    NAN_METHOD(SocketWatcher::Stop) {
-        SocketWatcher *watcher = Nan::ObjectWrap::Unwrap<SocketWatcher>(info.Holder());
-        watcher->Stop();
-    }
-
-    void
-    SocketWatcher::Stop() {
-        if (poll_ != NULL) {
-            uv_poll_stop(poll_);
-            Unref();
-        }
-    }
-
-    NAN_METHOD(SocketWatcher::New) {
-        SocketWatcher *s = new SocketWatcher();
-        s->Wrap(info.This());
-        info.GetReturnValue().Set(info.This());
-    }
-
-    NAN_METHOD(SocketWatcher::Set) {
-        SocketWatcher *watcher = Nan::ObjectWrap::Unwrap<SocketWatcher>(info.Holder());
-        if (!info[0]->IsInt32()) {
-            return Nan::ThrowTypeError("First arg should be a file descriptor.");
-        }
-        int fd = Nan::To<int32_t>(info[0]).FromJust();
-        if (!info[1]->IsBoolean()) {
-            return Nan::ThrowTypeError("Second arg should be a boolean (readable).");
-        }
-        int events = 0;
-
-        if (info[1]->IsTrue()) events |= UV_READABLE;
-
-        if (!info[2]->IsBoolean()) {
-            return Nan::ThrowTypeError("Third arg should be a boolean (writable).");
-        }
-
-        if (info[2]->IsTrue()) events |= UV_WRITABLE;
-
-        assert(watcher->poll_ == NULL);
-
-        watcher->fd_ = fd;
-        watcher->events_ = events;
-    }
-
-} // end of namespace node_mdns
-
-#endif // NODE_MDNS_USE_SOCKET_WATCHER
diff --git a/lib/jdiscovery/mdns/src/socket_watcher.hpp b/lib/jdiscovery/mdns/src/socket_watcher.hpp
deleted file mode 100644
index 32166035..00000000
--- a/lib/jdiscovery/mdns/src/socket_watcher.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef NODE_MDNS_SOCKET_WATCHER_INCLUDED
-#define NODE_MDNS_SOCKET_WATCHER_INCLUDED
-
-namespace node_mdns {
-
-class SocketWatcher : public Nan::ObjectWrap {
-    public:
-        SocketWatcher();
-
-        static void Initialize(v8::Handle<v8::Object> target);
-
-    private:
-        uv_poll_t* poll_;
-        int fd_;
-        int events_;
-
-        static NAN_METHOD(New);
-        static NAN_METHOD(Set);
-        static NAN_METHOD(Start);
-        static NAN_METHOD(Stop);
-        
-        void Start();
-        void Stop();
-        static void Callback(uv_poll_t *w, int status, int events);
-};
-
-} // end of namespace node_mdns
-
-#endif // NODE_MDNS_SOCKET_WATCHER_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/txt_record_buffer_to_object.cpp b/lib/jdiscovery/mdns/src/txt_record_buffer_to_object.cpp
deleted file mode 100644
index 1c5ce5a1..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_buffer_to_object.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#include "mdns.hpp"
-
-#include <vector>
-
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(txtRecordBufferToObject) {
-    if (argumentCountMismatch(info, 1)) {
-      return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject() || ! Buffer::HasInstance(info[0]->ToObject())) {
-      return throwTypeError("argument 1 must be a buffer (txtRecord)");
-    }
-    Local<Object> buffer = info[0]->ToObject();
-
-    Local<Object> result = Nan::New<Object>();
-    std::vector<char> key(16);
-    size_t buffer_length = Buffer::Length(buffer);
-    void * data = Buffer::Data(buffer);
-    uint16_t item_count = TXTRecordGetCount(buffer_length, data);
-    DNSServiceErrorType error;
-    const void * value_ptr;
-    uint8_t value_length;
-    for (uint16_t i = 0; i < item_count; ++i) {
-        while (kDNSServiceErr_NoMemory == (error =
-                    TXTRecordGetItemAtIndex(buffer_length, data, i, key.size(),
-                        &*key.begin(), & value_length, & value_ptr)))
-        {
-            key.resize(key.size() * 2);
-        }
-        if (error != kDNSServiceErr_NoError) {
-          return throwMdnsError(error);
-        }
-        if (value_ptr) {
-          Nan::Set(result, Nan::New(&*key.begin()).ToLocalChecked(),
-                Nan::New(static_cast<const char*>(value_ptr), value_length).ToLocalChecked());
-        } else {
-            Nan::Set(result, Nan::New(&*key.begin()).ToLocalChecked(), Nan::Undefined());
-        }
-    }
-    info.GetReturnValue().Set(result);
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/txt_record_create.cpp b/lib/jdiscovery/mdns/src/txt_record_create.cpp
deleted file mode 100644
index 0978a246..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_create.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "mdns.hpp"
-
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(TXTRecordCreate) {
-    if (argumentCountMismatch(info, 2)) {
-        return throwArgumentCountMismatchException(info, 2);
-    }
-    if ( ! info[0]->IsObject() || ! TxtRecordRef::HasInstance(info[0]->ToObject())) {
-        return throwTypeError("argument 1 must be a TXTRecordRef object");
-    }
-
-    void * buffer = NULL;
-    uint16_t buffer_length = 0;
-    if ( ! info[1]->IsUndefined() && ! info[1]->IsNull()) {
-        if ( ! info[1]->IsObject() || ! Buffer::HasInstance(info[1]->ToObject())) {
-            return throwTypeError("argument 1 must be a buffer");
-        }
-        Local<Object> buffer_object = info[1]->ToObject();
-        buffer = Buffer::Data(buffer_object);
-        buffer_length = Buffer::Length(buffer_object);
-    }
-
-    TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[0]->ToObject());
-    TXTRecordCreate( & ref->GetTxtRecordRef(), buffer_length, buffer);
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/txt_record_deallocate.cpp b/lib/jdiscovery/mdns/src/txt_record_deallocate.cpp
deleted file mode 100644
index f739dcda..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_deallocate.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "mdns.hpp"
-
-#include "mdns_utils.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(TXTRecordDeallocate) {
-    if (argumentCountMismatch(info, 1)) {
-      return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject() || ! TxtRecordRef::HasInstance(info[0]->ToObject())) {
-      return throwTypeError("argument 1 must be a TXTRecordRef object");
-    }
-
-    TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[0]->ToObject());
-    TXTRecordDeallocate( & ref->GetTxtRecordRef());
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/txt_record_get_length.cpp b/lib/jdiscovery/mdns/src/txt_record_get_length.cpp
deleted file mode 100644
index 75cf8cfc..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_get_length.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "mdns.hpp"
-
-#include "mdns_utils.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-NAN_METHOD(TXTRecordGetLength) {
-    if (argumentCountMismatch(info, 1)) {
-        return throwArgumentCountMismatchException(info, 1);
-    }
-    if ( ! info[0]->IsObject() || ! TxtRecordRef::HasInstance(info[0]->ToObject())) {
-        return throwTypeError("argument 1 must be a buffer (txtRecord)");
-    }
-    TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[0]->ToObject());
-    uint16_t result = ::TXTRecordGetLength( & ref->GetTxtRecordRef());
-    info.GetReturnValue().Set(result);
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/txt_record_ref.cpp b/lib/jdiscovery/mdns/src/txt_record_ref.cpp
deleted file mode 100644
index 92461f57..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_ref.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "mdns.hpp"
-
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-
-namespace node_mdns {
-
-Nan::Persistent<FunctionTemplate> TxtRecordRef::constructor_template;
-
-TxtRecordRef::TxtRecordRef() :
-    ref_()
-{
-}
-
-TxtRecordRef::~TxtRecordRef() {
-    TXTRecordDeallocate( & ref_);
-}
-
-void
-TxtRecordRef::Initialize(Handle<Object> target) {
-    Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
-    constructor_template.Reset(t);
-    t->InstanceTemplate()->SetInternalFieldCount(1);
-    t->SetClassName(Nan::New("TXTRecordRef").ToLocalChecked());
-
-    Nan::Set(target, Nan::New("TXTRecordRef").ToLocalChecked(),
-            Nan::GetFunction(t).ToLocalChecked());
-}
-
-NAN_METHOD(TxtRecordRef::New) {
-    TxtRecordRef * o = new TxtRecordRef();
-    o->Wrap(info.Holder());
-    info.GetReturnValue().Set(info.This());
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/src/txt_record_ref.hpp b/lib/jdiscovery/mdns/src/txt_record_ref.hpp
deleted file mode 100644
index 2ba53a51..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_ref.hpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef NODE_MDNS_TXT_RECORD_REF_INCLUDED
-#define NODE_MDNS_TXT_RECORD_REF_INCLUDED
-
-namespace node_mdns {
-
-class TxtRecordRef : public Nan::ObjectWrap {
-    public:
-        TxtRecordRef();
-        ~TxtRecordRef();
-
-        static void Initialize(v8::Handle<v8::Object> target);
-        static NAN_METHOD(New);
-
-        //inline bool IsInitialized() const { return ref_ != NULL; }
-
-        static inline bool HasInstance(v8::Handle<v8::Value> value) {
-            if ( ! value->IsObject() ) return false;
-            v8::Local<v8::Object> object = value->ToObject();
-            return Nan::New(constructor_template)->HasInstance( object );
-        }
-
-        TXTRecordRef & GetTxtRecordRef() { return ref_; }
-        void SetTxtRecordRef(TXTRecordRef ref) { ref_ = ref; }
-
-    private:
-        TXTRecordRef ref_;
-
-        static Nan::Persistent<v8::FunctionTemplate> constructor_template;
-};
-
-} // end of namespace node_mdns
-#endif // NODE_MDNS_TXT_RECORD_REF_INCLUDED
diff --git a/lib/jdiscovery/mdns/src/txt_record_set_value.cpp b/lib/jdiscovery/mdns/src/txt_record_set_value.cpp
deleted file mode 100644
index ed340bc9..00000000
--- a/lib/jdiscovery/mdns/src/txt_record_set_value.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-#include "mdns.hpp"
-
-#include <node_buffer.h>
-
-#include "mdns_utils.hpp"
-#include "txt_record_ref.hpp"
-
-using namespace v8;
-using namespace node;
-
-namespace node_mdns {
-
-size_t length(Handle<Value> v) {
-    if (v->IsString()) {
-        return v->ToString()->Utf8Length();
-    } else if (Buffer::HasInstance(v)) {
-        return Buffer::Length(v->ToObject());
-    } else {
-        return 0;
-    }
-}
-
-NAN_METHOD(TXTRecordSetValue) {
-    if (argumentCountMismatch(info, 3)) {
-        return throwArgumentCountMismatchException(info, 3);
-    }
-    if ( ! info[0]->IsObject() || ! TxtRecordRef::HasInstance(info[0]->ToObject())) {
-        return throwTypeError("argument 1 must be a TXTRecordRef object");
-    }
-    TxtRecordRef * ref = Nan::ObjectWrap::Unwrap<TxtRecordRef>(info[0]->ToObject());
-
-    if ( ! info[1]->IsString()) {
-        return throwTypeError("argument 1 must be a string (key)");
-    }
-    String::Utf8Value key(info[1]);
-    
-    if ( ! (info[2]->IsNull() || info[2]->IsUndefined() ||
-        Buffer::HasInstance(info[2]) || info[2]->IsString())) {
-        return throwTypeError("argument 1 must be null, undefined, a buffer or a string (value)");
-    }
-    DNSServiceErrorType code = TXTRecordSetValue( & ref->GetTxtRecordRef(), *key,
-            length(info[2]),
-            ((info[2]->IsNull()||info[2]->IsUndefined()) 
-                ? NULL : info[2]->IsString() ? *String::Utf8Value(info[2]->ToString()) : Buffer::Data(info[2]->ToObject())));
-
-    if (code != kDNSServiceErr_NoError) {
-        return throwMdnsError(code);
-    }
-}
-
-} // end of namespace node_mdns
diff --git a/lib/jdiscovery/mdns/tests/test_browser.js b/lib/jdiscovery/mdns/tests/test_browser.js
deleted file mode 100644
index e580644c..00000000
--- a/lib/jdiscovery/mdns/tests/test_browser.js
+++ /dev/null
@@ -1,98 +0,0 @@
-var st = require('../lib/service_type.js')
-  , dns_sd = require('../lib/dns_sd')
-  , proxyquire =  require('proxyquire')
-  ;
-
-//=== Browser ===========================================================
-
-module.exports["Browser"] = {
-  "Should retrieve interface name from cache when interface index is no longer valid": function(test) {
-    var callback = null
-      , invocations = 0
-      , threwException = false
-      , interfaceName = "foo"
-      , serviceName = "_foo._tcp.";
-
-    var mock_dns_sd = {
-      kDNSServiceErr_NoError: dns_sd.kDNSServiceErr_NoError,
-      kDNSServiceInterfaceIndexLocalOnly: dns_sd.kDNSServiceInterfaceIndexLocalOnly,
-      kDNSServiceFlagsAdd: dns_sd.kDNSServiceFlagsAdd,
-
-      // we stub the if_indextoname method
-      if_indextoname: function() {
-        invocations++;
-
-        if(invocations == 1) {
-          return interfaceName;
-        }
-
-        threwException = true;
-        throw new Error("Panic!");
-      },
-      // and stub DNSServiceBrowse to expose a reference to the passed callback
-      DNSServiceBrowse: function(sdRef, flags, ifaceIdx, serviceType, domain, on_service_changed, context) {
-        callback = function() {
-        // pass 1 as the interface index so we don't try to find the name for loopback
-          on_service_changed(sdRef, flags, 1, dns_sd.kDNSServiceErr_NoError, serviceName, serviceType, domain, context);
-        };
-      }
-    };
-
-    // we're going to expect two invocations, one where the interface is present and one where it's not
-    var serviceDownInvocations = 0;
-
-    // create the browser with our mock of dns_sd
-    var browser = proxyquire('../lib/browser.js', { './dns_sd': mock_dns_sd }).Browser.create(serviceName);
-    browser.on("serviceDown", function(service) {
-      serviceDownInvocations++;
-
-      if(serviceDownInvocations == 1) {
-        // first invocation, should have interface name from dns_sd callback
-        test.equal(service.networkInterface, interfaceName);
-
-        // should not have thrown an exception yet
-        test.ok( ! threwException);
-      }
-
-      if(serviceDownInvocations == 2) {
-        // second invocation, should have thrown an exception in the callback
-        test.ok(threwException);
-
-        // should still have the interface name even though we threw an exception
-        test.equal(service.networkInterface, interfaceName);
-
-        test.done();
-      }
-    });
-
-    // simulate two MDNS events, this will trigger the serviceDown event we listen for above.
-    callback();
-    callback();
-  },
-
-  "Should survive invalid MDNS advert": function(test) {
-    var callback = null
-      , serviceName = "_foo._tcp.";
-
-    var mock_dns_sd = {
-      kDNSServiceErr_NoError: dns_sd.kDNSServiceErr_NoError,
-
-      // and stub DNSServiceBrowse to expose a reference to the passed callback
-      DNSServiceBrowse: function(sdRef, flags, ifaceIdx, serviceType, domain, on_service_changed, context) {
-        callback = function() {
-          // pass 1 as the interface index so we don't try to find the name for loopback
-          on_service_changed(sdRef, flags, 1, dns_sd.kDNSServiceErr_NoError, '', '', domain, context);
-        };
-      }
-    };
-
-    // create the browser with our mock of dns_sd
-    var browser = proxyquire('../lib/browser.js', { './dns_sd': mock_dns_sd }).Browser.create(serviceName);
-    browser.on('error', function(error) {
-      test.equal(error.message, "protocol must be either '_tcp' or '_udp' but is 'undefined'");
-      test.done();
-    });
-
-    callback();
-  }
-}
diff --git a/lib/jdiscovery/mdns/tests/test_dns_sd.js b/lib/jdiscovery/mdns/tests/test_dns_sd.js
deleted file mode 100755
index 2d487b3b..00000000
--- a/lib/jdiscovery/mdns/tests/test_dns_sd.js
+++ /dev/null
@@ -1,970 +0,0 @@
-var path = require('path')
-  , mdns_test    = require('../utils/lib/mdns_test')
-  , dns_sd       = mdns_test.require('dns_sd')
-  , service_type = "_mdns_test._tcp"
-  , test_port    = 4321
-  ;
-
-//=== DNSServiceRef ===========================================================
-
-exports['DNSServiceRef'] = function(t) {
-  
-  var sr = new dns_sd.DNSServiceRef();
-
-  t.ok(sr,
-      'DNSServiceRef must be truthy');
-  t.strictEqual(sr.fd, -1,
-      'File descriptor must be -1');
-  t.strictEqual(sr.initialized, false,
-      'DNSServiceRef must not be initialized');
-  t.done();
-}
-
-//=== DNSServiceRegister ======================================================
-
-exports['DNSServiceRegister()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  t.doesNotThrow(function() {
-    var /* uses paren scope serviceRef */
-        flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister( serviceRef, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  'Call with minimal argumentss must succeed.');
-
-  t.notStrictEqual(serviceRef.fd, -1,
-      'File descriptor must not be -1 after initialization');
-
-  t.strictEqual(serviceRef.initialized, true,
-      'DNSServiceRef must be initialized');
-
-  t.throws(function() {
-    var /* uses parent scope serviceRef */
-        flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister( serviceRef, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  'Duplicate initialization of DNSServiceRef must throw');
-
-  t.doesNotThrow(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = 'My sh4red stuff'
-      , type   = service_type
-      , domain = 'somedomain'
-      , host   = null /* avahi throws 'bad param'*/
-      , port   = test_port
-      , txtRec = new Buffer('\0')
-      , cb     = function() {}
-      , ctx    = {anything: true}
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  'Call with all arguments on must succed.');
-
-  t.throws(function() { dns_sd.DNSServiceRegister(); },
-      'Call with zero arguments must throw.');
-
-  t.throws(function() {
-    var a1, a2, a3, a4, a5, a6, a7, a8;
-    dns_sd.DNSServiceRegister(a1, a2, a3, a4, a5, a6, a7, a7);
-  },  'Call with eight arguments must throw.');
-
-  t.throws(function() {
-    var ref    = { not_a_service_ref: true } /* broken */
-      , flags  = 0 
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'flags' must be a number, not a string.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = '=== KAPUTT ===' /* broken */
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'flags' must be a number, not a string.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = '=== KAPUTT ===' /* broken */
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'interfaceIndex' must be a number, not a string.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = 1111111111 /* broken */
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'name' must be a string, not a number.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = null /* broken */
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'regtype' must be a string, not null.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = 1111111111 /* broken */
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'regtype' has to be a string, not a number.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = 1111111111 /* broken */
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'domain' must not be a string, not a number.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = 1111111111 /* broken */
-      , port   = test_port
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'host' must be a string, not a number.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = '=== KAPUTT ===' /* broken */
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'port' must be a number, not a string.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = 1111111111 /* broken */
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'txtRecord' must be a TXTRecordRef or buffer, not a number.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = test_port
-      , txtRec = null
-      , cb     = '=== KAPUTT ===' /* broken */
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  "'callback' must be a function, not a string.");
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = 1 << 16 /* broken */
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  'port number must be <= ' + ((1 << 16) - 1) + '.');
-
-  t.throws(function() {
-    var ref    = new dns_sd.DNSServiceRef()
-      , flags  = 0
-      , iface  = 0
-      , name   = null
-      , type   = service_type
-      , domain = null
-      , host   = null
-      , port   = -1    /* broken */
-      , txtRec = null
-      , cb     = null
-      , ctx    = null
-      ;
-    dns_sd.DNSServiceRegister(ref, flags, iface, name, type, domain,
-      host, port, txtRec, cb, ctx);
-  },  'port number must >= 0.');
-
-  t.done();
-};
-
-//=== DNSServiceProcessResult =================================================
-
-exports['DNSServiceProcessResult()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef()
-    , IOWatcher  = require('../lib/io_watcher').IOWatcher
-    , watcher    = new IOWatcher()
-    , timeout    = 3000
-    , timeoutId  = setTimeout(function() {
-        t.ok(false ,"Test did not finish within " + (timeout/1000) + "s");
-        watcher.stop();
-        t.done();
-      }
-      , timeout);
-
-  t.throws(function() {
-    dns_sd.DNSServiceProcessResult();
-  });
-
-  t.throws(function() {
-    dns_sd.DNSServiceProcessResult('flip', 'flap', 'flop');
-  });
-
-  t.throws(function() {
-    dns_sd.DNSServiceProcessResult(new dns_sd.DNSServiceRef());
-  });
-
-  t.throws(function() {
-    dns_sd.DNSServiceProcessResult(5);
-  });
-
-  watcher.callback = function() {
-    dns_sd.DNSServiceProcessResult(serviceRef);
-  }
-
-  function result_callback(sdRef, flags, errorCode, name, serviceType, domain,
-      context)
-  {
-    t.strictEqual(sdRef, serviceRef,
-        'serviceRef must be identical');
-    t.strictEqual(typeof flags, "number",
-        "'flags' must be of type number");
-    t.strictEqual(typeof errorCode, "number",
-        "'errorCode' must be of type number");
-    t.strictEqual(errorCode, dns_sd.kDNSServiceErr_NoError,
-        "'errorCode' must be kDNSServiceErr_NoError");
-    t.strictEqual(typeof name, "string",
-        "'name' must be of type string");
-    t.strictEqual(typeof serviceType, "string",
-        "'serviceType' must be of type string");
-    t.strictEqual(typeof domain, "string",
-        "'domain' must be of type string");
-    t.strictEqual(typeof context, "string",
-        "'context' must be of type string (in this test)");
-    t.strictEqual(context, "foobar",
-      "expected 'foobar' but got '" + context + "'");
-
-    clearTimeout(timeoutId);
-    watcher.stop();
-    watcher.callback = null;
-    t.done();
-  }
-
-  dns_sd.DNSServiceRegister(serviceRef, 0, 0, null, service_type,
-      null, null, test_port, null, result_callback, "foobar");
-
-  watcher.set(serviceRef.fd, true, false);
-  watcher.start();
-}
-
-//=== DNSServiceRefSockFD =====================================================
-
-exports['DNSServiceRefSockFD()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  t.throws(function() {dns_sd.DNSServiceRefSockFD(serviceRef)},
-      'call with uninitialized serviceRef must throw');
-
-  dns_sd.DNSServiceRegister(serviceRef, 0, 0, null, service_type,
-      null, null, test_port, null, null, null);
-
-  var fd;
-  t.doesNotThrow(function() {fd = dns_sd.DNSServiceRefSockFD(serviceRef)},
-    "DNSServiceRefSockFD() must not throw");
-
-  t.notEqual(fd, -1, 'file descriptor must not be -1');
-
-  t.strictEqual(serviceRef.fd, fd,
-      'result of DNSServiceRefSockFD() and fd getter must be the same');
-
-  t.throws(function() { dns_sd.DNSServiceRefSockFD("narf"); },
-      'argument must be a DNSServiceRef');
-
-  t.throws(function() { dns_sd.DNSServiceRefSockFD(); },
-      'must throw when called with not enough arguments');
-
-  t.done();
-}
-
-//=== DNSServiceBrowse ========================================================
-
-exports['DNSServiceBrowse()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  t.doesNotThrow(function() {
-    dns_sd.DNSServiceBrowse(serviceRef, 0, 0, service_type, null,
-      function() {}, null);
-  }, "DNSServiceBrowse() must not throw");
-
-  t.throws(function() {
-    dns_sd.DNSServiceBrowse(serviceRef, 0, 0, service_type, null,
-      function() {}, null);
-  },  'serviceRef already initialized');
-
-  t.throws(function() {
-    dns_sd.DNSServiceBrowse();
-  }, 'not enough arguments');
-
-  t.throws(function() {
-    dns_sd.DNSServiceBrowse("", 0, 0, service_type, null,
-      function() {}, null);
-  }, "'serviceRef' must not be a string");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, "", 0, service_type, null,
-      function() {}, null);
-  }, "'flags' must be a number, not a string");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, "", service_type, null,
-      function() {}, null);
-  }, "'interfaceIndex' must be a number, not a string");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, null, null, function() {}, null);
-  }, "'regtype' must be a string, not null");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, 0, null, function() {}, null);
-  }, "'regtype' must be a string, not a number");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, service_type, 0,
-      function() {}, null);
-  }, "'domain' must be a string, not a number");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, service_type, null, 0, null);
-  }, "'callback' must be a function, not a number");
-
-  // coverage: take domain branch
-  t.doesNotThrow(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, service_type, 'somedomain',
-      function() {}, null);
-  }, "DNSServiceBrowse() must not throw");
-
-  // coverage: take domain undefined branch
-  t.doesNotThrow(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, service_type, undefined,
-      function() {}, null);
-  }, "DNSServiceBrowse() must not throw");
-
-  // coverage: take domain undefined branch
-  t.doesNotThrow(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceBrowse(ref, 0, 0, service_type, undefined,
-      function() {}, undefined);
-  }, "DNSServiceBrowse() must not throw");
-
-  t.done();
-}
-
-//=== DNSServiceRefDeallocate =================================================
-
-exports['DNSServiceRefDeallocate()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  dns_sd.DNSServiceRegister(serviceRef, 0, 0, null, "_node-mdns-test._tcp",
-      null, null, test_port, null, null, null);
-
-  t.strictEqual(serviceRef.initialized, true,
-      "'initialized' must be true after inititalization");
-
-  dns_sd.DNSServiceRefDeallocate(serviceRef);
-
-  t.strictEqual(serviceRef.initialized, false,
-      "'initialized' must be false after deallocation");
-
-  t.strictEqual(serviceRef.fd, -1,
-      "'fd' must be -1 after deallocation");
-
-  t.throws(function() { dns_sd.DNSServiceRefDeallocate(serviceRef); },
-      "serviceRef is already deallocated");
-
-  t.throws(function() { dns_sd.DNSServiceRefDeallocate(); },
-      "not enough arguments");
-
-  t.throws(function() { dns_sd.DNSServiceRefDeallocate(undefined); },
-      "argument must be DNSServiceRef, not undefined");
-
-  t.throws(function() { dns_sd.DNSServiceRefDeallocate(serviceRef, serviceRef); },
-      "to many arguments");
-
-  t.throws(function() { dns_sd.DNSServiceRefDeallocate({foo: 'bar'}); },
-      "call with non serviceRef object must throw");
-
-  t.done();
-}
-
-//=== DNSServiceResolve =======================================================
-
-exports['DNSServiceResolve()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  t.doesNotThrow(function() {
-    dns_sd.DNSServiceResolve(serviceRef, 0, 0, 'hostname',
-      service_type, 'local.', function() {}, null);
-  }, 'DNSServiceResolve() must not throw');
-
-  t.strictEqual(serviceRef.initialized, true,
-      "'initialized' must be true after inititalization");
-
-  t.throws(function() {
-    dns_sd.DNSServiceResolve(serviceRef, 0, 0, undefined,
-      service_type, 'local.', function() {}, null);
-  }, 'duplicate initialization must throw');
-
-  t.throws(function() {
-    dns_sd.DNSServiceResolve();
-  }, "not enough arguments");
-
-  t.throws(function() {
-    dns_sd.DNSServiceResolve({not_a_service_re: true}, 0, 0, 'hostname',
-      service_type, 'local.', function() {}, null);
-  }, 'serviceRef must be DNSServiceRef object');
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, "", 0, 'hostname',
-      service_type, 'local.', function() {}, null);
-  }, "'flags' must be a number, not a string");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, 0, null, 'hostname',
-      service_type, 'local.', function() {}, null);
-  }, "'interfaceIndex' must be a number, not null");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, 0, 0, null,
-      service_type, 'local.', function() {}, null);
-  }, "'name' must be a string, not null");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, 0, 0, 'hostname', null,
-      'local.', function() {}, null);
-  }, "'regtype' must be a string, not null");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, 0, 0, 'hostname',
-      service_type, null, function() {}, null);
-  }, "'domain' must be a string, not null");
-
-  t.throws(function() {
-    var ref = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceResolve(ref, 0, 0, 'hostname',
-      service_type, 'local.', null, null);
-  }, "'callback' must be a function, not null");
-
-  t.done();
-}
-
-//=== DNSServiceEnumerateDomains ==============================================
-
-exports['DNSServiceEnumerateDomains()'] = function(t) {
-  var serviceRef = new dns_sd.DNSServiceRef();
-
-  t.doesNotThrow( function() {
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, function() {}, null);
-  }, 'DNSServiceEnumerateDomains() must not throw');
-
-  t.notEqual(serviceRef.fd, -1,
-      "'fd' must not be -1 after inititalization");
-  t.strictEqual(serviceRef.initialized, true,
-      "'initialized' must be true after inititalization");
-
-  t.doesNotThrow( function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, function() {}, undefined);
-  }, 'DNSServiceEnumerateDomains() must not throw');
-
-  t.doesNotThrow( function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, function() {}, {some: 'context'});
-  }, 'DNSServiceEnumerateDomains() must not throw');
-
-  t.throws(function() {
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, function() {}, null);
-  }, 'dupliate inititalization of serviceRef must throw');
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      'flags', 0, function() {}, null);
-  }, "'flags' must be a number, not a string");
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      0, 0, function() {}, null);
-  }, "'flags' must be kDNSServiceFlagsBrowseDomains or " +
-     "kDNSServiceFlagsRegistrationDomains");
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 'interfaceIndex', function() {}, null);
-  }, "'interfaceIndex' must be number, not a string");
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, 'function', null);
-  }, "'callback' must be function, not a string");
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains(serviceRef,
-      dns_sd.kDNSServiceFlagsBrowseDomains, 0, null, null);
-  }, "'callback' must be function, not null");
-
-  t.throws(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceEnumerateDomains();
-  }, "not enough arguments");
-
-  t.throws(function() {
-    dns_sd.DNSServiceEnumerateDomains('not a serviceRef',
-      0, 0, function() {}, null);
-  }, "serviceRef must be a DNSServiceRef object");
-
-  t.done();
-}
-
-//=== DNSServiceGetAddrInfo ===================================================
-
-function findInterfaceIndex() {
-  var serviceRef, i;
-  for (var i = 0; i < 100; ++i) {
-    try {
-      serviceRef = new dns_sd.DNSServiceRef();
-      dns_sd.DNSServiceGetAddrInfo(serviceRef, 0, i, 0, 'somehost.local.',
-          function() {}, null);
-      return i;
-
-    } catch (ex) {}
-  }
-}
-
-exports['DNSServiceGetAddrInfo'] = function DNSServiceGetAddrInfo(t) {
-
-  if ( ! dns_sd.DNSServiceGetAddrInfo) {
-    console.log('[SKIPPED] DNSServiceGetAddrInfo() not available');
-    t.done();
-    return;
-  }
-
-  var iface = findInterfaceIndex();
-
-  t.doesNotThrow(function() {
-    var serviceRef = new dns_sd.DNSServiceRef();
-    dns_sd.DNSServiceGetAddrInfo(serviceRef, 0, iface, 0, 'somehost.local.',
-      function() {}, null);
-  }, 'DNSServiceGetAddrInfo() must not throw');
-
-  // TODO add more tests
-
-  t.done();
-}
-
-//=== TXTRecordRef ============================================================
-
-exports['TXTRecordRef'] = function(t) {
-  var txtRecord = new dns_sd.TXTRecordRef()
-    , uninitialized_txt_record = new dns_sd.TXTRecordRef()
-    ;
-  dns_sd.TXTRecordCreate(txtRecord, null);
-
-  var txtRecord = new dns_sd.TXTRecordRef();
-  var buffer = new Buffer(256);
-  dns_sd.TXTRecordCreate(txtRecord, buffer);
-  txtRecord.buffer = buffer;
-
-  dns_sd.TXTRecordSetValue(txtRecord, 'foo', 'bar');
-  t.strictEqual(dns_sd.TXTRecordGetLength(txtRecord), 8,
-      "length must be 8 bytes after adding 'foo=bar'");
-
-  dns_sd.TXTRecordSetValue(txtRecord, 'foobar', 'foobar');
-  t.strictEqual(dns_sd.TXTRecordGetLength(txtRecord), 22,
-      "length must be 22 bytes after adding 'foobar=foobar'");
-
-  dns_sd.TXTRecordSetValue(txtRecord, 'buffer', new Buffer('raw'));
-  t.strictEqual(dns_sd.TXTRecordGetLength(txtRecord), 33,
-      "length must be 33 bytes after adding 'buffer=raw'");
-
-  t.throws(function() { dns_sd.TXTRecordCreate() },
-      'TXTRecordCreate() must throw when called without arguments');
-  t.throws(function() { dns_sd.TXTRecordCreate('narf') },
-      'TXTRecordCreate() must throw when called with a string');
-  t.throws(function() { dns_sd.TXTRecordCreate(txtRecord) },
-      'duplicate call to TXTRecordCreate() must throw');
-  t.throws(function() {
-    var b = new Buffer(256);
-    dns_sd.TXTRecordCreate({not_a_txt_record: true}, b);
-  }, 'txtRecord must be a TXTRecordRef object');
-  t.throws(function() {
-    var b = new Buffer(256);
-    dns_sd.TXTRecordCreate(5, b);
-  }, 'txtRecord must be a TXTRecordRef object');
-  t.doesNotThrow(function() {
-    var tref = new dns_sd.TXTRecordRef();
-    dns_sd.TXTRecordCreate(tref, undefined);
-  },'TXTRecordCreate() with undefined buffer must succeed');
-  t.throws(function() {
-    var tref = new dns_sd.TXTRecordRef();
-    dns_sd.TXTRecordCreate(tref, {not_a_buffer: true});
-  }, 'illegal buffer argument must throw');
-  t.throws(function() {
-    var tref = new dns_sd.TXTRecordRef();
-    dns_sd.TXTRecordCreate(tref, 5);
-  }, 'illegal buffer argument must throw');
-
-
-  t.throws(function() { 
-    dns_sd.TXTRecordDeallocate({not_a_txt_record_ref: true})
-  }, 'illegal argument must throw');
-
-  t.throws(function() { 
-    dns_sd.TXTRecordDeallocate(5)
-  }, 'illegal argument must throw');
-
-  t.throws(function() { dns_sd.TXTRecordSetValue()},
-    'TXTRecordSetValue() must throw when called without arguments');
-  t.throws(function() { dns_sd.TXTRecordSetValue(5, null, null)},
-    'TXTRecordSetValue() must throw when called with non TXTRecordRef object');
-  t.throws(function() { dns_sd.TXTRecordSetValue({not_a_txt_record: true}, null, null)},
-    'TXTRecordSetValue() must throw when called with non TXTRecordRef object');
-  t.throws(function() {
-      dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), {not_a_string: true}, null)
-  }, 'TXTRecordSetValue() must throw when called with non TXTRecordRef object');
-
-  // XXX avahi doesn't like these. replace with real tests when txt records are
-  //     implemented in javascript
-  try {
-    dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'foo', null);
-    t.doesNotThrow(function() {
-        dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'foo', null)
-    }, 'TXTRecordSetValue() must not throw when called with null value');
-  } catch (ex) {
-    console.log("[SKIPPED] avahi doesn't support null values");
-  }
-  try {
-    dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'foo', undefined);
-    t.doesNotThrow(function() {
-        dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'foo', undefined)
-    }, 'TXTRecordSetValue() must not throw when called with undefined value');
-  } catch (ex) {
-    console.log("[SKIPPED] avahi doesn't support undefined values");
-  }
-  t.throws(function() {
-      dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 5, undefined)
-  }, 'TXTRecordSetValue() must throw when called with non string key');
-  t.throws(function() {
-      dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'foo', 5)
-  }, 'TXTRecordSetValue() must throw when called with strange value');
-  t.throws(function() {
-      dns_sd.TXTRecordSetValue(new dns_sd.TXTRecordRef(), 'illeagal=key', 'bar')
-  }, 'TXTRecordSetValue() must throw when called with strange value');
-
-  t.throws(function() { dns_sd.TXTRecordGetLength() },
-      'not enough arguments must throw');
-  t.throws(function() { dns_sd.TXTRecordGetLength(5) },
-      'illegal arguments must throw');
-  t.throws(function() { dns_sd.TXTRecordGetLength({not_a_buffer: true}) },
-      'illegal arguments must throw');
-
-  t.doesNotThrow(function() { dns_sd.TXTRecordDeallocate( txtRecord ); },
-      'deallocating a txtRecord must not throw');
-  t.throws(function() { dns_sd.TXTRecordDeallocate(); },
-      'TXTRecordDeallocate() must throw when called without arguments');
-  t.throws(function() { dns_sd.TXTRecordDeallocate(null, null); },
-      'TXTRecordDeallocate() must throw when called with more than one argument');
-
-  t.throws(function() { dns_sd.txtRecordBufferToObject(); },
-      'txtRecordBufferToObject() must throw when called with no arguments');
-
-  t.throws(function() { dns_sd.txtRecordBufferToObject(5); },
-      'txtRecordBufferToObject() must throw when called with a non-object');
-
-  t.throws(function() { dns_sd.txtRecordBufferToObject({not_a_txt_record_ref: true}); },
-      'txtRecordBufferToObject() must throw when called with strange objects');
-
-  t.done();
-}
-
-//=== buildException ==========================================================
-
-exports['buildException()'] = function(t) {
-  t.strictEqual(dns_sd.buildException(dns_sd.kDNSServiceErr_NoError), undefined,
-      'buildException(kDNSServiceErr_NoError) must return undefined');
-
-  var ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Unknown);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Unknwon) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Unknown);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NoSuchName);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NoSuchName) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NoSuchName);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NoMemory);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NoMemory) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NoMemory);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadParam);
-  t.ok(ex instanceof Error,
-      'buildException() must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadParam);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadReference);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_BadReference) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadReference);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadState);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_BadState) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadState);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadFlags);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_BadFlags) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadFlags);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Unsupported);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Unsupported) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Unsupported);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NotInitialized);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NotInitialized) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NotInitialized);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_AlreadyRegistered);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_AlreadyRegistered) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_AlreadyRegistered);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NameConflict);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NameConflict) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NameConflict);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Invalid);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Invalid) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Invalid);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Firewall);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Firewall) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Firewall);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Incompatible);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Incompatible) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Incompatible);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadInterfaceIndex);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_BadInterfaceIndex) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadInterfaceIndex);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_Refused);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_Refused) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_Refused);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NoSuchRecord);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NoSuchRecord) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NoSuchRecord);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NoAuth);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NoAuth) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NoAuth);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NoSuchKey);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NoSuchKey) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NoSuchKey);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_NATTraversal);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_NATTraversal) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_NATTraversal);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_DoubleNAT);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_DoubleNAT) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_DoubleNAT);
-
-  ex = dns_sd.buildException(dns_sd.kDNSServiceErr_BadTime);
-  t.ok(ex instanceof Error,
-      'buildException(kDNSServiceErr_BadTime) must return an Error object');
-  t.strictEqual( ex.errorCode, dns_sd.kDNSServiceErr_BadTime);
-
-  t.throws(function() { dns_sd.buildException() },
-    'not enough arguments');
-  t.throws(function() { dns_sd.buildException('foobar') },
-    'argument must be a string');
-  t.done();
-}
-
-exports['exportConstants()'] = function(t) {
-  var o = {}
-  dns_sd.exportConstants(o);
-  t.ok(o.kDNSServiceErr_BadParam);
-
-  t.throws(function() { dns_sd.exportConstants() });
-  t.throws(function() { dns_sd.exportConstants(5) });
-
-  t.done();
-}
-
-// vim: filetype=javascript:
diff --git a/lib/jdiscovery/mdns/tests/test_functional.js b/lib/jdiscovery/mdns/tests/test_functional.js
deleted file mode 100755
index 49accf54..00000000
--- a/lib/jdiscovery/mdns/tests/test_functional.js
+++ /dev/null
@@ -1,501 +0,0 @@
-#!/usr/bin/env node
-
-var mdns_test = require('../utils/lib/mdns_test')
-  , mdns      = mdns_test.require('mdns')
-  , nif       = require('../lib/network_interface.js')
-  , os        = require('os')
-  ;
-
-exports['simple browsing'] = function(t) {
-  var timeout = 5000
-    , port = 4321
-    , service_type = mdns_test.suffixedServiceType('node-mdns', 'tcp');
-    ;
-
-  var timeoutId = setTimeout(function() {
-    t.fail("test did not finish within " + (timeout / 1000) + " seconds.");
-    t.done();
-  }, timeout)
-
-  var browser = mdns.createBrowser(service_type, {context: {some: 'context'}});
-
-  function stopBrowserIfDone() {
-    if (upCount > 0 &&
-        downCount > 0 && 
-        upCount == downCount && 
-        changedCount == upCount + downCount)
-    {
-      browser.stop();
-      clearTimeout(timeoutId);
-      t.done();
-    }
-  }
-
-  var changedCount = 0;
-  browser.on('serviceChanged', function(service, ctx) {
-    //console.log("changed:", service);
-    t.strictEqual(typeof service.flags, 'number',
-        "'flags' must be a number");
-    if (changedCount === 0) {
-      t.ok(service.flags & mdns.kDNSServiceFlagsAdd,
-          "'flags' must have kDNSServiceFlagsAdd set");
-      t.strictEqual(typeof service.fullname, 'string',
-          "'fullname' must be a string");
-      t.strictEqual(typeof service.host, 'string',
-          "'host' must be a string");
-      t.strictEqual(typeof service.port, 'number',
-          "'port' must be a number");
-      t.strictEqual(service.port, port,
-          "'port' must match the advertisement");
-
-      t.ok('addresses' in service, 
-          "'service' must have a address property");
-      t.ok(Array.isArray(service.addresses),
-          "'addresses' must be an array");
-      t.ok(service.addresses.length > 0,
-          "addresses must not be empty");
-    }
-    t.strictEqual(typeof service.interfaceIndex, 'number',
-        "'interfaceIndex' must be a number");
-    t.strictEqual(typeof service.name, 'string',
-        "'name' must be a string");
-    t.ok(service.type instanceof mdns.ServiceType,
-        "'type' must be a service type object");
-    t.strictEqual('' + service.type, service_type + '.',
-        "type must match the target type");
-    t.strictEqual(typeof service.replyDomain, 'string',
-        "'replyDomain' must be a string");
-    t.strictEqual(service.replyDomain, 'local.',
-        "'replyDomain' must match 'local.'");
-
-    t.strictEqual(typeof service.networkInterface, 'string',
-        'must have a networkInterface');
-
-    t.ok(ctx, 'must have context');
-    t.strictEqual(ctx.some, 'context', 'property must match input');
-
-    changedCount += 1;
-    stopBrowserIfDone();
-  });
-
-  var upCount = 0;
-  browser.on('serviceUp', function(service, ctx) {
-    //console.log("up:", service);
-    t.strictEqual(typeof service.flags, 'number',
-        "'flags' must be a number");
-    t.strictEqual(typeof service.interfaceIndex, 'number',
-        "'interfaceIndex' must be a number");
-    t.strictEqual(typeof service.name, 'string',
-        "'name' must be a string");
-    t.ok(service.type instanceof mdns.ServiceType,
-        "'type' must be ServiceType object");
-    t.strictEqual('' + service.type, service_type + '.',
-        "'type' must match target type");
-    t.strictEqual(typeof service.replyDomain, 'string',
-        "'replyDomain' must be a string");
-    t.strictEqual(service.replyDomain, 'local.',
-        "'replyDomain' must match 'local.'");
-
-    t.strictEqual(typeof service.fullname, 'string',
-        "'fullname' must be a string");
-    t.strictEqual(typeof service.host, 'string',
-        "'host' must be a string");
-    t.strictEqual(typeof service.port, 'number',
-        "'port' must be a number");
-    t.strictEqual(service.port, port,
-        "'port' must match");
-
-    t.ok('addresses' in service,
-        "'service' must have a addresses property");
-    t.ok(Array.isArray(service.addresses),
-        "'addresses' must be a string");
-    t.ok(service.addresses.length > 0,
-        "'addresses' must not be empty");
-
-    t.ok('rawTxtRecord' in service,
-        "'service' must have a rawTxtRecord property");
-    t.ok(service.rawTxtRecord,
-        "'rawTxtRecord' must be truthy");
-    t.ok(service.txtRecord,
-        "'txtRecord' must be truthy");
-
-    t.strictEqual(typeof service.networkInterface, 'string',
-        'must have a networkInterface');
-
-    var p;
-    for (p in txt_record) {
-      t.strictEqual('' + txt_record[p], service.txtRecord[p],
-          "property " + p + " in txtRecord must match");
-    }
-    
-    t.ok(ctx, 'must have context');
-    t.strictEqual(ctx.some, 'context', 'property must match input');
-
-    upCount += 1;
-    stopBrowserIfDone();
-  });
-
-  var downCount = 0;
-  browser.on('serviceDown', function(service, ctx) {
-    t.strictEqual(typeof service.flags, 'number',
-        "'flags' must be a number");
-    t.strictEqual(typeof service.interfaceIndex, 'number',
-        "'interfaceIndex' must be a number");
-    t.strictEqual(typeof service.name, 'string',
-        "'name' must be a string");
-    t.ok(service.type instanceof mdns.ServiceType,
-        "'type' must be a ServiceType object");
-    t.strictEqual('' + service.type, service_type + '.',
-        "'type' must match target aervice type");
-    t.strictEqual(typeof service.replyDomain, 'string',
-        "'replyDomain' must be a string");
-    t.strictEqual(service.replyDomain, 'local.',
-        "'replyDomain' must match 'local.'");
-
-    t.strictEqual(typeof service.networkInterface, 'string',
-        'must have a networkInterface');
-
-    t.ok(ctx, 'must have context');
-    t.strictEqual(ctx.some, 'context', 'property must match input');
-
-    downCount += 1;
-    stopBrowserIfDone();
-  });
-
-  browser.start();
-
-  var txt_record = {type: 'bacon', chunky: true, strips: 5, buffer: new Buffer('raw')}
-    , ad = mdns.createAdvertisement(service_type, port,
-        {txtRecord: txt_record}, function(err, service, flags) {
-          if (err) throw err;
-          setTimeout(function() { ad.stop() }, 500);
-        });
-
-  ad.start();
-}
-
-exports['create ads'] = function(t) {
-  var timeout = 500 // ms
-    , counter = 0
-    ;
-
-  function stopIfDone() {
-    if (++counter === 4) {
-      t.done();
-    }
-  }
-  var ad1 = mdns.createAdvertisement(['mdns-test1', 'tcp'], 4321);
-  ad1.start();
-  setTimeout(function() { ad1.stop(); stopIfDone(); }, timeout);
-
-  var ad2 = mdns.createAdvertisement(['mdns-test2', 'tcp'], 4322, {});
-  ad2.start();
-  setTimeout(function() { ad2.stop(); stopIfDone(); }, timeout);
-
-  function checkAd(service, name, proto) {
-    t.ok('flags' in service,
-        "service must have a flags property");
-    t.strictEqual(typeof service.flags, 'number',
-        "'flags' must be a number");
-    t.ok(service.type instanceof mdns.ServiceType,
-        "'type' must be a ServiceType object");
-    t.strictEqual(service.type.toString(), '_' + name + '._' + proto + '.',
-        "'type' must be as advertised");
-  }
-
-  var ad3 = mdns.createAdvertisement(['mdns-test3', 'tcp'], 4323, {name: 'foobar'}, function(error, service) {
-    if (error) t.fail(error);
-    
-    checkAd(service, 'mdns-test3', 'tcp');
-
-    var ad = this;
-    setTimeout(function(){ ad.stop(); stopIfDone(); }, timeout);
-  });
-  ad3.start();
-
-  var ad4 = mdns.createAdvertisement(['mdns-test4', 'udp'], 4324, {}, function(error, service) {
-    if (error) t.fail(error);
-
-    checkAd(service, 'mdns-test4', 'udp');
-
-    var ad = this;
-    setTimeout(function(){ ad.stop(); stopIfDone(); }, timeout);
-  });
-  ad4.start();
-
-}
-
-exports['update ad record'] = function(t) {
-  var timeout = 10000
-    , port = 4321
-    , service_type = mdns_test.suffixedServiceType('test-adu', 'tcp')
-    , registered = false
-    , updated = false
-    ;
-
-  var timeoutId = setTimeout(function() {
-    t.fail("test did not finish within " + (timeout / 1000) + " seconds.");
-    browser.stop();
-    browser2.stop();
-    ad.stop();
-    t.done();
-  }, timeout)
-
-  var browser = mdns.createBrowser(service_type, {context: {some: 'context'}});
-  var browser2 = mdns.createBrowser(service_type, {context: {some: 'context'}});
-
-  function stopBrowserIfDone() {
-    if (changedCount == 2 && updated)
-    {
-      browser.stop();
-      browser2.stop();
-      ad.stop();
-      clearTimeout(timeoutId);
-      t.done();
-    }
-  }
-
-  function browserOnServiceChanged(service, ctx) {
-    t.ok('rawTxtRecord' in service,
-        "'service' must have a rawTxtRecord property");
-    t.ok(service.rawTxtRecord,
-        "'rawTxtRecord' must be truthy");
-    t.ok(service.txtRecord,
-        "'txtRecord' must be truthy");
-
-    if (changedCount === 0) {
-      t.strictEqual(service.txtRecord['value'], '1',
-          "'txtRecord' doesn\'t match");
-      changedCount += 1;
-      browser.stop();
-    } else {
-      if(updated) {
-        changedCount += 1;
-        t.strictEqual(service.txtRecord['value'], '2',
-          "'txtRecord' doesn\'t match");
-      }
-    }
-
-    stopBrowserIfDone();
-  }
-
-  var changedCount = 0;
-  browser.on('serviceChanged', browserOnServiceChanged);
-  browser2.on('serviceChanged', browserOnServiceChanged);
-
-  browser.start();
-
-  var ad = mdns.createAdvertisement(service_type, port, {name: 'foobar', txtRecord: {value: '1'}}, function(error, service) {
-    if (error) t.fail(error);
-    if (registered === false) {
-      registered = true;
-      setTimeout(function(){
-        if (ad.serviceRef !== null) {
-          ad.updateTXTRecord({value: '2'});
-          updated = true;
-          setTimeout(function(){browser2.start();}, 3000);
-        }
-      }, 1000);
-    }
-  });
-  ad.start();
-}
-
-exports['browseThemAll()'] = function(t) {
-  var browser = new mdns.browseThemAll()
-    , up = 0
-    , down = 0
-    , changed = 0
-    , type = mdns_test.suffixedServiceType('node-mdns', 'udp')
-    ;
-
-  // XXX
-  console.log('[SKIPPED] write better test for browseThemAll()');
-  t.done();
-  return; // XXX
-
-  browser.on('serviceUp', function(service) {
-    if (type.matches(service.type)) {
-      ++up;
-    }
-  });
-  browser.on('serviceDown', function(service) {
-    if (type.matches(service.type)) {
-      ++down;
-    }
-  });
-  browser.on('serviceChanged', function(service) {
-    if (type.matches(service.type)) {
-      ++changed;
-    }
-  });
-
-  // it takes forever until the service type disappears
-  var cooltime = 15000;
-  var timeoutId = setTimeout(cooltime + 5000, function() {
-    t.ok(false, "test did not finish");
-    t.done();
-  });
-  var ad = mdns_test.runTestAd(type, 1337, 2000, cooltime, function() {
-    //t.strictEqual(down, up, 'up count must match down count');
-    //t.strictEqual(down + up, changed, 'up plus down must equal changed count');
-    console.log('[SKIPPED] write better test for browseThemAll()')
-    browser.stop();
-    clearTimeout(timeoutId);
-    t.done();
-  });
-
-  browser.start();
-}
-
-exports['resolver sequence'] = function(t) {
-  var type = mdns_test.suffixedServiceType('node-mdns', 'tcp')
-    , browser = new mdns.createBrowser(type, {resolverSequence: []})
-    , rst = mdns.rst
-    ;
-
-  browser.on('serviceUp', function(service) {
-    t.ok( ! ('host' in service),
-        "service must not have a 'host' property");
-    t.ok( ! ('port' in service),
-        "service must not have a 'port' property");
-    t.ok( ! ('fullname' in service),
-        "service must not have a 'fullname' property");
-    t.ok( ! ('addresses' in service),
-        "service must not have an 'addresses' property");
-
-    var result_count = 0;
-    function done() {
-      if (++result_count === 3) {
-        t.done();
-      }
-    }
-    var s1 = clone(service);
-    mdns.resolve(s1, function(error, service) {
-      //console.log('default resolve:', error, service);
-      if (error) throw error;
-      t.ok('host' in service,
-        "service must have a 'host' property");
-      t.ok('port' in service,
-        "service must have a 'port' property");
-      t.ok('fullname' in service,
-        "service must have a 'fullname' property");
-      t.ok('addresses' in service,
-        "service must have an 'addresses' property");
-      done();
-    });
-
-    var s2 = clone(service);
-    var seq2 = [rst.DNSServiceResolve(), rst.getaddrinfo()];
-    mdns.resolve(s2, seq2, function(error, service) {
-      //console.log('getaddrinfo resolve:', error, service);
-      if (error) throw error;
-      t.ok('host' in service,
-        "service must have a 'host' property");
-      t.ok('port' in service,
-        "service must have a 'port' property");
-      t.ok('fullname' in service,
-        "service must have a 'fullname' property");
-      t.ok('addresses' in service,
-        "service must have an 'addresses' property");
-      done();
-    });
-
-    var s3 = clone(service);
-    var seq3 = [rst.DNSServiceResolve()];
-    mdns.resolve(s3, seq3, function(error, service) {
-      //console.log('resolve (no addresses):', error, service);
-      if (error) throw error;
-      t.ok('host' in service,
-        "service must have a 'host' property");
-      t.ok('port' in service,
-        "service must have a 'port' property");
-      t.ok('fullname' in service,
-        "service must have a 'fullname' property");
-      t.ok( ! ('addresses' in service),
-        "service must not have an 'addresses' property");
-      done();
-    });
-
-    browser.stop();
-  });
-
-  var ad = mdns_test.runTestAd(type, 1337, 1000, function() {});
-
-  browser.start();
-
-  function clone(o) {
-    var clone = {}, p;
-    for (p in o) {
-      clone[p] = o[p];
-    }
-    return clone;
-  }
-}
-
-exports['local advertisement invisible on external interfaces'] = function(t) {
-  var st = mdns.tcp('local-ad')
-    , exif = someExternalInterface()
-    , avahi = require('../lib/avahi')
-    ;
-  if (avahi) {
-    console.log('[SKIPPED] avahi does not support local only operation');
-    t.done();
-    return;
-  }
-  if ( ! exif) {
-    console.log('[SKIPPED] failed to find an external network interface');
-    t.done();
-    return;
-  }
-  var ad = mdns.createAdvertisement(st, 4321, 
-      {networkInterface: mdns.loopbackInterface()})
-    , externalBrowser = mdns.createBrowser(st, {networkInterface: exif})
-    , externalBrowserEvents = 0
-    ;
-  externalBrowser.on('serviceUp', function(service) {
-    externalBrowserEvents++;
-  });
-  externalBrowser.on('serviceDown', function(service) {
-    externalBrowserEvents++;
-  });
-  var loopbackBrowser = mdns.createBrowser(st,
-      {networkInterface: mdns.loopbackInterface()});
-  loopbackBrowser.on('serviceUp', function(service) {
-    t.strictEqual(service.interfaceIndex, mdns.loopbackInterface(),
-        'service must have the loopback interface index');
-    t.strictEqual(service.networkInterface, nif.loopbackName(),
-        'service must have the loopback interface name');
-    setTimeout(function() { ad.stop() }, 1000);
-  });
-  loopbackBrowser.on('serviceDown', function(service) {
-    t.strictEqual(service.interfaceIndex, mdns.loopbackInterface(),
-        'service must have the loopback interface index');
-    t.strictEqual(service.networkInterface, nif.loopbackName(),
-        'service must have the loopback interface name');
-    setTimeout(function() {
-      t.strictEqual(externalBrowserEvents, 0, 
-        'browser on external interface must not receive events');
-      externalBrowser.stop();
-      loopbackBrowser.stop();
-      t.done();
-    }, 1000);
-  });
-  externalBrowser.start();
-  loopbackBrowser.start();
-  ad.start();
-}
-
-function someExternalInterface() {
-  var ifaces = os.networkInterfaces()
-    , loopback = nif.loopbackName()
-    ;
-  for (var name in ifaces) {
-    if (name !== loopback) {
-      return name;
-    }
-  }
-  throw new Error('failed to find an external network interface');
-}
-
-// vim: filetype=javascript :
diff --git a/lib/jdiscovery/mdns/tests/test_mdns_service.js b/lib/jdiscovery/mdns/tests/test_mdns_service.js
deleted file mode 100644
index 746a5c67..00000000
--- a/lib/jdiscovery/mdns/tests/test_mdns_service.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var dns_sd = require('../lib/dns_sd')
-  , proxyquire =  require('proxyquire')
-  ;
-
-//=== MDNSService ===========================================================
-
-module.exports["MDNSService"] = {
-  "DNS errors should propagate": function(test) {
-    var message = "Panic!"
-    var mock_dns_sd = {
-      DNSServiceProcessResult: function() {
-        throw new Error(message);
-      }
-    };
-
-    var MDNSService = proxyquire("../lib/mdns_service.js", { "./dns_sd": mock_dns_sd }).MDNSService;
-
-    var service = new MDNSService();
-    service.on("error", function(error) {
-      test.equal(message, error.message);
-
-      test.done();
-    });
-    service.watcher.start = function() {
-      process.nextTick(this.callback.bind(null));
-    }
-    service.start();
-  }
-}
diff --git a/lib/jdiscovery/mdns/tests/test_network_interface.js b/lib/jdiscovery/mdns/tests/test_network_interface.js
deleted file mode 100644
index 5228fc17..00000000
--- a/lib/jdiscovery/mdns/tests/test_network_interface.js
+++ /dev/null
@@ -1,55 +0,0 @@
-var os = require('os')
-  , mdns_test = require('../utils/lib/mdns_test')
-  , dns_sd = require('../lib/dns_sd.js')
-  , nif = require('../lib/network_interface.js');
-  ;
-
-function loopback() {
-  var interfaces = os.networkInterfaces();
-  for (var name in interfaces) {
-    for (var i = 0; i < interfaces[name].length; ++i) {
-      if (interfaces[name][i].address === '127.0.0.1') {
-        return name;
-      }
-    }
-  }
-  throw new Error('failed to find loopback interface');
-}
-
-var have_if_nametoindex = typeof dns_sd.if_nametoindex !== 'undefined';
-
-exports['if_nametoindex <-> if_indextoname'] = function(t) {
-  if (have_if_nametoindex) {
-    var interfaces = os.networkInterfaces();
-    for (var name in interfaces) {
-      var index = dns_sd.if_nametoindex(name);
-      t.ok( index > 0);
-      t.strictEqual( dns_sd.if_indextoname(index), name);
-    }
-  } else {
-    console.log('[SKIPPED] if_nametoindex() not supported on this platform');
-  }
-  t.done();
-}
-
-exports['interfaceIndex'] = function(t) {
-  t.strictEqual(nif.interfaceIndex({interfaceIndex: 0}), 0);
-  t.strictEqual(nif.interfaceIndex({interfaceIndex: 1}), 1);
-
-  t.strictEqual(nif.interfaceIndex({networkInterface: 0}), 0);
-
-  t.strictEqual(nif.interfaceIndex({}), 0);
-
-  if (have_if_nametoindex) {
-    t.ok(nif.interfaceIndex({networkInterface: loopback()}) > 0);
-    t.ok(nif.interfaceIndex({networkInterface: '127.0.0.1'}) > 0);
-
-    t.strictEqual(nif.interfaceIndex({networkInterface: loopback()}),
-        nif.interfaceIndex({networkInterface: '127.0.0.1'}));
-  } else {
-    console.log('[SKIPPED] if_nametoindex() not supported on this platform');
-  }
-
-
-  t.done();
-}
diff --git a/lib/jdiscovery/mdns/tests/test_odd_ends.js b/lib/jdiscovery/mdns/tests/test_odd_ends.js
deleted file mode 100644
index b8e3e784..00000000
--- a/lib/jdiscovery/mdns/tests/test_odd_ends.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var mdns_test = require('../utils/lib/mdns_test')
-  , mdns      = mdns_test.require('mdns')
-  ;
-
-//=== DNSServiceRef ===========================================================
-
-exports['DNSServiceEnumerateDomains'] = function(t) {
-  var enumerator = new mdns.MDNSService();
-  var timeout = setTimeout(function() {
-    console.log('[SKIPPED] probably running on ahavi');
-    enumerator.stop();
-    t.done()
-  }, 10000);
-  function on_enum(sref, flags, iface, error, domain, context) {
-    t.strictEqual(error, mdns.kDNSServiceErr_NoError, "error must be NoError");
-    t.strictEqual(typeof flags, 'number', "'flags' must be of type number");
-    t.strictEqual(typeof iface, 'number', "'iface' must be of type number");
-
-    enumerator.stop();
-    clearTimeout(timeout);
-    t.done();
-  }
-  mdns.dns_sd.DNSServiceEnumerateDomains(enumerator.serviceRef,
-      mdns.kDNSServiceFlagsBrowseDomains, 0, on_enum, {some: 'context'});
-  enumerator.start();
-}
-
diff --git a/lib/jdiscovery/mdns/tests/test_service_type.js b/lib/jdiscovery/mdns/tests/test_service_type.js
deleted file mode 100644
index 69e57152..00000000
--- a/lib/jdiscovery/mdns/tests/test_service_type.js
+++ /dev/null
@@ -1,101 +0,0 @@
-var mdns_test = require('../utils/lib/mdns_test')
-  , mdns      = mdns_test.require('mdns')
-  ;
-
-exports['protocol helpers'] = function(t) {
-  t.strictEqual( mdns.udp('osc').toString(), '_osc._udp',
-      'comparing simple udp service type');
-
-  t.strictEqual( mdns.tcp('http', 'blogapi').toString(), '_http._tcp,_blogapi',
-      'comparing tcp service type with subtype');
-  t.strictEqual( mdns.tcp('http', 'blogapi', 'newblogapi').toString(),
-      '_http._tcp,_blogapi,_newblogapi',
-      'comparing tcp service types with two subtypes');
-
-  t.done();
-}
-
-function compareServiceType() {
-  var args = Array.prototype.slice.call(arguments)
-    , t = args.shift()
-    , msg = args.pop()
-    , str = args.pop()
-    , type = mdns.makeServiceType.apply(this, args)
-    ;
-  t.strictEqual(type.toString(), str, msg);
-}
-
-var http_type = '_http._tcp,_foo,_bar';
-
-exports['makeServiceType()'] = function(t) {
-
-  compareServiceType(t, http_type, http_type,
-      'construct from string form');
-  compareServiceType(t, 'http', 'tcp', 'foo', 'bar', http_type,
-      'construct from tokens');
-  compareServiceType(t, '_http', '_tcp', '_foo', '_bar', http_type,
-      'construct from tokens with underscores');
-  compareServiceType(t, ['http', 'tcp', 'foo', 'bar'], http_type,
-      'construct from array');
-  compareServiceType(t, mdns.tcp('http', 'foo', 'bar'), http_type,
-      'construct with protocol helper');
-  compareServiceType(t,
-    { name: 'http'
-    , protocol: 'tcp'
-    , subtypes: ['foo', 'bar']
-    }
-    , http_type,
-    'construct from object (json)');
-
-  t.done();
-}
-
-
-exports['json round-trip'] = function(t) {
-  compareServiceType(t, mdns.makeServiceType(
-        JSON.parse(JSON.stringify(mdns.makeServiceType(http_type))))
-      , http_type,
-      'construct from result of JSON.parse(JSON.stringify(...))');
-
-  t.done();
-}
-
-exports['ServiceType() constructor'] = function(t) {
-  compareServiceType(t, new mdns.ServiceType('http', 'tcp', 'foo', 'bar'),  http_type,
-      'construct from tokens');
-  compareServiceType(t, new mdns.ServiceType(['http', 'tcp', 'foo', 'bar']), http_type,
-      'construct from array');
-  compareServiceType(t, new mdns.ServiceType({name: 'http', protocol: 'tcp', subtypes: ['foo', 'bar']}), http_type,
-      'construct from object');
-
-  t.done();
-}
-
-exports['illegal arguments'] = function(t) {
-  t.throws(function() { mdns.tcp('abcdefghijklmnopq') },
-      'service type to long');
-  t.throws(function() { mdns.tcp('abc%') },
-      'illegal characters in primary type');
-  t.throws(function() { mdns.tcp('abc', 'abcdefghijklmnopq') },
-      'subtype to long');
-  t.throws(function() { mdns.tcp('abc', '%$@') },
-      'illegal characters in subtype');
-  t.throws(function() { mdns.tcp('abc', 'def', 'foo_bar') },
-      'illegal chars in subtype');
-
-  t.throws(function() { mdns.tcp('abc', 'tcp', 'foo') },
-      'type token already present');
-
-  t.throws(function() { mdns.makeServiceType({}) },
-      'missing properties');
-  t.throws(function() { mdns.makeServiceType({name: 'foo', protocol: 'bar'}) },
-      'illegal protocol');
-  t.throws(function() { mdns.makeServiceType(5) },
-      'attempt to construct from number');
-  t.throws(function() { mdns.tcp('') },
-      'aatempt to construct with an empty string');
-
-  t.done();
-}
-
-// vim: filetype=javascript:
diff --git a/lib/jdiscovery/mdns/utils/coverage b/lib/jdiscovery/mdns/utils/coverage
deleted file mode 100755
index fc4badf1..00000000
--- a/lib/jdiscovery/mdns/utils/coverage
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env node
-
-var input = '';
-
-process.stdin.on('data', function(chunk) {
-  input += chunk;
-})
-
-function p() { return Array.prototype.join.call(arguments, '.') }
-
-process.stdin.on('end', function() {
-  input.split('\n').forEach(function(l) {
-    var match = l.match(/^\s+([^.]+)\.+: ([0-9.]+)% \(([0-9]+) of ([0-9]+) .*$/);
-    if (match) {  
-      var what    = match[1]
-        , percent = match[2]
-        , covered = match[3]
-        , total   = match[4]
-        ;
-      console.log(p(what, 'percent', 'covered'), '=', percent);
-      console.log(p(what, 'percent', 'missed'), '=', 100 - percent);
-      console.log(p(what, 'covered'), '=', covered);
-      console.log(p(what, 'missed'), '=', total - covered);
-      console.log(p(what, 'total'), '=', total);
-    }
-  })
-});
-
-process.stdin.resume();
diff --git a/lib/jdiscovery/mdns/utils/docpack b/lib/jdiscovery/mdns/utils/docpack
deleted file mode 100755
index 572d8000..00000000
--- a/lib/jdiscovery/mdns/utils/docpack
+++ /dev/null
@@ -1,129 +0,0 @@
-#!/usr/bin/env node
-
-// docpack - documentation for take-away
-// disgusting ... but works for now ...
-
-var path   = require('path')
-  , fs     = require('fs')
-  , child_process = require('child_process')
-  , slide  = require('slide')
-  , mkdirp = require('mkdirp')
-  , glob   = require('glob')
-  , ejs    = require('ejs')
-  , less   = require('less')
-  , ncp    = require('ncp').ncp
-  , view   = require('./lib/view')
-  , obj = require('./lib/obj')
-  , j      = path.join
-  , chain  = slide.chain
-  , compiler = require('./lib/compiler')
-  ;
-
-var rootd = path.resolve(path.dirname(process.argv[1]), '..')
-  , docd  = rootd + '/doc'
-  , outd  = rootd + '/build/pages'
-  , package = JSON.parse(fs.readFileSync(rootd + '/package.json'))
-  , layout = fs.readFileSync(docd + '/layout.ejs').toString()
-  ;
-
-var ejs2textile = compiler.compilerSync(ejs.render, 'ejs', {package: package})
-  , ejs2html    = compiler.compiler(render_page, 'redcloth', {dst: compiler.dst, outputDir: outd})
-  , less2css    = compiler.compiler(less.render, 'less')
-  ;
-
-var prepare = [ [ mkdirp, outd ]
-              ]
-  , tasks   = [ [ ejs2textile, docd + '/README.ejs', rootd + '/README.textile']
-              , [ render_pages, docd + '/pages/*.ejs']
-              , [ render_build_status_pages ]
-              , [ render_stylesheets, docd + '/pages/stylesheets/*.less']
-              , [ ncp, docd + '/pages/images', outd + '/images']
-              , [ ncp, docd + '/pages/scripts', outd + '/scripts']
-              , [ ncp, docd + '/pages/jqplot', outd + '/jqplot']
-              ]
-              ;
-
-chain( [ [ run_all, prepare]
-       , [ run_all, tasks]
-       ]
-     , function(error) { if (error) console.log(error) }
-     );
-
-
-
-function render_pages(pattern, cb) {
-  glob(pattern, function(error, files) {
-    var tasks = files.map(function(f) { 
-      var dst = path.join(outd, path.basename(path.basename(f), '.ejs'))
-          + '.html';
-      return [ejs2html, f, dst] 
-    });
-    run_all(tasks, cb);
-  });
-}
-
-function render_stylesheets(pattern, cb) {
-  glob(pattern, function(error, files) {
-    var tasks = files.map(function(f) { 
-      var dst = outd + '/stylesheets/'
-          + path.basename(path.basename(f), '.less') + '.css';
-      return [less2css, f, dst, {}] 
-    });
-    run_all(tasks, cb);
-  });
-}
-
-function render_page(source, options, cb) {
-  var metadata = { scripts: [], stylesheets: ['/stylesheets/mdns.css']};
-  function layout_page(error, html) {
-    metadata.body = html;
-    cb(error, ejs.render(layout, obj.union(metadata, view.helpers(metadata, options))))
-  }
-  var locals = obj.union({package: package}, view.helpers(metadata, options));
-  piped_comand('redcloth', ejs.render(source, locals), layout_page);
-}
-
-function render_build_status_pages(cb) {
-  cb()
-}
-
-function piped_comand(comand, input, args, cb) {
-  if ( ! cb ) {
-    cb = args;
-    args = undefined;
-  }
-  var cmd = child_process.spawn(comand, args)
-    , out = ''
-    ;
-  cmd.stdout.on('data', function(chunk) { out += chunk })
-  cmd.stderr.on('data', function(chunk) { console.log(chunk)})
-  cmd.on('exit', function(code) {
-    if (code) {
-      cb(new Error("command '" + command + "' exited with code " + code))
-    } else {
-      cb(null, out)
-    }
-  });
-  cmd.stdin.write(input);
-  cmd.stdin.destroySoon();
-  
-}
-
-
-function run_all(tasks, cb) {
-  var done_count = 0, first_error;
-  function on_done(error) {
-    if (error) {
-      first_error = error;
-      cb(error);
-    }
-    if ( ! first_error && ++done_count === tasks.length) cb();
-  }
-  tasks.forEach(function(t) {
-    var f = t.shift();
-    t.push(on_done)
-    f.apply(null, t);
-  });
-}
-
-// vim: filetype=javascript :
diff --git a/lib/jdiscovery/mdns/utils/jsf b/lib/jdiscovery/mdns/utils/jsf
deleted file mode 100755
index 23088267..00000000
--- a/lib/jdiscovery/mdns/utils/jsf
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env node
-
-var path   = require('path')
-  , fs     = require('fs')
-  , slide  = require('slide')
-  , mkdirp = require('mkdirp')
-  , chain  = slide.chain
-
-  , outd = process.argv.pop()
-  , build_type = process.env.BUILDTYPE
-  ;
-
-//console.log(process.env);
-
-function copy(f, cb) {
-  var target = path.join(outd, path.basename(f))
-    , readStream = fs.createReadStream(f)
-    ;
-  console.log('  COPY', target);
-  readStream.pipe(fs.createWriteStream(target));
-  readStream.once('end', cb);
-}
-
-function copyFiles(files, cb) {
-  slide.asyncMap(files, copy, cb);
-}
-
-function jscoverage(files, cb) {
-  var just_do_it_kthxbye = path.join(outd, 'jscoverage.html')
-  chain( [ [ fs.writeFile, just_do_it_kthxbye, '' ]
-         , [ run_jscoverage, files]
-         , [ fs.unlink, just_do_it_kthxbye]
-         ]
-         , cb
-       )
-}
-
-function run_jscoverage(files, cb) {
-  var spawn = require('child_process').spawn
-    , cmd = process.env.JSCOVERAGE || 'jscoverage'
-    , src_dir = path.dirname(files[0])
-    , jsc = spawn(cmd, [src_dir, outd, '-v', '--no-highlight'])
-    ;
-    function log(d) { console.log(d.toString()) }
-    jsc.stdout.on('data', log);
-    jsc.stderr.on('data', log);
-    jsc.on('exit', function(code) {
-      if (code) {
-        cb(new Error('jscoverage exited with code ' + code))
-      } else {
-        cb();
-      }
-    })
-}
-
-var op = build_type === 'Coverage' ? jscoverage : copyFiles
-
-chain( [ [ mkdirp, outd ]
-       , [ op, process.argv.slice(2) ]
-       ]
-     , function(error) { if (error) { console.log(error); process.exit(1) } }
-     )
-// vim: set filetype=javascript :
diff --git a/lib/jdiscovery/mdns/utils/lib/actors.js b/lib/jdiscovery/mdns/utils/lib/actors.js
deleted file mode 100644
index 7b7040dd..00000000
--- a/lib/jdiscovery/mdns/utils/lib/actors.js
+++ /dev/null
@@ -1,63 +0,0 @@
-var fs = require('fs')
-  , child_process = require('child_process')
-  , _glob = require('glob')
-  , bunch = require('./bunch')
-  ;
-
-exports.loadEnv = function loadEnv(env, cb) {
-  var loaders = []
-  function load(name, cb) {
-    fs.readFile(env[name], function(error, data) {
-      env[name] = env[name].match(/.*\.json$/) ?  JSON.parse(data) : data;
-      cb(error, data)
-    })
-  }
-  for (var name in env) {
-    loaders.push([load, name])
-  }
-  bunch(loaders, cb)
-}
-
-exports.commandActor = function command(executable) {
-  return function command(args, opts, cb) {
-    if (!cb) {
-      cb = opts;
-      opts = {}
-    }
-    var cmd = child_process.spawn(executable, args, opts);
-    function log(b) { console.log(b.toString()) } 
-    cmd.stdout.on('data', log);
-    cmd.stderr.on('data', log);
-    cmd.on('exit', function(code) {
-      if (code) {
-        cb(new Error(executable + ' exited with status ' + code));
-      } else {
-        cb();
-      }
-    });
-    return cmd;
-  }
-}
-
-exports.jsonParse = function(str, cb) {
-  try {
-    cb(null, JSON.parse(str));
-  } catch (ex) {
-    cb(ex);
-  }
-}
-
-exports.jsonStringify = function(obj, cb) {
-  try {
-    cb(null, JSON.stringify(obj));
-  } catch (ex) {
-    cb(ex);
-  }
-}
-exports.glob = function glob(pattern, cb) {
-  console.log('pattern', pattern);
-  _glob(pattern, function(error, files) {
-    cb(error, [files]);
-  });
-}
-
diff --git a/lib/jdiscovery/mdns/utils/lib/bunch.js b/lib/jdiscovery/mdns/utils/lib/bunch.js
deleted file mode 100644
index d155bc46..00000000
--- a/lib/jdiscovery/mdns/utils/lib/bunch.js
+++ /dev/null
@@ -1,12 +0,0 @@
-module.exports = function bunch(tasks, cb) {
-  var count = 0, has_error;
-  function done(error) {
-    if (error) {
-      has_error = error;
-      cb(error);
-    }
-    if (++count === tasks.length && ! has_error) cb();
-  }
-  tasks.forEach(function(t) { t[0].apply(null, t.slice(1).concat([done])) });
-}
-
diff --git a/lib/jdiscovery/mdns/utils/lib/compiler.js b/lib/jdiscovery/mdns/utils/lib/compiler.js
deleted file mode 100644
index 7a1f3742..00000000
--- a/lib/jdiscovery/mdns/utils/lib/compiler.js
+++ /dev/null
@@ -1,70 +0,0 @@
-
-var fs     = require('fs')
-  , path = require('path')
-  , mkdirp = require('mkdirp')
-  , obj = require('./obj')
-  ;
-
-exports.compiler = function compiler(f, name, options) {
-  options = options || {};
-  return function(src, dst, dynamic_options, cb) {
-    if ( ! cb ) {
-      cb = dynamic_options;
-      dynamic_options = {};
-    }
-    var all_options = resolve_options(src, dst, options, dynamic_options)
-    fs.readFile(src, function(error, data) {
-      if (error) return cb(error);
-      f(data.toString(), all_options, function(error, result) {
-        if (error) {
-          cb(error);
-        } else {
-          mkdir_and_write_file(dst, result, cb);
-        }
-      });
-    });
-  }
-}
-
-exports.compilerSync = function compilerSync(f, name, options) {
-  options = options || {};
-  return function(src, dst, dynamic_options, cb) {
-    if ( ! cb ) {
-      cb = dynamic_options;
-      dynamic_options = {};
-    }
-    console.log('[' + name + ']', dst)
-    var all_options = resolve_options(src, dst, options, dynamic_options)
-    fs.readFile(src, function(error, data) {
-      if (error) return cb(error);
-      var result = f(data.toString(), all_options);
-      mkdir_and_write_file(dst, result, cb);
-    });
-  }
-}
-
-exports.src = {};
-exports.dst = {};
-
-function resolve_options(src, dst /*, options, ... */) {
-  var option_objects = Array.prototype.slice.call(arguments, 2)
-    , options = obj.union.apply(null, option_objects);
-  for (var opt in options) {
-    if (options[opt] === exports.src) {
-      options[opt] = src;
-    } else if (options[opt] === exports.dst) {
-      options[opt] = dst;
-    }
-  }
-  return options;
-}
-
-function mkdir_and_write_file(file, content, cb) {
-  mkdirp(path.dirname(file), function(error) {
-    if (error) {
-      cb(error)
-    } else {
-      fs.writeFile(file, content, cb)
-    }
-  });
-}
diff --git a/lib/jdiscovery/mdns/utils/lib/lcov.js b/lib/jdiscovery/mdns/utils/lib/lcov.js
deleted file mode 100644
index 9e7b5b92..00000000
--- a/lib/jdiscovery/mdns/utils/lib/lcov.js
+++ /dev/null
@@ -1,127 +0,0 @@
-
-var fs = require('fs')
-  ;
-exports.infoFileToJson = function infoFileToJson(filename, filefilter, cb) {
-  var input = fs.createReadStream(filename)
-  var parser = new LcovLoader(filefilter)
-  function process_lcov_chunk(chunk) { parser.consume(chunk); }
-  input.on('data', process_lcov_chunk);
-  input.on('end', function() {
-    //process.stdout.write(JSON.stringify(parser.result, null, 2));
-    cb(null, parser.result)
-  });
-}
-
-
-
-var LcovLoader = exports.LcovLoader = function LcovLoader(filefilter) {
-  this.tail = ''
-  this.result   = {
-      files: {}
-    , tests: {}
-    , functionCount: {}
-  }
-  this.testname = '';
-  this.filename = '';
-  this.filefilter = filefilter || function() { return true };
-}
-LcovLoader.prototype =
-{ tail: ''
-, result: {}
-, testname: ''
-, filename: ''
-, filefilter: null
-}
-
-LcovLoader.prototype.consume = function consume(chunk) {
-  var self = this
-    , lines = chunk.toString().split('\n')
-    ;
-  if (this.tail) { lines[0] = this.tail + lines[0]; }
-  this.tail = lines.pop()
-
-  var testname = self.testname
-    , filename = self.filename
-    , result = self.result
-    , functionCount = result.functionCount
-    , file = result.files[filename]
-    , test = result.tests[filename]
-    , count = 0
-    , warn_negative = false
-    ;
-  function consumeLine(line) {
-    if (line.match(/^TN:([^,]*)(,diff)?/)) {
-      testname = (RegExp.$1 || '') + (RegExp.$2 || '');
-      if (testname) result.tests[testname] = {functionCount: {} };
-    } else if (line.match(/^[SK]F:(.*)/)) {
-      filename = RegExp.$1;
-      if (self.filefilter(filename)) {
-        if (!result.files[filename]) result.files[filename] = {};
-        file = result.files[filename];
-        //console.log('file:', filename, file)
-        if (testname) {
-          if (!result.tests[testname]) result.tests[testname] = {};
-          test = result.tests[testname];
-        } else {
-          test = null;
-        }
-      } else {
-        file = null
-      }
-    } else if (line.match(/^DA:(\d+),(-?\d+)(,[^,\s]+)?/)) {
-      if (!file) { 
-        return
-      }
-      count = parseInt(RegExp.$2)
-      if (count < 0) {
-        count = 0;
-        warn_negative = true;
-      }
-      if (!file.lines) file.lines = {};
-      if (!file.lines[RegExp.$1]) file.lines[RegExp.$1] = 0
-      file.lines[RegExp.$1] += count;
-      if (test) {
-        if (!test.lines) test.lines = {};
-        if (!test.lines[RegExp.$1]) test.lines[RegExp.$1] = 0;
-        test.lines[RegExp.$1] += count;
-      }
-    } else if (line.match(/^FN:(\d+),([^,]+)/)) {
-      if (!file) { 
-        return
-      }
-      if (!file.functions) file.functions = {};
-      file.functions[RegExp.$2] = {};
-      file.functions[RegExp.$2].line = parseInt(RegExp.$1);
-      functionCount[RegExp.$2] = 0;
-      if (test && ! test.functionCount[RegExp.$2]) test.functionCount[RegExp.$2] = 0
-    } else if (line.match(/^FNDA:(\d+),([^,]+)/)) {
-      if (!file) { 
-        return
-      }
-      functionCount[RegExp.$2] += parseInt(RegExp.$1);
-    } else if (line.match(/^BRDA:(\d+),(\d+),(\d+),(\d+|-)/)) {
-      if (!file) {
-        return
-      }
-      if (!file.branches) { file.branches = {} }
-      var line = RegExp.$1
-        , block = RegExp.$2
-        , branch = RegExp.$3
-        , taken  = RegExp.$4
-        ;
-      if(!file.branches[block]) {file.branches[block] = {}};
-      if(!file.branches[block][branch]) {file.branches[block][branch] = 0};
-      file.branches[block][branch] += parseInt(taken);
-      //console.log('line:', line, 'block:', block, 'branch:', branch, 'taken:', taken)
-    } else if (line.match(/^end_of_record/)) {
-    } else {
-      //console.log('unhandled:', line)
-    }
-  }
-
-  lines.forEach(consumeLine);
-
-  self.filename = filename;
-  self.testname = testname;
-}
-
diff --git a/lib/jdiscovery/mdns/utils/lib/mdns_test.js b/lib/jdiscovery/mdns/utils/lib/mdns_test.js
deleted file mode 100644
index 0ed156c4..00000000
--- a/lib/jdiscovery/mdns/utils/lib/mdns_test.js
+++ /dev/null
@@ -1,55 +0,0 @@
-
-var path = require('path')
-  ;
-
-
-exports.require = function _require(what) {
-  if (process.env.npm_config_coverage || process.env.BUILDTYPE === 'Coverage') {
-    return require(path.join('../../build/Coverage/lib/' + what))
-  }
-  return require('../../lib/' + what)
-}
-
-var mdns  = exports.require('mdns');
-
-var legal_chars = "abcefghijklmnopqrstuvwxyz0123456789"
-
-exports.suffixedServiceType = function suffixedServiceType(serviceName, protocol) {
-  serviceName += '-';
-  while (serviceName.length < 14) {
-    serviceName += legal_chars[Math.floor(Math.random() * legal_chars.length)];
-  }
-  return mdns.makeServiceType.apply(this, arguments)
-}
-
-exports.runTestAd = function runTestAd(type, port, uptime, cooltime, cb) {
-  if ( ! cb ) {
-    cb = cooltime;
-    cooltime = uptime;
-  }
-  if ( ! cb) {
-    cb = cooltime;
-    uptime = cooltime = 2000;
-  }
-  var ad = new mdns.createAdvertisement(type, port, function(error, service) {
-        if (error) throw error;
-        setTimeout(function() { ad.stop(); setTimeout(cb, cooltime); }, uptime);
-      });
-  ad.start();
-  return ad;
-}
-
-if (0) {
-exports.lifeExpectancy = 5000;
-
-var lifeTimer;
-
-process.nextTick(function() {
-  lifeTimer = setTimeout(function() {
-    throw new Error("test did not finish within " + (exports.lifeExpectancy / 1000) + " seconds.");
-    process.exit(-1);
-  }, exports.lifeExpectancy);
-});
-
-exports.done = function done() { clearTimeout( lifeTimer ); }
-}
diff --git a/lib/jdiscovery/mdns/utils/lib/ncov.js b/lib/jdiscovery/mdns/utils/lib/ncov.js
deleted file mode 100755
index ce7acec9..00000000
--- a/lib/jdiscovery/mdns/utils/lib/ncov.js
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env node
-
-var fs = require('fs')
-  , path = require('path')
-  , nopt = require('nopt')
-  , mm = require('minimatch')
-  , lcov = require('./lcov')
-  , slide = require('slide')
-  , ejs = require('ejs')
-  , mkdirp = require('mkdirp')
-  , view = require('./view')
-  , obj = require('./obj')
-
-  , knownOptions =
-    { match    : String
-    , template : path
-    }
-  , parsedOptions = nopt(knownOptions)
-  , rootd = path.resolve(__dirname, '..', '..')
-  ;
-
-var filefilter = mm.Minimatch(parsedOptions.match || rootd + '/**');
-filter = function(f) { return filefilter.match(f) };
-
-var chain = slide.chain
-  , meta = {}
-  , outd = path.join(rootd, 'build')
-  , pagedir = path.join(outd, 'pages')
-  , docd = path.join(rootd, 'doc')
-  , lcov_info_file = path.join(outd, 'reports', 'coverage', 'cpp', 'testrun_all.info')
-  , json_report = path.join(outd, 'reports', 'coverage', 'testrun_coverage-cpp.json')
-  , prerequisites =
-    { layout: path.join(docd, 'layout.ejs')
-    , source_template: path.join(docd, 'build', 'source.ejs')
-    }
-  ;
-
-chain( [ [ load_prerequisites, meta, prerequisites]
-       , [ lcov.infoFileToJson, lcov_info_file, filter]
-       , [ save_json, chain.last, json_report]
-       , [ render_pages, chain.last]
-       ]
-     , function(error) {
-         console.log('done', error);
-         if (error) { process.exit(1) }
-       }
-     );
-
-function save_json(report, file, cb) {
-  fs.writeFile(file, JSON.stringify(report, null, 2), function(err) {
-    console.log('================================================================================');
-    console.log('testrun c++ coverage report saved to', file.replace(/.*\/(build\/.*)/, "$1"));
-    console.log('================================================================================');
-    cb(err, report);
-  });
-}
-
-function render_pages(coverage, cb) {
-  var pages = Object.keys(coverage.files)
-    , stripped_names = strip_common_path(pages, 1)
-    , funcs = pages.map(function(p,i) {
-        return [render_source_page, p, stripped_names[i], coverage.files[p], coverage.functionCount, meta]
-      })
-    ;
-  bunch(funcs, cb);
-}
-
-function render_source_page(file, stripped_name, coverage, functions, meta, cb) {
-  var outfile = path.join(pagedir, 'coverage', stripped_name) + '.html';
-  chain( [ [ fs.readFile, file ]
-         , [ render_source_view, chain.last, stripped_name, coverage, functions, outfile, meta ]
-         , [ mkdirp, path.dirname(outfile) ]
-         , [ fs.writeFile, outfile, chain.last ]
-         ]
-       , cb
-       )
-}
-
-function pad(n, len) {
-  var str = '' + n;
-  while (str.length < len) { str = ' ' + str }
-  return str
-}
-
-function hit_counts(coverage, line_count) {
-  var hit_lines = [];
-  for (var i = 1; i <= line_count; ++i) {
-    if (coverage.lines[i]) {
-      hit_lines.push(coverage.lines[i])
-    } else {
-      hit_lines.push(0)
-    }
-  }
-  var num_digits = number_of_digits( Math.max.apply(null, hit_lines))
-    ;
-  return hit_lines.map(function(lc) { return lc ? pad(lc, num_digits) : '&nbsp;' }).join('\n')
-}
-
-function number_of_digits(n) { return Math.ceil(Math.log(n)/Math.LN10) }
-
-function render_source_view(source, name, coverage, functions, dst, meta, cb) {
-  var lines = source.toString().split('\n')
-    ;
-  if (lines[lines.length - 1] === '') {
-    lines.pop()
-  }
-
-  var metadata =
-    { stylesheets: 
-      [ '/stylesheets/mdns.css'
-      , '/stylesheets/build_status.css'
-      ]
-    , scripts: []
-    , title: 'Test Coverage of ' + name
-    };
-  var locals = { lines: lines
-               , coverage: coverage
-               , path: name.split('/')
-               , escape: require('ejs/lib/utils').escape
-               , counts: found_and_hit(coverage, functions)
-               };
-  locals = obj.union(locals, view.helpers(metadata, {outputDir: pagedir, dst: dst}));
-  metadata.body = ejs.render(meta.source_template.toString(), locals);
-  metadata.path = view.getPathHelper({outputDir: pagedir, dst: dst});
-  var html = ejs.render(meta.layout.toString(), metadata);
-  cb(null, html);
-}
-
-function found_and_hit(coverage, function_calls) {
-  var result = { lines:     {found: 0, hit: 0}
-               , functions: {found: 0, hit: 0}
-               , branches:  {found: 0, hit: 0}
-               };
-  function fnh(things, out) {
-    for (var p in things) {
-      out.found += 1;
-      if (things[p] > 0) {
-        out.hit += 1;
-      }
-    }
-  }
-  fnh(coverage.lines, result.lines);
-  for (var f in coverage.functions) {
-    result.functions.found += 1;
-    if (function_calls[f] > 0) {
-      result.functions.hit += 1;
-    }
-  }
-  return result;
-}
-
-function load(file, data, name, cb) {
-  fs.readFile(file, function(error, content) {
-    if (error) { cb(error); return }
-    data[name] = content;
-    cb()
-  });
-}
-
-function load_prerequisites(data, things, cb) {
-  var funcs = [];
-  for (var p in things) {
-    funcs.push([load, things[p], data, p]);
-  }
-
-  bunch(funcs, cb);
-}
-
-
-function strip_common_path(names, keep_common) {
-  var paths = names.map(function(n) {return n.split('/')})
-    , common_elements = 0
-    , first
-    , all_equal = true;
-    ;
-  while (all_equal) {
-    first = paths[0][common_elements];
-    all_equal = paths.every(function(p) { return p[common_elements] === first});
-    if (all_equal) common_elements += 1;
-  }
-  common_elements -= 1;
-  common_elements -= keep_common;
-  if (common_elements < 0) {
-    return
-  }
-
-  return paths.map(function(p) { return p.slice(common_elements).join('/') })
-
-}
-
-function bunch(things, cb) {
-  var count = 0, has_error;
-  function done(error) {
-    count += 1;
-    if (error && ! has_error) {
-      has_error = error;
-      cb(error);
-    }
-    if ( ! has_error && count == things.length) {
-      cb();
-    }
-  }
-  things.forEach(function(t) { t[0].apply(null, t.slice(1).concat([done])) });
-}
-
-// vim: filetype=javascript :
diff --git a/lib/jdiscovery/mdns/utils/lib/obj.js b/lib/jdiscovery/mdns/utils/lib/obj.js
deleted file mode 100644
index 8c4313ae..00000000
--- a/lib/jdiscovery/mdns/utils/lib/obj.js
+++ /dev/null
@@ -1,12 +0,0 @@
-exports.union = function union(/* object, object, ... */)  {
-  if (arguments.length < 2) {
-    throw new Error('union() requires at least two arguments');
-  }
-  var result = {};
-  for (var i in arguments) {
-    for (var p in arguments[i]) {
-      result[p] = arguments[i][p];
-    }
-  }
-  return result;
-}
diff --git a/lib/jdiscovery/mdns/utils/lib/view.js b/lib/jdiscovery/mdns/utils/lib/view.js
deleted file mode 100644
index e070efe9..00000000
--- a/lib/jdiscovery/mdns/utils/lib/view.js
+++ /dev/null
@@ -1,53 +0,0 @@
-
-var path = require('path')
-  , ejs = require('ejs')
-  , obj = require('./obj')
-  ;
-
-exports.make_relative = function make_relative(paths, dst, root) {
-  var result = [];
-  var r = dst.substr(0, root.length);
-  if ( r !== root) {
-    console.log('KAPUTT');
-  }
-  dst = dst.substr(root.length)
-  return paths.map(function(p) { return path.relative(path.dirname(dst), p) });
-}
-
-var helpers = exports.helpers = function helpers(ctx, options) {
-  options = options || {};
-  var helpers = {};
-  helpers.title = function title(t) { if(t) {ctx.title = t} return ctx.title }
-  helpers.script = function script(s) { ctx.scripts.push(s) }
-  helpers.stylesheet = function stylesheet(s) { ctx.stylesheets.push(s) }
-  helpers.path = exports.getPathHelper(options)
-  return helpers;
-}
-
-function passthrough(p) { return p }
-
-exports.getPathHelper = function getPathHelper(options) {
-  if (options.outputDir) {
-    return function _path(p) {
-      var r = options.dst.substr(0, options.outputDir.length);
-      if ( r !== options.outputDir) { throw new Error('KAPUTT') }
-      var dst = options.dst.substr(options.outputDir.length);
-      var relpath =  path.relative(path.dirname(dst), p);
-      return relpath;
-    }
-  } else {
-    return function _path(p) { return p }
-  }
-}
-
-
-exports.render = function render(source, options, cb) {
-  var metadata = { stylesheets: [], scripts: []}
-    , locals = helpers(metadata, options)
-    ;
-  locals = obj.union(options.locals || {}, locals);
-  metadata.body = ejs.render(source, locals);
-  metadata.path = exports.getPathHelper(options);
-  var html = ejs.render(options.layout.toString(), metadata);
-  cb(null, html);
-}
diff --git a/lib/jdiscovery/mdns/utils/render_report b/lib/jdiscovery/mdns/utils/render_report
deleted file mode 100755
index 97251393..00000000
--- a/lib/jdiscovery/mdns/utils/render_report
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env node
-
-var fs = require('fs')
-  , path  = require('path')
-  , view  = require('./lib/view')
-  , obj  = require('./lib/obj')
-  , actors  = require('./lib/actors')
-  , compiler = require('./lib/compiler')
-  , slide = require('slide')
-  , chain = slide.chain
-  , asyncMap = slide.asyncMap
-  ;
-
-var rootd = path.resolve(__dirname, '..')
-  , env =
-    { layout: rootd + '/doc/layout.ejs'
-    , apple_logo: rootd + '/doc/pages/images/apple_logo.svg'
-    , ubuntu_logo: rootd + '/doc/pages/images/ubuntu_logo.svg'
-    , freebsd_logo: rootd + '/doc/pages/images/freebsd_logo.svg'
-    , windows_logo: rootd + '/doc/pages/images/windows_logo.svg'
-    }
-
-function done() {
-  console.log('============= DONE ===============');
-  console.log(arguments);
-  console.log(env);
-  console.log('==================================');
-}
-
-var render = compiler.compiler(view.render, 'ejs', env)
-  ;
-
-function configure(cb) {
-  env.src = compiler.src;
-  env.dst = compiler.dst;
-  env.outputDir = rootd + '/out/pages';
-  cb()
-};
-
-var git = actors.commandActor('git');
-
-console.log('rootd', rootd);
-
-chain( [ [ actors.loadEnv, env ]
-       , [ configure ]
-       //, [ git, ['clone', '-b', 'ci-build', 'git://github.com/agnat/node_mdns.git'
-       //        , rootd + '/out/ci-build'] ]
-       , [ actors.glob, rootd + '/out/ci-build/builds/*.json' ]
-       , [ load_builds, chain.last ]
-       , [ render_status_page, chain.last]
-       ]
-     , done
-     );
-
-function load_builds(files, cb) {
-  asyncMap(files, load_build, function(error, builds) {
-    var result = {}
-    builds.forEach(function(b) { result[b.id] = b; b.timestamp = parseInt(b.timestamp) });
-    cb(error, result);
-  });
-}
-
-function render_status_page(builds, cb) {
-  var ids = Object.keys(builds).sort(function(a, b) { return b - a })
-    , latest = builds[ids[0]]
-    , history = []
-    , latest_stages = []
-    , versions = {}
-    , os = {}
-    ;
-  for (var i = 1; i < ids.length; ++i) {
-    history.push(builds[ids[i]]);
-  }
-  for (var stage in latest.stages) {
-    var tokens = stage.split('-')
-      , node = tokens[0]
-      , build_system = tokens[2]
-      ;
-    node = node.replace(/(\d+)_(\d+)/, "$1.$2").replace(/_/, ' ');
-    var version = node.split(' ')[1];
-    latest.stages[stage].node_version = version;
-    version += '-' + build_system;
-    latest.stages[stage].key = version;
-    versions[version] = {name: latest.stages[stage].node_version, sub: build_system};
-    os[latest.stages[stage].os] = {name: latest.stages[stage].os};
-  }
-  Object.keys(versions).forEach(function(v, i) { versions[v].idx = i});
-  Object.keys(os).forEach(function(o, i) { os[o].idx = i; });
-  var version_count = Object.keys(versions).length;
-  Object.keys(os).forEach(function(o,i) {
-    latest_stages[i] = new Array(version_count);
-  });
-  for (var stage in latest.stages) {
-    var s = latest.stages[stage];
-    latest_stages[versions[s.key].idx][os[s.os].idx] = s;
-  }
-
-  console.log('versions:', versions, 'os:', os);
-
-  render( rootd + '/doc/build/status.ejs'
-        , rootd + '/out/pages/build/status.html'
-        , { locals:
-            { latest: latest
-            , history: history
-            , latestStages: latest_stages
-            , versions: versions
-            , os: os
-            , logos:
-              { macosx: env.apple_logo
-              , linux: env.ubuntu_logo
-              , freebsd: env.freebsd_logo
-              , win32: env.windows_logo
-              }
-            }
-          }
-        , cb
-        )
-}
-
-function load_build(f, cb) {
-  fs.readFile(f, function loaded(error, buffer) {
-    cb(error, buffer ? JSON.parse(buffer) : buffer)
-  })
-}
-
-// vim: filetype=javascript :
diff --git a/lib/jdiscovery/mdns/utils/testrun b/lib/jdiscovery/mdns/utils/testrun
deleted file mode 100755
index 5f2cff98..00000000
--- a/lib/jdiscovery/mdns/utils/testrun
+++ /dev/null
@@ -1,301 +0,0 @@
-#!/usr/bin/env node
-
-process.env.AVAHI_COMPAT_NOWARN = 1
-
-try {
-  var path = require('path')
-    , fs = require('fs')
-    , assert = require('assert')
-    , events = require('events')
-    , nopt = require('nopt')
-    , chain = require('slide').chain
-    , glob = require('glob')
-    , mkdirp = require('mkdirp')
-    ;
-} catch(e) {
-  console.error("Failed to load dependencies. Run 'npm link' to install them.");
-  console.error(e);
-  process.exit(1);
-}
-
-if (process.env.BUILDTYPE === 'Coverage') {
-  process.once('exit', function(code) {
-    var fs   = require('fs')
-      , path = require('path')
-      , executable = path.basename(require.main.filename)
-      , file = path.join(process.env.NCOV_OUT, executable + '_coverage-js.json')
-      , dir  = path.dirname(file)
-      , existsSync = fs.existsSync || path.existsSync
-      ;
-    if( ! existsSync(dir)) {
-      fs.mkdirSync(dir);
-    }
-    fs.writeFileSync(file, JSON.stringify(_$jscoverage, null, 2));
-    console.log('================================================================================');
-    console.log(executable, ' js coverage report saved to', file);
-    console.log('================================================================================');
-  })
-}
-
-function padding(l) {
-  var p = '', i = 0;
-  for (i = 0; i < l; ++i) { p += ' ' }
-  return p;
-}
-
-var DefaultFormater = function(options) {
-  var self = this
-    , max_length = 0
-    ;
-  self.ok     = options.ascii ? '[OK]'     : '\u2714';
-  self.failed = options.ascii ? '[FAILED]' : '\u2718';
-  self.tcase_bullet = '*'
-
-  self.groupStart = function groupStart(name, members) {
-    console.log();
-    if (options.ascii) {
-      console.log('===', name);
-    } else {
-      console.log(bold(name));
-    }
-    max_length = 0;
-    members.forEach(function(m) {
-      if (m.length > max_length) { max_length = m.length }
-    });
-  }
-  this.testcaseEnd = function testcaseEnd(name, assertions, failures) {
-    var pad = padding(max_length - name.length);
-    if (options.verbose) {
-      console.log( self.tcase_bullet
-                 , name
-                 , '[' + (assertions.length - failures.length) + '/' + assertions.length + ']'
-                 );
-
-      assertions.forEach(function(a) {
-        console.log((options.ascii ? '' : '  ') + (a.error ? self.failed : self.ok), a.message || 'MESSAGE MISSING');
-      });
-    } else {
-      console.log( failures.length === 0 ? self.ok : self.failed
-                 , name, pad
-                 , '[' + (assertions.length - failures.length) + '/' + assertions.length + ']'
-                 );
-
-    }
-    failures.forEach(function(f) {
-      console.log(f.error.toString());
-      console.log(f.error);
-    });
-  }
-  this.done = function done(total, failed) {
-    console.log()
-    if (failed) {
-      console.log('FAILURES:', failed + '/' + total, 'assertions failed', 
-          options.bark ? String.fromCharCode(7) : '');
-    } else {
-      console.log('OK:', total, 'assertions');
-    }
-  }
-}
-
-var knownOptions = { 'bark'    : Boolean
-                   , 'format'  : ['default']
-                   , 'ascii'   : Boolean 
-                   , 'verbose' : Boolean 
-                   , 'help'    : Boolean 
-                   }
-  , parsed = nopt(knownOptions)
-  , test_dir = path.join(__dirname, '..', 'tests')
-  ;
-
-if ( ! ('bark' in parsed)) { parsed.bark = true; }
-var formats = { default: new DefaultFormater(parsed) };
-
-process.title = path.basename(__filename);
-if (parsed.help) { printHelp(); process.exit(); }
-
-function run_tests(files, cb) {
-  var reporter = new AssertRecorder()
-    , format = formats[parsed.format || 'default']
-    ;
-  Object.keys(format).forEach(function(ev) {
-    if (typeof format[ev] === 'function') {
-      reporter.on(ev, format[ev]);
-    }
-  });
-
-  chain( [ //[glob_list, path.join(test_dir, 'test_*.js')]
-         , [test_list]
-         , [run_modules, chain.last, reporter]
-         , [save_reports, reporter]
-         ]
-         , function() { reporter.allDone(); cb(undefined, reporter.failedCount) }
-       )
-}
-
-function test_list(cb) {
-  fs.readdir(test_dir, function(error, files) {
-    var r = []
-      , pattern = /^test_.*\.js$/
-      ;
-    r = files . filter(function(f) { return pattern.test(f) })
-              . map(function(f) { return path.join('tests', f)})
-              ;
-    cb(error, [r]);
-  })
-}
-
-
-
-function glob_list(pattern, cb) {
-  glob(pattern, function(error, files) {
-    cb(error, [files])
-  });
-}
-
-function run_modules(files, reporter, cb) {
-  var module_chain = files.map(function(f) {
-      var name = path.basename(path.basename(f), '.js');
-      return [run_group, require(path.join('..', f)), name, reporter];
-  });
-  chain(module_chain, cb);
-}
-
-function run_group(group, name, reporter, cb) {
-  var names = Object.keys(group)
-    , group_chain = names.map(function(test) {
-        return [ typeof group[test] === 'function' ? run_testcase : run_group
-               , group[test], test, reporter];
-      });
-    ;
-    reporter.group_start(name, names);
-    chain(group_chain, function(error) {
-      reporter.group_end(name);
-      cb(error);
-    });
-}
-
-var maybe_gc;
-try {
-  maybe_gc = gc;
-} catch (ex) {
-  maybe_gc = function() {}
-}
-
-function run_testcase(f, name, reporter, cb) {
-  reporter.testcase_start(name);
-  reporter.done = function done() {
-    maybe_gc();
-    reporter.testcase_end(name);
-    cb();
-  };
-  f(reporter);
-}
-
-function save_reports(reporter, cb) {
-  var dir = path.join(__dirname, '..', 'out', 'reports');
-  chain( [ [mkdirp, dir]
-         , [fs.writeFile, path.join(dir, 'tests.json'), JSON.stringify(reporter.results, null, 2)]
-         ]
-       , cb
-       );
-}
-
-var msg_indices = 
-{ ok: 1
-, equal: 2
-, notEqual: 2
-, deepEqual: 2
-, notDeepEqual: 2
-, strictEqual: 2
-, notStrictEqual: 2
-, throws: 1
-, doesNotThrow: 1
-};
-
-function wrap_assert(name) {
-  var msg_idx = msg_indices[name];
-  return function() {
-    var assertion = {test: name, message: arguments[msg_idx], error: undefined};
-    try {
-      assert[name].apply(null, arguments);
-    } catch (error) {
-      assertion.error = error;
-    }
-    this.back().push(assertion);
-  }
-}
-
-function add_assert_wrappers(t) {
-  var p, msg_idx;
-  for (p in assert) {
-    if (assert[p] !== assert.AssertionError) {
-      t[p] = wrap_assert(p);
-    }
-  }
-}
-
-var AssertRecorder = function AssertRecorder() {
-  events.EventEmitter.call(this);
-  this.results = {};
-  this.stack = [this.results];
-  this.assertionCount = 0;
-  this.failedCount = 0;
-}
-AssertRecorder.prototype = new events.EventEmitter();
-add_assert_wrappers(AssertRecorder.prototype);
-
-AssertRecorder.prototype.group_start = function group_start(name, members) {
-  var group = this.back()[name] = {};
-  this.stack.push(group);
-  this.emit('groupStart', name, members);
-}
-AssertRecorder.prototype.group_end = function group_end(name) {
-  this.stack.pop();
-  this.emit('groupEnd', name);
-}
-AssertRecorder.prototype.testcase_start = function testcase_start(name) {
-  var testcase = this.back()[name] = [];
-  this.stack.push(testcase);
-  this.emit('testcaseStart', name);
-}
-AssertRecorder.prototype.testcase_end = function testcase_end(name) {
-  var assertions = this.stack.pop()
-    , failures = assertions.filter(function(a) { return a.error }) || []
-    ;
-  this.emit('testcaseEnd', name, assertions, failures);
-  this.assertionCount += assertions.length;
-  this.failedCount += failures.length;
-}
-AssertRecorder.prototype.back = function back() {
-  return this.stack[this.stack.length - 1];
-}
-AssertRecorder.prototype.allDone = function() {
-  this.emit('done', this.assertionCount, this.failedCount);
-}
-
-run_tests(parsed.argv.remain, function(error, failed_count) {
-  process.exit(failed_count);
-});
-
-function printUsage() {
-  console.log('Usage:', process.title,
-      Object.keys(knownOptions).map(function(o) {
-        return '[--' + o + ']'
-      }).join(' '));
-}
-
-function printHelp() {
-  printUsage(); console.log(
-"Options:\n\
-   --format=<fmt>  Use formatter <fmt>\n\
-   --no-bark       Don't bark on test failures\n\
-   --help          Print this help and exit\n\
-\n\
-Formats:\n\
-   " + Object.keys(formats).join(', ') + "\n\
-");
-
-}
-
-function bold(s) { return '\033[1m' + s + '\033[22m' };
-// vim: filetype=javascript :
diff --git a/lib/jdiscovery/mdns/wscript b/lib/jdiscovery/mdns/wscript
deleted file mode 100644
index a7feca70..00000000
--- a/lib/jdiscovery/mdns/wscript
+++ /dev/null
@@ -1,101 +0,0 @@
-import os, shutil, subprocess, Scripting, Options
-
-out  = 'build'
-name = 'dns_sd_bindings'
-
-def set_options(opt):
-  opt.tool_options('compiler_cxx')
-  opt.tool_options('node_addon')
-
-def configure(conf):
-  conf.check_tool('compiler_cxx')
-  conf.check_tool('node_addon')
-
-  # HACK: The current node master builds a i386 binary while older releases used
-  # the compilers default x86_64. node-waf still targets x86_64 resulting in an
-  # incompatible binary. Building the add-on as a universal binary for both
-  # architectures solves this for now ...
-  if conf.env.DEST_OS == 'darwin':
-    universal_flags = ['-arch', 'i386', '-arch', 'x86_64']
-    conf.env.append_value('CXXFLAGS', universal_flags)
-    conf.env.append_value('LINKFLAGS_MACBUNDLE', universal_flags)
-
-  includes = ['/usr/local/include'] # help freebsd
-  libpath = ['/usr/local/lib']      # help freebsd
-  if conf.check( header_name='dns_sd.h'
-               , includes=includes
-               , uselib_store='DNS_SD'
-               , mandatory=True):
-    conf.check(lib='dns_sd', libpath=libpath, uselib_store='DNS_SD')
- 
-    conf.check(function_name='DNSServiceGetAddrInfo',
-               header_name="dns_sd.h",
-               uselib='DNS_SD')
-
-
-def build(bld):
-  obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
-  obj.target = name
-  obj.uselib = 'DNS_SD'
-  if bld.env.HAVE_DNSSERVICEGETADDRINFO:
-    obj.defines = ['HAVE_DNSSERVICEGETADDRINFO']
-  obj.includes = ['..']
-  obj.cxxflags = ['-Wall']
-  obj.source = [ 'src/dns_sd.cpp'
-               , 'src/dns_service_browse.cpp'
-               , 'src/dns_service_enumerate_domains.cpp'
-               , 'src/dns_service_process_result.cpp'
-               , 'src/dns_service_ref.cpp'
-               , 'src/dns_service_ref_deallocate.cpp'
-               , 'src/dns_service_ref_sock_fd.cpp'
-               , 'src/dns_service_register.cpp'
-               , 'src/dns_service_resolve.cpp'
-               , 'src/dns_service_get_addr_info.cpp'
-               , 'src/dns_service_update_record.cpp'
-               , 'src/mdns_utils.cpp'
-               , 'src/network_interface.cpp'
-               , 'src/txt_record_ref.cpp'
-               , 'src/txt_record_create.cpp'
-               , 'src/txt_record_deallocate.cpp'
-               , 'src/txt_record_set_value.cpp'
-               , 'src/txt_record_get_length.cpp'
-               , 'src/txt_record_buffer_to_object.cpp'
-               ]
-
-  obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
-  obj.target = 'demangle'
-  obj.cxxflags = ['-Wall']
-  obj.source = [ 'src/demangle.cpp' ]
-
-  #bld.add_post_fun(post_build)
-
-
-def update_addon_symlink(ctx, directory, target):
-  symlink_path = os.path.join(directory, target)
-  remove_symlink(symlink_path)
-  path_to_addon = os.path.join('..', 'out', ctx.path.bld_dir(ctx.env), target)
-  os.symlink(path_to_addon, symlink_path)
-
-def post_build(ctx):
-  update_addon_symlink(ctx, 'lib', name + '.node')
-  update_addon_symlink(ctx, 'utils/lib', 'demangle.node')
-
-
-def remove_symlink(symlink):
-  if os.path.lexists(symlink):
-    os.unlink(symlink)
-
-def distclean(ctx):
-  Scripting.distclean(ctx)
-
-  remove_symlink(os.path.join('lib', name + '.node'))
-  remove_symlink(os.path.join('utils/lib', 'demangle.node'))
-
-  if os.path.exists('node_modules'):
-      shutil.rmtree('node_modules')
-
-def test(ctx):
-  subprocess.call(['utils/testrun'])
-
-
-# vim: set filetype=python shiftwidth=2 softtabstop=2 :
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index b13280c6..199263b9 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -1,14 +1,7 @@
-var mdns = require('./mdns/lib/mdns'),
+var bonjour = require('bonjour'),
     constants = require('../jamserver/constants'),
     Registry = require('./registry');
 
-var sequence = [
-    mdns.rst.DNSServiceResolve(),
-    'DNSServiceGetAddrInfo' in mdns.dns_sd ?
-	mdns.rst.DNSServiceGetAddrInfo() : mdns.rst.getaddrinfo({families:[0]}),
-    mdns.rst.makeAddressesUnique()
-];
-
 function MDNSRegistry(app, type, id, port) {
 
     Registry.call(this, app, type, id, port);
@@ -54,28 +47,22 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
     ['device', 'fog', 'cloud'].map(
         (x) => {
             this.attrRemovalBrowsers[x] =
-                mdns.createBrowser(
-                    mdns.tcp(this.app + '-' + (x) + '-attrrem'),
-                    {resolverSequence: sequence});
-            this.attrRemovalBrowsers[x].on('serviceUp', function(service) {
+                bonjour.find({type : this.app + '-' + (x) + '-attrrem'});
+            this.attrRemovalBrowsers[x].on('up', function(service) {
                 // ignore our own services
                 if (service.name == self.id) {
                     return;
                 }
                 // update attrChangeCache
-                let attrs = JSON.parse(service.txtRecord.data);
-                let seqval = service.txtRecord.seqval;
+                let attrs = JSON.parse(service.txt.data);
+                let seqval = service.txt.seqval;
                 for (var attr in attrs)
                     if (attrs.hasOwnProperty(attr))
                         self.attrRemovalCache[attr] = seqval;
             });
-            this.attrRemovalBrowsers[x].on('serviceDown', function(service) {
+            this.attrRemovalBrowsers[x].on('down', function(service) {
                 // do nothing!
             });
-            this.attrRemovalBrowsers[x].on('error', function(err) {
-                browser.stop();
-                self.quit();
-            });
             this.attrRemovalBrowsers[x].start();
         }
     );
@@ -104,17 +91,19 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
 MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
 
     // create attrrem advertisement
-    let attrRemovalAd = mdns.createAdvertisement(
-        mdns.tcp(this.app + '-' + this.type + '-attrrem'),
-        this.port,
+    let attrRemovalAd = bonjour.publish(
         {
-            name: this.id, 
-            txtRecord: {
-                data: JSON.stringify(Object.keys(this.attrs)),
-                seqval: seqval
-            }
+            name    :   this.id,
+            port    :   this.port,
+            type    :   this.app + '-' + this.type + '-attrrem',
+            txt     :   {
+                            data    :   JSON.stringify(Object.keys(attrs)),
+                            seqval  :   seqval
+                        }
         }
     );
+    attrRemovalAd.start();
+
     for (var attr in attrs) {
         if (attrs.hasOwnProperty(attr)) {
             if (this.attrsToAdvertise[attr] && this.ads[attr]) {
@@ -177,32 +166,13 @@ MDNSRegistry.prototype.stopDiscoveringAttributes = function(dattrs) {
  */
 MDNSRegistry.prototype.quit = function(seqval) {
 
-    // create attrrem advertisement
-    let attrRemovalAd = mdns.createAdvertisement(
-        mdns.tcp(this.app + '-' + this.type + '-attrrem'),
-        this.port,
-        {
-            name: this.id, 
-            txtRecord: {
-                data: JSON.stringify(Object.keys(this.attrsToAdvertise)),
-                seqval : seqval
-            }
-        }
-    );
-    // stop ads
-    for (var attr in this.ads) {
-        if(this.ads.hasOwnProperty(attr)) {
-            this.ads[attr].stop();
-        }
-    }
-    // stop attrrem advertisement
-    attrRemovalAd.stop();
-    // stop browsers
+    // stop advertising all attrs
+    this.removeAttributes(this.attrsToAdvertise , seqval);
+    // stop discovering all attrs
+    this.stopDiscoveringAttributes(this.attrsToBrowse);
+    // stop removal browsers
     ['device', 'fog', 'cloud'].map(
             (x) => {
-                for (var attr in this.browsers[x])
-                    if(this.browsers[x].hasOwnProperty(attr))
-                        this.browsers[x][attr].stop();
                 this.attrRemovalBrowsers[x].stop();
             }
     );
@@ -229,17 +199,17 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
                 this.ads[attr].stop();
                 delete this.ads[attr];
             }
-            // Create adv formatted info
-            let name = this.app + '-' + this.type + '-' + attr;
-            let txtRecord = {
-                data: JSON.stringify(attrs[attr]),
-                seqval: seqval
-            };
-            this.ads[attr] = mdns.createAdvertisement(
-                mdns.tcp(name), 
-                this.port, 
-                {name: this.id, txtRecord: txtRecord});
-
+            this.ads[attr] = bonjour.publish(
+                {
+                    name    :   this.id,
+                    port    :   this.port,
+                    type    :   this.app + '-' + this.type + '-' + attr,
+                    txt     :   {
+                                    data    :   JSON.stringify(attrs[attr]),
+                                    seqval  :   seqval
+                                }
+                }
+            );
             this.ads[attr].start();
         }
     }
@@ -249,13 +219,11 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
  */
 MDNSRegistry.prototype._createBrowser = function(attr, type) {
 
-    let browser = mdns.createBrowser(
-                    mdns.tcp(this.app + '-' + type + '-' + attr),
-					{resolverSequence: sequence});
+    let browser = bonjour.find({ type : this.app + '-' + type + '-' + attr });
     this.browsers[type][attr] = browser;
 
     var self = this;
-    browser.on('serviceUp', function(service) {
+    browser.on('up', function(service) {
         // DEBUG
         console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service up");
 
@@ -269,11 +237,11 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
                 ? self.attrsToBrowse[type][attr].online
                 : self.attrsToBrowse[type][attr].onAdd,
             service.name,
-            JSON.parse(service.txtRecord.data),
-            service.txtRecord.seqval
+            JSON.parse(service.txt.data),
+            service.txt.seqval
         );
     });
-    browser.on('serviceDown', function(service) {
+    browser.on('down', function(service) {
         // DEBUG
         console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service down");
 
@@ -298,11 +266,6 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
             }
         }
     });
-    browser.on('error', function(err) {
-        browser.stop();
-        self.emit('error', undefined);
-    });
-
     browser.start();
 }
 

From d7a07f34bc9d3727c8aa1a7c5a24014c0235212a Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 19 Jun 2018 12:45:10 -0400
Subject: [PATCH 34/62] JDISC: Fixed bugs in MDNSReg and JRegistrar, MQTTReg
 working well, issues with MDNSReg..

---
 lib/jdiscovery/apps/tester.js  | 12 ++++++++++--
 lib/jdiscovery/jregistrar.js   | 11 ++++++-----
 lib/jdiscovery/mdnsregistry.js |  6 +++---
 3 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index 12c942f2..dad4a519 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -40,19 +40,27 @@ reggie.on('error', function(err) {
 
 // Custom attributes/discoveries
 if (type === 'device') {
+    let i = 0;
     let f = (x) => {
         reggie.setAttributes({ secret : Math.random().toString(16) });
-        setTimeout(f, 10000);
+        ++i;
+        if(i < 5) {
+            setTimeout(f, 10000);
+        } else {
+            reggie.removeAttributes({secret : null});
+        }
     }
     f();
 } else if (type === 'fog') {
+    
+
     reggie.on('new-secret', function(id, secret) {
         console.log('NEW-SECRET: id: ' + id + ' secret: ' + secret);
     });
     reggie.on('no-more-secret', function(id) {
         console.log('NO-MORE-SECRET: id: ' + id);
     });
-    reggie.discoverAttributes({ device: { secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
+    reggie.discoverAttributes({ device: { status: {online: 'online', offline: 'offline'}, secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
 }    
 /*
 
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index aa3f7a4e..b0583c81 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -167,11 +167,11 @@ Registrar.prototype.registerAndDiscover = function(options) {
             self.emit('error', info);
         });
         x.on('discovery', function(attr, event, id, data, seqval) {
-            self._respondToDiscoveryEvent.call(self, attr, event, self.id, data, seqval, x.protocol);
+            self._respondToDiscoveryEvent.call(self, attr, event, id, data, seqval, x.protocol);
         });
 
         x.on('attr-removed', function(attr, event, id, seqval) {
-            self._respondToRemovalEvent.call(self, attr, event, self.id, seqval, x.protocol);
+            self._respondToRemovalEvent.call(self, attr, event, id, seqval, x.protocol);
         });
     });
 
@@ -290,7 +290,7 @@ Registrar.prototype.quit = function() {
 Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
     // DUBUGGING: Print shit
-    console.log("DEBUG: Registar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
+    console.log("DEBUG: Registrar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
 
     // Update discovery table
     if (!this.dt.hasOwnProperty(id)) {
@@ -353,8 +353,9 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
     // DUBUGGING: Print shit
     console.log("DEBUG: Registar._respondToRemovalEvent: ", event, id, seqval, protocol);
 
+    // check dt first
     if (  !this.dt.hasOwnProperty(id)
-       || !this.dt.id.hasOwnProperty(attr))
+       || !this.dt[id]['attrs'].hasOwnProperty(attr))
         return;
 
     if ( !this.dt[id]['attrs'][attr].hasOwnProperty('seqval')
@@ -362,7 +363,7 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
        || (  (this.dt[id]['attrs'][attr]['seqval'] > (Number.MAX_SAFE_INTEGER/2))
           && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
     {
-        delete this.dt.id.attr;
+        delete this.dt[id]['attrs'][attr];
         this.emit(event, id, protocol);
     }
 }
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 199263b9..950d9348 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -1,4 +1,4 @@
-var bonjour = require('bonjour'),
+var bonjour = require('bonjour')(),
     constants = require('../jamserver/constants'),
     Registry = require('./registry');
 
@@ -50,10 +50,10 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                 bonjour.find({type : this.app + '-' + (x) + '-attrrem'});
             this.attrRemovalBrowsers[x].on('up', function(service) {
                 // ignore our own services
-                if (service.name == self.id) {
+                if (service.name === self.id) {
                     return;
                 }
-                // update attrChangeCache
+                // update attrRemovalCache
                 let attrs = JSON.parse(service.txt.data);
                 let seqval = service.txt.seqval;
                 for (var attr in attrs)

From 7dbe1e67fe544a42d0f990cd18759186fc5469a0 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 20 Jun 2018 12:48:39 -0400
Subject: [PATCH 35/62] JDISC: MDNSReg: Fixed attrRemovalAd reception delay
 error

---
 lib/jdiscovery/apps/tester.js  |  1 +
 lib/jdiscovery/jregistrar.js   |  6 +++--
 lib/jdiscovery/mdnsregistry.js | 44 ++++++++++++++++++++++++----------
 3 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index dad4a519..a60f5965 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -48,6 +48,7 @@ if (type === 'device') {
             setTimeout(f, 10000);
         } else {
             reggie.removeAttributes({secret : null});
+            reggie.quit();
         }
     }
     f();
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar.js
index b0583c81..4d290461 100644
--- a/lib/jdiscovery/jregistrar.js
+++ b/lib/jdiscovery/jregistrar.js
@@ -276,8 +276,10 @@ Registrar.prototype._modifyDattributes = function(fun, dattrs) {
  */
 Registrar.prototype.quit = function() {
     let seqval = this._getSeqVal();
-    [this.mqttRegistry, this.mdnsRegistry].map(
-        x => {x.quit(seqval);}
+    [this.mqttRegistry, this.mdnsRegistry].filter(x => x).map(
+        (x) => {
+            x.quit(seqval);
+        }
     );
 }
 
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 950d9348..3356e62a 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -21,6 +21,7 @@ function MDNSRegistry(app, type, id, port) {
         cloud: {}
     }
 
+    this.attrRemovalAd = null;
     this.attrRemovalBrowsers = {};
     this.attrRemovalCache = {};
 
@@ -49,6 +50,9 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
             this.attrRemovalBrowsers[x] =
                 bonjour.find({type : this.app + '-' + (x) + '-attrrem'});
             this.attrRemovalBrowsers[x].on('up', function(service) {
+                // DEBUG
+                console.log('DEBUG: MDNSRegistry.attrRemovalBrowser[' + x + ']');
+                console.log(service);
                 // ignore our own services
                 if (service.name === self.id) {
                     return;
@@ -75,7 +79,9 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
  *          on the receiving node
  */
 MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
+    // DEBUG
     console.log('DEBUG: MDNSRegistry.setAttributes');
+
     if (this.started) {
         this._createAdvertisements(attrs, seqval);
     } else {
@@ -89,9 +95,15 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
  * Removes attributes by stopping the advertisements
  */
 MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
+    // DEBUG
+    console.log('DEBUG: MDNSRegistry.removeAttributes');
 
-    // create attrrem advertisement
-    let attrRemovalAd = bonjour.publish(
+    // clean & create attrrem advertisement
+    if (this.attrRemovalAd) {
+        this.attrRemovalAd.stop();
+        this.attrRemovalAd = null;
+    }
+    this.attrRemovalAd = bonjour.publish(
         {
             name    :   this.id,
             port    :   this.port,
@@ -102,18 +114,26 @@ MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
                         }
         }
     );
-    attrRemovalAd.start();
+    this.attrRemovalAd.start();
 
-    for (var attr in attrs) {
-        if (attrs.hasOwnProperty(attr)) {
-            if (this.attrsToAdvertise[attr] && this.ads[attr]) {
-                this.ads[attr].stop();
-                delete this.attrsToAdvertise[attr];
-                delete this.ads[attr];
+    setTimeout(
+        () => {
+            for (var attr in attrs) {
+                if (attrs.hasOwnProperty(attr)) {
+                    if (this.attrsToAdvertise[attr] && this.ads[attr]) {
+                        this.ads[attr].stop();
+                        delete this.attrsToAdvertise[attr];
+                        delete this.ads[attr];
+                    }
+                }
             }
-        }
-    }
-    attrRemovalAd.stop();
+            if (this.attrRemovalAd) {
+                this.attrRemovalAd.stop();
+                this.attrRemovalAd = null;
+            }
+        },
+        5000
+    );
 }
 /**
  * Discovers attributes by starting browsers

From 7314fcf56f49a6270e5252a86409dbcf4d3c7890 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 21 Jun 2018 11:52:01 -0400
Subject: [PATCH 36/62] JDISC: MDNSReg: Made mdns attr removal scheme more
 robust

---
 lib/jdiscovery/apps/tester.js  |  2 +-
 lib/jdiscovery/mdnsregistry.js | 44 ++++++++++++++++++++--------------
 2 files changed, 27 insertions(+), 19 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index a60f5965..fc141663 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -61,7 +61,7 @@ if (type === 'device') {
     reggie.on('no-more-secret', function(id) {
         console.log('NO-MORE-SECRET: id: ' + id);
     });
-    reggie.discoverAttributes({ device: { status: {online: 'online', offline: 'offline'}, secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
+    reggie.discoverAttributes({ device: { secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
 }    
 /*
 
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 3356e62a..dc5603d0 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -23,7 +23,6 @@ function MDNSRegistry(app, type, id, port) {
 
     this.attrRemovalAd = null;
     this.attrRemovalBrowsers = {};
-    this.attrRemovalCache = {};
 
     this.started = false;
 }
@@ -52,17 +51,37 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
             this.attrRemovalBrowsers[x].on('up', function(service) {
                 // DEBUG
                 console.log('DEBUG: MDNSRegistry.attrRemovalBrowser[' + x + ']');
-                console.log(service);
+                console.log(JSON.stringify(service));
+                console.log(JSON.stringify(self.attrsToBrowse));
+
                 // ignore our own services
                 if (service.name === self.id) {
                     return;
                 }
-                // update attrRemovalCache
+                // notify jregistrar of lost attributes/disconnections
                 let attrs = JSON.parse(service.txt.data);
                 let seqval = service.txt.seqval;
-                for (var attr in attrs)
-                    if (attrs.hasOwnProperty(attr))
-                        self.attrRemovalCache[attr] = seqval;
+                attrs.filter(
+                    (attr) => {
+                        return self.attrsToBrowse[x][attr];
+                    }
+                ).map(
+                    (attr) => {
+                        // DEBUG
+                        console.log('TYPE: ' + x + ' ATTR: ' + attr);
+
+                        // propagate advertised attribute removals
+                        if(attr === 'status') {
+                            self.emit('discovery', attr,
+                                self.attrsToBrowse[x][attr].offline,
+                                service.name, 'offline', undefined);
+                        } else {
+                            self.emit('attr-removed', attr,
+                                self.attrsToBrowse[x][attr].onRemove,
+                                service.name, seqval);
+                        }
+                    }
+                );
             });
             this.attrRemovalBrowsers[x].on('down', function(service) {
                 // do nothing!
@@ -264,26 +283,15 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
     browser.on('down', function(service) {
         // DEBUG
         console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service down");
-
         // ignore our own services
         if (service.name === self.id) {
             return;
         }
+        // do nothing unless it s a status offline notification
         if(attr === 'status') {
             self.emit('discovery', attr,
                 self.attrsToBrowse[type][attr].offline,
                 service.name, 'offline', undefined);
-        } else {
-            // for m-o disconnections: do nothing
-            // for service downs associated with attr value changes: do nothing
-            // for advertised attr removals: propagate
-            if (self.attrRemovalCache.hasOwnProperty(attr)) {
-                let seqval = self.attrRemovalCache[attr];
-                delete self.attrRemovalCache[attr];
-                self.emit('attr-removed', attr,
-                    self.attrsToBrowse[type][attr].onRemove,
-                    service.name, seqval);
-            }
         }
     });
     browser.start();

From ef724418239245d07e14bbeea0e973263c946692 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 21 Jun 2018 12:51:45 -0400
Subject: [PATCH 37/62] JDISC: Moving discovery services to secondary process
 (in progress)

---
 lib/jdiscovery/jregistrar-abdomen.js          | 27 ++++++
 lib/jdiscovery/jregistrar-head.js             | 83 +++++++++++++++++++
 .../{jregistrar.js => jregistrar-tail.js}     |  0
 lib/jdiscovery/mqttregistry.js                |  6 --
 4 files changed, 110 insertions(+), 6 deletions(-)
 create mode 100644 lib/jdiscovery/jregistrar-abdomen.js
 create mode 100644 lib/jdiscovery/jregistrar-head.js
 rename lib/jdiscovery/{jregistrar.js => jregistrar-tail.js} (100%)

diff --git a/lib/jdiscovery/jregistrar-abdomen.js b/lib/jdiscovery/jregistrar-abdomen.js
new file mode 100644
index 00000000..38b9680c
--- /dev/null
+++ b/lib/jdiscovery/jregistrar-abdomen.js
@@ -0,0 +1,27 @@
+const   EventEmitter = require('events'),
+        RegistrarTail = require('./jregistrar-tail.js');
+
+function RegistrarAbdomen(app, type, id, port, config) {
+    
+    this.registrarTail = new RegistrarTail(app, type, id, port, config);
+
+    registrarTail.on('discovery', (...) => {
+        process.send({...});
+    });
+    registrarTail.on('attr-removed', (...) => {
+        process.send({...});
+    });
+
+    process.on('message', (m) => {
+        for(f in m)
+            if(m.hasOwnProperty(f))
+                this.registrarTail[f](m[f]);
+    });
+}
+
+/* Registrar inherits from EventEmitter */
+RegistrarAbdomen.prototype = Object.create(EventEmitter.prototype);
+RegistrarAbdomen.prototype.constructor = RegistrarAbdomen;
+
+/* exports */
+module.exports = RegistrarAbdomen;
diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
new file mode 100644
index 00000000..b9cc4ae4
--- /dev/null
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -0,0 +1,83 @@
+const   EventEmitter = require('events'),
+        cp = require('child_process');
+
+/**
+ * Registrar Class
+ *      This class is the interface between the application
+ *      and the MQTT, mDNS registries
+ */
+
+function RegistrarHead(app, type, id, port, config) {
+    
+    this.registrarAbdomen = cp.fork('./jregistrar-abdomen.js', [app, type, id, port, config]);
+
+    let self = this;
+    registrarAbdomen.on('message', (m) => {
+        // TODO
+        self.emit(...);
+    }
+}
+
+/* Registrar inherits from EventEmitter */
+RegistrarHead.prototype = Object.create(EventEmitter.prototype);
+RegistrarHead.prototype.constructor = RegistrarHead;
+
+/**
+ * REGISTRAR INTERFACE METHODS
+ * __JDISCOVERY_EXTERNAL_API__
+ */
+
+/**
+ * Register a node on the network, and discover other nodes.
+ * `options` is an optional parameter
+ * `options` include:
+ *   attrsToSet: key/value pair as in this.setAttributes
+ *   attrsToDiscover: as in this.discoverAttributes
+ */
+RegistrarHead.prototype.registerAndDiscover = function(options) {
+    this.registrarAbdomen.send({ registerAndDiscover : options });
+}
+
+/**
+ * Add custom, discoverable attributes to this node
+ * attrs is an object of key value pairs
+ */
+RegistrarHead.prototype.setAttributes = function(attrs) {
+    this.registrarAbdomen.send({ setAttributes : attrs });
+}
+RegistrarHead.prototype.removeAttributes = function(attrs) {
+    this.registrarAbdomen.send({ removeAttributes : attrs });
+}
+
+/**
+ * Specify attributes to be discovered.
+ * dattrs can have one of the following forms:
+ * (a)
+ *    {
+ *        all: {attr: event, ...}, // discover these attributes for all nodes
+ *        device: {attr: event, ...}, // discover these attributes just for devices
+ *        fog: {attr: event, ...}, // discover these attributes just for fogs
+ *        cloud: {attr: event, ...} // discover these attributes just for clouds
+ *    }
+ * (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
+ *
+ * For the status attribute, the format is:
+ *      status: {
+ *          online: 'fog-up',
+ *          offline: 'fog-down'
+ *      }
+ * Whereas for custom attributes, the format is:
+ *      is_a_phone: {
+ *          onAdd: 'phone-found'
+ *          onRemove: 'phone-lost'
+ *      }
+ */
+RegistrarHead.prototype.discoverAttributes = function(dattrs) {
+    this.registrarAbdomen.send({ discoverAttributes : dattrs });
+}
+RegistrarHead.prototype.stopDiscoveringAttributes = function(dattrs) {
+    this.registrarAbdomen.send({ stopDiscoveringAttributes : dattrs });
+}
+
+/* exports */
+module.exports = RegistrarHead;
diff --git a/lib/jdiscovery/jregistrar.js b/lib/jdiscovery/jregistrar-tail.js
similarity index 100%
rename from lib/jdiscovery/jregistrar.js
rename to lib/jdiscovery/jregistrar-tail.js
diff --git a/lib/jdiscovery/mqttregistry.js b/lib/jdiscovery/mqttregistry.js
index 749d4e5f..0cb32dcc 100644
--- a/lib/jdiscovery/mqttregistry.js
+++ b/lib/jdiscovery/mqttregistry.js
@@ -54,12 +54,6 @@ MQTTRegistry.prototype.registerAndDiscover = function() {
     var self = this;
 
     /* connect event emitted on successful connection or reconnection */
-    /**
-     * TODO handle reconnect logic: on reconnect we aren t subscribed
-     * but when we do subscribe, we might get back our own LWT with offline status
-     * can this happen? if so, how should it be caught and handled?
-     * TODO
-     */
     this.client.on('connect', function (connack) {
 		/*
 		 * session is not present - subscriptions start from scratch

From 2ad0dccc7803eb9112fb68c3fbfb770c4c1d268b Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 21 Jun 2018 23:35:45 -0400
Subject: [PATCH 38/62] JDISC: Lib moved to another process (complete/working)

---
 lib/jdiscovery/apps/tester.js        | 101 +--------------------------
 lib/jdiscovery/jregistrar-abdomen.js |  41 +++++------
 lib/jdiscovery/jregistrar-head.js    |  66 +++++------------
 lib/jdiscovery/jregistrar-tail.js    |  52 +++++++-------
 4 files changed, 64 insertions(+), 196 deletions(-)

diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index fc141663..d66762f2 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -1,4 +1,4 @@
-var Registrar = require('../jregistrar'),
+var Registrar = require('../jregistrar-head.js'),
 globals = require('../../jamserver/constants').globals,
 events = require('events');
 
@@ -14,6 +14,7 @@ console.log();
 
 var reggie = new Registrar(app, type, id, port,
                            { protocols: { mqtt: false, mdns: true } });
+reggie.registerAndDiscover();
 
 // Default discoveries
 if (type === 'device') {
@@ -63,101 +64,3 @@ if (type === 'device') {
     });
     reggie.discoverAttributes({ device: { secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
 }    
-/*
-
-if (machType === 'device') {
-// we'll have devices announce if they are phones (iphone or android)
-// we'll say all devices are thermostats too...I know it doesn't make sense but it's just meant
-// to be demonstrative :P
-if (phoneType === 'iPhone') {
-    reggie.addAttributes({
-	iPhone: phoneNumber
-    });
-} else if (phoneType === 'Android') {
-    reggie.addAttributes({
-	android: 'psych, get an iPhone!'
-    });
-
-    // in 10 seconds, turn this android into an iphone
-    setTimeout(function() {
-	reggie.removeAttributes('android');
-	reggie.addAttributes({
-	    iPhone: phoneNumber
-	});
-    }, 5000);
-}
-reggie.addAttributes({
-    thermostat: function() {
-	// returns some random number, which we'll treat as the temperature
-	return 'Temperature: ' + Math.random() * 100;
-    }
-});
-} else if (machType === 'fog') {
-// since we'll have clouds discover fogs, we don't need fogs to discover clouds
-reggie.stopDiscoveringAttributes({
-    cloud: ['status']
-});
-
-reggie.discoverAttributes({
-    device: {
-	thermostat: {
-	    onAdd: 'thermo-added',
-	    onRemove: 'thermo-removed'
-	}
-    }
-});
-
-reggie.on('thermo-added', function(id, temp) {
-    console.log('DEVICE ' + id + ' is a thermostat with temperature ' + temp);
-});
-
-reggie.on('thermo-removed', function(id, temp) {
-    console.log('DEVICE ' + id + ' is no longer a thermostat');
-});
-} else {
-// maybe clouds want to discover fogs, and iphone devices
-reggie.discoverAttributes({
-    device: {
-	iPhone: {
-	    onAdd: 'iPhone-added',
-	    onRemove: 'iPhone-removed'
-	},
-	android: {
-	    onAdd: 'android-added',
-	    onRemove: 'android-removed'
-	}
-    },
-    fog: {
-	status: {
-	    online: 'fog-up',
-	    offline: 'fog-down'
-	}
-    }
-});
-
-reggie.on('fog-up', function(fogId, connInfo) {
-    console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
-});
-
-reggie.on('fog-down', function(fogId) {
-    console.log('FOG DOWN: id: ' + fogId);
-});
-
-reggie.on('iPhone-added', function(deviceId, phoneNumber) {
-    console.log('DEVICE ' + deviceId + ' is an iPhone with number ' + phoneNumber);
-});
-
-reggie.on('iPhone-removed', function(deviceId) {
-    console.log('DEVICE ' + deviceId + ' is no longer an iPhone');
-});
-
-reggie.on('android-added', function(deviceId, phoneNumber) {
-    console.log('DEVICE ' + deviceId + ' is an Android with number: ' + phoneNumber);
-});
-
-reggie.on('android-removed', function(deviceId) {
-    console.log('DEVICE ' + deviceId + ' is no longer an Android');
-});
-}
-*/
-reggie.registerAndDiscover();
diff --git a/lib/jdiscovery/jregistrar-abdomen.js b/lib/jdiscovery/jregistrar-abdomen.js
index 38b9680c..b109c30c 100644
--- a/lib/jdiscovery/jregistrar-abdomen.js
+++ b/lib/jdiscovery/jregistrar-abdomen.js
@@ -1,27 +1,20 @@
-const   EventEmitter = require('events'),
-        RegistrarTail = require('./jregistrar-tail.js');
+const   RegistrarTail = require('./jregistrar-tail.js');
 
-function RegistrarAbdomen(app, type, id, port, config) {
-    
-    this.registrarTail = new RegistrarTail(app, type, id, port, config);
+[,, app, type, id, port, config] = process.argv;
+this.registrarTail = new RegistrarTail(app, type, id, port, JSON.parse(config));
 
-    registrarTail.on('discovery', (...) => {
-        process.send({...});
-    });
-    registrarTail.on('attr-removed', (...) => {
-        process.send({...});
-    });
+this.registrarTail.on('appNotifLess', (event, id, protocol) => {
+    process.send({ appNotifLess: {event: event, id: id, protocol: protocol }});
+});
+this.registrarTail.on('appNotifMore', (event, id, data, protocol) => {
+    process.send({ appNotifMore: {event: event, id: id, data: data, protocol: protocol }});
+});
 
-    process.on('message', (m) => {
-        for(f in m)
-            if(m.hasOwnProperty(f))
-                this.registrarTail[f](m[f]);
-    });
-}
-
-/* Registrar inherits from EventEmitter */
-RegistrarAbdomen.prototype = Object.create(EventEmitter.prototype);
-RegistrarAbdomen.prototype.constructor = RegistrarAbdomen;
-
-/* exports */
-module.exports = RegistrarAbdomen;
+process.on('message', (m) => {
+    // XXX
+    console.log('Received call to: ');
+    console.log(m);
+    for(f in m)
+        if(m.hasOwnProperty(f))
+            this.registrarTail[f](m[f]);
+});
diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
index b9cc4ae4..fe6484fd 100644
--- a/lib/jdiscovery/jregistrar-head.js
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -1,21 +1,23 @@
 const   EventEmitter = require('events'),
         cp = require('child_process');
 
-/**
- * Registrar Class
- *      This class is the interface between the application
- *      and the MQTT, mDNS registries
- */
-
 function RegistrarHead(app, type, id, port, config) {
-    
-    this.registrarAbdomen = cp.fork('./jregistrar-abdomen.js', [app, type, id, port, config]);
 
+    this.registrarAbdomen = cp.fork(
+        './jregistrar-abdomen.js',
+        [app, type, id, port, JSON.stringify(config)]
+    );
     let self = this;
-    registrarAbdomen.on('message', (m) => {
-        // TODO
-        self.emit(...);
-    }
+    this.registrarAbdomen.on('message', (m) => {
+        let e;
+        if(m.hasOwnProperty('appNotifLess')) {
+            e = m['appNotifLess'];
+            self.emit(e.event, e.id, e.protocol);
+        } else if(m.hasOwnProperty('appNotifMore')) {
+            e = m['appNotifMore'];
+            self.emit(e.event, e.id, e.data, e.protocol);
+        }
+    });
 }
 
 /* Registrar inherits from EventEmitter */
@@ -27,21 +29,10 @@ RegistrarHead.prototype.constructor = RegistrarHead;
  * __JDISCOVERY_EXTERNAL_API__
  */
 
-/**
- * Register a node on the network, and discover other nodes.
- * `options` is an optional parameter
- * `options` include:
- *   attrsToSet: key/value pair as in this.setAttributes
- *   attrsToDiscover: as in this.discoverAttributes
- */
 RegistrarHead.prototype.registerAndDiscover = function(options) {
-    this.registrarAbdomen.send({ registerAndDiscover : options });
+    this.registrarAbdomen.send({ registerAndDiscover : ((options)?(options):null) });
 }
 
-/**
- * Add custom, discoverable attributes to this node
- * attrs is an object of key value pairs
- */
 RegistrarHead.prototype.setAttributes = function(attrs) {
     this.registrarAbdomen.send({ setAttributes : attrs });
 }
@@ -49,29 +40,6 @@ RegistrarHead.prototype.removeAttributes = function(attrs) {
     this.registrarAbdomen.send({ removeAttributes : attrs });
 }
 
-/**
- * Specify attributes to be discovered.
- * dattrs can have one of the following forms:
- * (a)
- *    {
- *        all: {attr: event, ...}, // discover these attributes for all nodes
- *        device: {attr: event, ...}, // discover these attributes just for devices
- *        fog: {attr: event, ...}, // discover these attributes just for fogs
- *        cloud: {attr: event, ...} // discover these attributes just for clouds
- *    }
- * (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
- *
- * For the status attribute, the format is:
- *      status: {
- *          online: 'fog-up',
- *          offline: 'fog-down'
- *      }
- * Whereas for custom attributes, the format is:
- *      is_a_phone: {
- *          onAdd: 'phone-found'
- *          onRemove: 'phone-lost'
- *      }
- */
 RegistrarHead.prototype.discoverAttributes = function(dattrs) {
     this.registrarAbdomen.send({ discoverAttributes : dattrs });
 }
@@ -79,5 +47,9 @@ RegistrarHead.prototype.stopDiscoveringAttributes = function(dattrs) {
     this.registrarAbdomen.send({ stopDiscoveringAttributes : dattrs });
 }
 
+RegistrarHead.prototype.quit = function() {
+    this.registrarAbdomen.send({ quit : null });
+}
+
 /* exports */
 module.exports = RegistrarHead;
diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index 4d290461..b9869492 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -1,9 +1,9 @@
-var EventEmitter = require('events'),
-    globals = require('../jamserver/constants').globals,
-    constants = require('../jamserver/constants'),
-    MQTTRegistry = require('./mqttregistry'),
-    MDNSRegistry = require('./mdnsregistry'),
-    os = require('os');
+const   EventEmitter = require('events'),
+        globals = require('../jamserver/constants').globals,
+        constants = require('../jamserver/constants'),
+        MQTTRegistry = require('./mqttregistry'),
+        MDNSRegistry = require('./mdnsregistry'),
+        os = require('os');
 
 /**
  * Registrar Class
@@ -11,7 +11,7 @@ var EventEmitter = require('events'),
  *      and the MQTT, mDNS registries
  */
 
-function Registrar(app, type, id, port, config) {
+function RegistrarTail(app, type, id, port, config) {
     // the name of the application
     this.app = app;
     // the type of the machine the registar is running on (device, fog, or cloud)
@@ -89,8 +89,8 @@ function Registrar(app, type, id, port, config) {
 }
 
 /* Registrar inherits from EventEmitter */
-Registrar.prototype = Object.create(EventEmitter.prototype);
-Registrar.prototype.constructor = Registrar;
+RegistrarTail.prototype = Object.create(EventEmitter.prototype);
+RegistrarTail.prototype.constructor = RegistrarTail;
 
 /**
  * REGISTRAR INTERFACE METHODS
@@ -104,7 +104,7 @@ Registrar.prototype.constructor = Registrar;
  *   attrsToSet: key/value pair as in this.setAttributes
  *   attrsToDiscover: as in this.discoverAttributes
  */
-Registrar.prototype.registerAndDiscover = function(options) {
+RegistrarTail.prototype.registerAndDiscover = function(options) {
 
     if (this.started)
         return;
@@ -204,13 +204,13 @@ Registrar.prototype.registerAndDiscover = function(options) {
  * Add custom, discoverable attributes to this node
  * attrs is an object of key value pairs
  */
-Registrar.prototype.setAttributes = function(attrs) {
+RegistrarTail.prototype.setAttributes = function(attrs) {
     this._modifyAttributes("setAttributes", attrs, this._getSeqVal());
 }
-Registrar.prototype.removeAttributes = function(attrs) {
+RegistrarTail.prototype.removeAttributes = function(attrs) {
     this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
-Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
+RegistrarTail.prototype._modifyAttributes = function(fun, attrs, seqval) {
     [this.mqttRegistry, this.mdnsRegistry].map(
         x => {if(x) x[fun](attrs, seqval);}
     );
@@ -239,11 +239,11 @@ Registrar.prototype._modifyAttributes = function(fun, attrs, seqval) {
  *          onRemove: 'phone-lost'
  *      }
  */
-Registrar.prototype.discoverAttributes = function(dattrs) {
+RegistrarTail.prototype.discoverAttributes = function(dattrs) {
     dattrs = this._formatDattributes(dattrs);
     this._modifyDattributes("discoverAttributes", dattrs);
 }
-Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
+RegistrarTail.prototype.stopDiscoveringAttributes = function(dattrs) {
     dattrs = this._formatDattributes(dattrs);
     this._modifyDattributes("stopDiscoveringAttributes", dattrs);
 }
@@ -251,7 +251,7 @@ Registrar.prototype.stopDiscoveringAttributes = function(dattrs) {
  * Marshall dattrs to the following format (expected by the registries)
  * { 'device' : {...}, 'fog' : {...}, 'cloud' : {...} }
  */
-Registrar.prototype._formatDattributes = function(dattrs) {
+RegistrarTail.prototype._formatDattributes = function(dattrs) {
     if(!(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud))
         dattrs = { 'all' : dattrs };
     ['device', 'fog', 'cloud'].map(
@@ -266,7 +266,7 @@ Registrar.prototype._formatDattributes = function(dattrs) {
         delete dattrs.all;
     return dattrs;
 }
-Registrar.prototype._modifyDattributes = function(fun, dattrs) {
+RegistrarTail.prototype._modifyDattributes = function(fun, dattrs) {
     [this.mqttRegistry, this.mdnsRegistry].map(
         x => {if(x) x[fun](dattrs);}
     );
@@ -274,7 +274,7 @@ Registrar.prototype._modifyDattributes = function(fun, dattrs) {
 /**
  * Exit from network cleanly
  */
-Registrar.prototype.quit = function() {
+RegistrarTail.prototype.quit = function() {
     let seqval = this._getSeqVal();
     [this.mqttRegistry, this.mdnsRegistry].filter(x => x).map(
         (x) => {
@@ -289,7 +289,7 @@ Registrar.prototype.quit = function() {
 /**
  * Upon receipt of a discovery event, pass it onto the rest of the application if it is not a duplicate
  */
-Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
+RegistrarTail.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
     // DUBUGGING: Print shit
     console.log("DEBUG: Registrar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
@@ -328,7 +328,7 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
         for(var key in this.dt[id]['attrs'])
             if(this.dt[id]['attrs'].hasOwnProperty(key))
                 this.dt[id]['attrs'][key]['seqval'] = -1;
-        this.emit(event, id, protocol);
+        this.emit('appNotifLess', event, id, protocol);
         return;
     }
 
@@ -341,7 +341,7 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
         this.dt[id]['attrs'][attr]['data'] = data;
         this.dt[id]['attrs'][attr]['seqval'] = seqval;
 
-        this.emit(event, id, data, protocol);
+        this.emit('appNotifMore', event, id, data, protocol);
     } else {
         return;
     }
@@ -350,7 +350,7 @@ Registrar.prototype._respondToDiscoveryEvent = function(attr, event, id, data, s
 /**
  * Upon receipt of an attribute removal event, pass it onto the rest of the application
  */
-Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, protocol) {
+RegistrarTail.prototype._respondToRemovalEvent = function(attr, event, id, seqval, protocol) {
 
     // DUBUGGING: Print shit
     console.log("DEBUG: Registar._respondToRemovalEvent: ", event, id, seqval, protocol);
@@ -366,14 +366,14 @@ Registrar.prototype._respondToRemovalEvent = function(attr, event, id, seqval, p
           && (seqval < (Number.MAX_SAFE_INTEGER/1024))))
     {
         delete this.dt[id]['attrs'][attr];
-        this.emit(event, id, protocol);
+        this.emit('appNotifLess', event, id, protocol);
     }
 }
 
 /**
  * returns next seq number for event ordering
  */
-Registrar.prototype._getSeqVal = function() {
+RegistrarTail.prototype._getSeqVal = function() {
     if (this.seqval == Number.MAX_SAFE_INTEGER) {
         this.seqval = 0;
     }
@@ -383,7 +383,7 @@ Registrar.prototype._getSeqVal = function() {
 /**
  * returns the IPv4 address of the node
  */
-Registrar.prototype._getIPv4Address = function() {
+RegistrarTail.prototype._getIPv4Address = function() {
     var niaddrs = os.networkInterfaces();
     for (var ni in niaddrs) {
         nielm = niaddrs[ni];
@@ -396,4 +396,4 @@ Registrar.prototype._getIPv4Address = function() {
 }
 
 /* exports */
-module.exports = Registrar;
+module.exports = RegistrarTail;

From 318d23457f890e75ea83dd225097bc1ce587d546 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 26 Jun 2018 13:07:55 -0400
Subject: [PATCH 39/62] JDISC: Updated dependencies for script install

---
 lib/jdiscovery/package-lock.json | 506 +++++++++++++------------------
 lib/jdiscovery/package.json      |  37 +--
 2 files changed, 218 insertions(+), 325 deletions(-)

diff --git a/lib/jdiscovery/package-lock.json b/lib/jdiscovery/package-lock.json
index 7bec6d84..d0dd2278 100644
--- a/lib/jdiscovery/package-lock.json
+++ b/lib/jdiscovery/package-lock.json
@@ -1,46 +1,64 @@
 {
   "name": "jdiscovery",
-  "version": "0.0.7",
+  "version": "1.0.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
+    "array-flatten": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz",
+      "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY="
+    },
+    "async-limiter": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
       "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
     },
-    "bindings": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
-      "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
-    },
     "bl": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.1.tgz",
-      "integrity": "sha1-ysMo977kVzDUBLaSID/LWQ4XLV4=",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
+      "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
+      "requires": {
+        "readable-stream": "2.3.6",
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "bonjour": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+      "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
       "requires": {
-        "readable-stream": "2.2.11"
+        "array-flatten": "2.1.1",
+        "deep-equal": "1.0.1",
+        "dns-equal": "1.0.0",
+        "dns-txt": "2.0.2",
+        "multicast-dns": "6.2.3",
+        "multicast-dns-service-types": "1.1.0"
       }
     },
     "brace-expansion": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
-      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "requires": {
         "balanced-match": "1.0.0",
         "concat-map": "0.0.1"
       }
     },
-    "bunyan": {
-      "version": "1.8.10",
-      "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.10.tgz",
-      "integrity": "sha1-IB/t0mxwgLYy9BYHL1OpC5pSmBw=",
-      "requires": {
-        "dtrace-provider": "0.8.3",
-        "moment": "2.18.1",
-        "mv": "2.1.1",
-        "safe-json-stringify": "1.0.4"
-      }
+    "buffer-from": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
+      "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
+    },
+    "buffer-indexof": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g=="
     },
     "callback-stream": {
       "version": "1.1.0",
@@ -48,7 +66,7 @@
       "integrity": "sha1-RwGlEmbwbgbqpx/BcjOCLYdfSQg=",
       "requires": {
         "inherits": "2.0.3",
-        "readable-stream": "2.2.11"
+        "readable-stream": "2.3.6"
       }
     },
     "commist": {
@@ -58,13 +76,6 @@
       "requires": {
         "leven": "1.0.2",
         "minimist": "1.2.0"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
-        }
       }
     },
     "concat-map": {
@@ -73,12 +84,13 @@
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "concat-stream": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
-      "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
       "requires": {
+        "buffer-from": "1.1.0",
         "inherits": "2.0.3",
-        "readable-stream": "2.2.11",
+        "readable-stream": "2.3.6",
         "typedarray": "0.0.6"
       }
     },
@@ -87,48 +99,48 @@
       "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
     },
-    "dtrace-provider": {
-      "version": "0.8.3",
-      "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.3.tgz",
-      "integrity": "sha1-uhv8ZJMoXM/PxqtpzVxh10wqQ78=",
-      "optional": true,
+    "deep-equal": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+      "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
+    },
+    "dns-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+      "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0="
+    },
+    "dns-packet": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
+      "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+      "requires": {
+        "ip": "1.1.5",
+        "safe-buffer": "5.1.2"
+      }
+    },
+    "dns-txt": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+      "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
       "requires": {
-        "nan": "2.6.2"
+        "buffer-indexof": "1.1.1"
       }
     },
     "duplexify": {
-      "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz",
-      "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=",
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz",
+      "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==",
       "requires": {
-        "end-of-stream": "1.0.0",
+        "end-of-stream": "1.4.1",
         "inherits": "2.0.3",
-        "readable-stream": "2.2.11",
+        "readable-stream": "2.3.6",
         "stream-shift": "1.0.0"
-      },
-      "dependencies": {
-        "end-of-stream": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz",
-          "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=",
-          "requires": {
-            "once": "1.3.3"
-          }
-        },
-        "once": {
-          "version": "1.3.3",
-          "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
-          "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
-          "requires": {
-            "wrappy": "1.0.2"
-          }
-        }
       }
     },
     "end-of-stream": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz",
-      "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=",
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
       "requires": {
         "once": "1.4.0"
       }
@@ -138,25 +150,17 @@
       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
       "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
     },
-    "extend-shallow": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-      "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-      "requires": {
-        "is-extendable": "0.1.1"
-      }
-    },
     "fs.realpath": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
       "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
     },
     "glob": {
-      "version": "6.0.4",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
-      "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
-      "optional": true,
+      "version": "7.1.2",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+      "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
       "requires": {
+        "fs.realpath": "1.0.0",
         "inflight": "1.0.6",
         "inherits": "2.0.3",
         "minimatch": "3.0.4",
@@ -183,33 +187,13 @@
         "glob-parent": "3.1.0",
         "is-negated-glob": "1.0.0",
         "ordered-read-streams": "1.0.1",
-        "pumpify": "1.3.5",
-        "readable-stream": "2.2.11",
-        "remove-trailing-separator": "1.0.2",
-        "to-absolute-glob": "2.0.1",
+        "pumpify": "1.5.1",
+        "readable-stream": "2.3.6",
+        "remove-trailing-separator": "1.1.0",
+        "to-absolute-glob": "2.0.2",
         "unique-stream": "2.2.1"
-      },
-      "dependencies": {
-        "glob": {
-          "version": "7.1.2",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
-          "requires": {
-            "fs.realpath": "1.0.0",
-            "inflight": "1.0.6",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.4",
-            "once": "1.4.0",
-            "path-is-absolute": "1.0.1"
-          }
-        }
       }
     },
-    "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
-    },
     "help-me": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/help-me/-/help-me-1.1.0.tgz",
@@ -221,11 +205,6 @@
         "xtend": "4.0.1"
       }
     },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
-    },
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -240,20 +219,20 @@
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
       "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
+    "ip": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
+    },
     "is-absolute": {
-      "version": "0.2.6",
-      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
-      "integrity": "sha1-IN5p89uULvLYe5wto28XIjWxtes=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+      "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
       "requires": {
-        "is-relative": "0.2.1",
-        "is-windows": "0.2.0"
+        "is-relative": "1.0.0",
+        "is-windows": "1.0.2"
       }
     },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
-    },
     "is-extglob": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -273,25 +252,25 @@
       "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI="
     },
     "is-relative": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.2.1.tgz",
-      "integrity": "sha1-0n9MfVFtF1+2ENuEu+7yPDvJeqU=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+      "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
       "requires": {
-        "is-unc-path": "0.1.2"
+        "is-unc-path": "1.0.0"
       }
     },
     "is-unc-path": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-0.1.2.tgz",
-      "integrity": "sha1-arBTpyVzwQJQ/0FqOBTDUXivObk=",
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+      "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
       "requires": {
         "unc-path-regex": "0.1.2"
       }
     },
     "is-windows": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz",
-      "integrity": "sha1-3hqm1j6indJIc3tp8f+LgALSEIw="
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
     },
     "isarray": {
       "version": "1.0.0",
@@ -316,122 +295,63 @@
       "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz",
       "integrity": "sha1-kUS27ryl8dBoAWnxpncNzqYLdcM="
     },
-    "lockfile": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.3.tgz",
-      "integrity": "sha1-Jjj8OaAzHpysGgS3F5mTHJxQ33k="
-    },
-    "mdns": {
-      "version": "file:https:/registry.npmjs.org/mdns/-/mdns-2.3.4.tgz",
-      "integrity": "sha512-Z4WTKeTukCtJG53SS3BGNnsGkHdIXNZa9nwGMYeoohU1AjEBPS3c/1vIx95SEfeQKYduuOMTo1E4RfXDUt2ZYg==",
-      "requires": {
-        "bindings": "1.2.1",
-        "nan": "2.3.5"
-      },
-      "dependencies": {
-        "nan": {
-          "version": "2.3.5",
-          "bundled": true
-        }
-      }
-    },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
       "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "requires": {
-        "brace-expansion": "1.1.8"
+        "brace-expansion": "1.1.11"
       }
     },
     "minimist": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-      "optional": true
-    },
-    "mkdirp": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-      "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
-      "optional": true,
-      "requires": {
-        "minimist": "0.0.8"
-      }
-    },
-    "moment": {
-      "version": "2.18.1",
-      "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
-      "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=",
-      "optional": true
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
     },
     "mqtt": {
-      "version": "2.8.2",
-      "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.8.2.tgz",
-      "integrity": "sha512-UBkIR9ZyLvKcC/nWGzPm4qee8TjqZfZK8wVs5SCKxkn9ta81WYjlr4O3/2ayft67O5F+Kx/qLJoC3ovTJR3GSg==",
+      "version": "2.18.1",
+      "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-2.18.1.tgz",
+      "integrity": "sha512-p+RIMFsNb5z65/dy5beKgTnycd3+N8gQ+E2Jnx+0g0OoRza/LCXtUp/vEb3mgWJdljTU+5n4Lc3h0ya994zmVg==",
       "requires": {
         "commist": "1.0.0",
-        "concat-stream": "1.6.0",
-        "end-of-stream": "1.4.0",
+        "concat-stream": "1.6.2",
+        "end-of-stream": "1.4.1",
         "help-me": "1.1.0",
         "inherits": "2.0.3",
         "minimist": "1.2.0",
-        "mqtt-packet": "5.3.0",
-        "pump": "1.0.2",
-        "readable-stream": "2.2.11",
+        "mqtt-packet": "5.6.0",
+        "pump": "3.0.0",
+        "readable-stream": "2.3.6",
         "reinterval": "1.1.0",
-        "split2": "2.1.1",
-        "websocket-stream": "5.0.0",
+        "split2": "2.2.0",
+        "websocket-stream": "5.1.2",
         "xtend": "4.0.1"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
-        }
       }
     },
     "mqtt-packet": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.3.0.tgz",
-      "integrity": "sha1-B47VmuTAb+vzs+rKkLUOl+Jp8gY=",
+      "version": "5.6.0",
+      "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-5.6.0.tgz",
+      "integrity": "sha512-QECe2ivqcR1LRsPobRsjenEKAC3i1a5gmm+jNKJLrsiq9PaSQ18LlKFuxvhGxWkvGEPadWv6rKd31O4ICqS1Xw==",
       "requires": {
-        "bl": "1.2.1",
+        "bl": "1.2.2",
         "inherits": "2.0.3",
-        "process-nextick-args": "1.0.7",
-        "safe-buffer": "5.0.1"
+        "process-nextick-args": "2.0.0",
+        "safe-buffer": "5.1.2"
       }
     },
-    "mv": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
-      "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
-      "optional": true,
+    "multicast-dns": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+      "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
       "requires": {
-        "mkdirp": "0.5.1",
-        "ncp": "2.0.0",
-        "rimraf": "2.4.5"
+        "dns-packet": "1.3.1",
+        "thunky": "1.0.2"
       }
     },
-    "nan": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
-      "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=",
-      "optional": true
-    },
-    "ncp": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
-      "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=",
-      "optional": true
-    },
-    "node-localstorage": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-1.3.0.tgz",
-      "integrity": "sha1-LkNqro3Mms6XtDxlwWwNV3vgpVw=",
-      "requires": {
-        "write-file-atomic": "1.3.4"
-      }
+    "multicast-dns-service-types": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+      "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE="
     },
     "once": {
       "version": "1.4.0",
@@ -446,7 +366,7 @@
       "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
       "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
       "requires": {
-        "readable-stream": "2.2.11"
+        "readable-stream": "2.3.6"
       }
     },
     "path-dirname": {
@@ -460,40 +380,51 @@
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
     },
     "process-nextick-args": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+      "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
     },
     "pump": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz",
-      "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+      "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
       "requires": {
-        "end-of-stream": "1.4.0",
+        "end-of-stream": "1.4.1",
         "once": "1.4.0"
       }
     },
     "pumpify": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.3.5.tgz",
-      "integrity": "sha1-G2ccYZlAq8rqwK0OOjwWS+dgmTs=",
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
       "requires": {
-        "duplexify": "3.5.0",
+        "duplexify": "3.6.0",
         "inherits": "2.0.3",
-        "pump": "1.0.2"
+        "pump": "2.0.1"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+          "requires": {
+            "end-of-stream": "1.4.1",
+            "once": "1.4.0"
+          }
+        }
       }
     },
     "readable-stream": {
-      "version": "2.2.11",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.11.tgz",
-      "integrity": "sha512-h+8+r3MKEhkiVrwdKL8aWs1oc1VvBu33ueshOvS26RsZQ3Amhx/oO3TKe4lApSV9ueY6as8EAh7mtuFjdlhg9Q==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "requires": {
         "core-util-is": "1.0.2",
         "inherits": "2.0.3",
         "isarray": "1.0.0",
-        "process-nextick-args": "1.0.7",
-        "safe-buffer": "5.0.1",
-        "string_decoder": "1.0.2",
+        "process-nextick-args": "2.0.0",
+        "safe-buffer": "5.1.2",
+        "string_decoder": "1.1.1",
         "util-deprecate": "1.0.2"
       }
     },
@@ -503,39 +434,19 @@
       "integrity": "sha1-M2Hs+jymwYKDOA3Qu5VG85D17Oc="
     },
     "remove-trailing-separator": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz",
-      "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE="
-    },
-    "rimraf": {
-      "version": "2.4.5",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
-      "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
-      "optional": true,
-      "requires": {
-        "glob": "6.0.4"
-      }
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8="
     },
     "safe-buffer": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
-      "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c="
-    },
-    "safe-json-stringify": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz",
-      "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=",
-      "optional": true
-    },
-    "slide": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
-      "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
     },
     "split2": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/split2/-/split2-2.1.1.tgz",
-      "integrity": "sha1-eh9VHhdqkOzTNF9yRqDP4XXvT9A=",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz",
+      "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==",
       "requires": {
         "through2": "2.0.3"
       }
@@ -546,11 +457,11 @@
       "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
     },
     "string_decoder": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.2.tgz",
-      "integrity": "sha1-sp4fThEl+pehA4K4pTNze3SR4Xk=",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "requires": {
-        "safe-buffer": "5.0.1"
+        "safe-buffer": "5.1.2"
       }
     },
     "through2": {
@@ -558,7 +469,7 @@
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
       "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
       "requires": {
-        "readable-stream": "2.2.11",
+        "readable-stream": "2.3.6",
         "xtend": "4.0.1"
       }
     },
@@ -571,13 +482,17 @@
         "xtend": "4.0.1"
       }
     },
+    "thunky": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz",
+      "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E="
+    },
     "to-absolute-glob": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.1.tgz",
-      "integrity": "sha1-cMN1gFueMQXome6NvdapqhCPQHs=",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+      "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
       "requires": {
-        "extend-shallow": "2.0.1",
-        "is-absolute": "0.2.6",
+        "is-absolute": "1.0.0",
         "is-negated-glob": "1.0.0"
       }
     },
@@ -587,9 +502,9 @@
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
     },
     "ultron": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.0.tgz",
-      "integrity": "sha1-sHoualQagV/Go0zNRTO67DB8qGQ="
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
     },
     "unc-path-regex": {
       "version": "0.1.2",
@@ -611,15 +526,15 @@
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
     },
     "websocket-stream": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.0.0.tgz",
-      "integrity": "sha1-HRMY8Fds4goSVVNyEIrpQYpANjQ=",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.1.2.tgz",
+      "integrity": "sha512-lchLOk435iDWs0jNuL+hiU14i3ERSrMA0IKSiJh7z6X/i4XNsutBZrtqu2CPOZuA4G/zabiqVAos0vW+S7GEVw==",
       "requires": {
-        "duplexify": "3.5.0",
+        "duplexify": "3.6.0",
         "inherits": "2.0.3",
-        "readable-stream": "2.2.11",
-        "safe-buffer": "5.0.1",
-        "ws": "3.0.0",
+        "readable-stream": "2.3.6",
+        "safe-buffer": "5.1.2",
+        "ws": "3.3.3",
         "xtend": "4.0.1"
       }
     },
@@ -628,23 +543,14 @@
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
     },
-    "write-file-atomic": {
-      "version": "1.3.4",
-      "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
-      "integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
-      "requires": {
-        "graceful-fs": "4.1.11",
-        "imurmurhash": "0.1.4",
-        "slide": "1.1.6"
-      }
-    },
     "ws": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-3.0.0.tgz",
-      "integrity": "sha1-mN2wAFbIOQy3Ued4h4hJf5kQO2w=",
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+      "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
       "requires": {
-        "safe-buffer": "5.0.1",
-        "ultron": "1.1.0"
+        "async-limiter": "1.0.0",
+        "safe-buffer": "5.1.2",
+        "ultron": "1.1.1"
       }
     },
     "xtend": {
diff --git a/lib/jdiscovery/package.json b/lib/jdiscovery/package.json
index d41366f7..44996959 100644
--- a/lib/jdiscovery/package.json
+++ b/lib/jdiscovery/package.json
@@ -1,31 +1,18 @@
 {
   "name": "jdiscovery",
-  "version": "0.0.7",
-  "description": "For testing registration/discovery in JAM",
-  "main": "jregistrar.js",
-  "author": "Keith Strickling",
-  "license": "MIT",
-  "dependencies": {
-    "bunyan": "^1.8.10",
-    "lockfile": "^1.0.3",
-    "mdns": "file:mdns",
-    "mqtt": "^2.4.0",
-    "node-localstorage": "^1.3.0"
-  },
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/anrl/JAMScript-beta/lib/jdiscovery"
+  "version": "1.0.0",
+  "description": "Discovery service for JAMScript",
+  "main": "jregistrar-head.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
   },
   "keywords": [
-    "JAMScript",
-    "Registry",
-    "Device Discovery Framework"
+    "discovery"
   ],
-  "preferGlobal": true,
-  "files": [
-    "*.js",
-    "mdns",
-    "build",
-    "errlog"
-  ]
+  "author": "Keith Strickling, Michael Saraga",
+  "license": "MIT",
+  "dependencies": {
+    "bonjour": "^3.5.0",
+    "mqtt": "^2.18.1"
+  }
 }

From c8c77622ff6ce6e96c7b3421f8d7c529af2c1d34 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 26 Jun 2018 13:50:54 -0400
Subject: [PATCH 40/62] JDISC: Fixed path for child spawning

---
 lib/jdiscovery/jregistrar-head.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
index fe6484fd..a67db4a0 100644
--- a/lib/jdiscovery/jregistrar-head.js
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -4,7 +4,7 @@ const   EventEmitter = require('events'),
 function RegistrarHead(app, type, id, port, config) {
 
     this.registrarAbdomen = cp.fork(
-        './jregistrar-abdomen.js',
+        __dirname + '/jregistrar-abdomen.js',
         [app, type, id, port, JSON.stringify(config)]
     );
     let self = this;

From 83b0152d645696dda9a21f87cd1b413cd3d04469 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 27 Jun 2018 12:03:25 -0400
Subject: [PATCH 41/62] JDISC: JAM Integration and arg array marshalling in
 jreg

---
 lib/jamserver/jamlib.js           | 75 ++++++++++++++++---------------
 lib/jamserver/jamsys.js           |  2 +-
 lib/jdiscovery/jregistrar-tail.js | 14 ++++++
 3 files changed, 53 insertions(+), 38 deletions(-)

diff --git a/lib/jamserver/jamlib.js b/lib/jamserver/jamlib.js
index f70eb041..87c1fe9a 100644
--- a/lib/jamserver/jamlib.js
+++ b/lib/jamserver/jamlib.js
@@ -6,7 +6,7 @@
 
 // Load the global modules
 const mqtt = require('mqtt'),
-	  os = require('os'),
+      os = require('os'),
       globals = require('./constants').globals;
 
 // Do command line processing...
@@ -26,11 +26,12 @@ var machType = getMachineType(cmdopts);
 deviceParams.setItem('machType', machType);
 
 var reggie = new Registrar(cmdopts.app, machType, deviceParams.getItem('deviceId'),
-						cmdopts.port, {long: cmdopts.long, lat: cmdopts.lat}, { protocols: { mqtt: true, mdns: true } });
+                            cmdopts.port, { protocols: { mqtt: true, mdns: true } });
+//                          cmdopts.port, {long: cmdopts.long, lat: cmdopts.lat}, { protocols: { mqtt: true, mdns: true } });
 
 var jamsys = require('./jamsys');
 jamsys.init(reggie, machType, cmdopts.tags, cmdopts.iflow, cmdopts.oflow, deviceParams.getItem('deviceId'),
-								cmdopts.link, cmdopts.long, cmdopts.lat);
+                                cmdopts.link, cmdopts.long, cmdopts.lat);
 jamsys.setMQTT(getMachineAddr(), cmdopts.port);
 
 
@@ -39,60 +40,60 @@ jnode.init(reggie, machType);
 
 
 module.exports = new function() {
-	this.registerFuncs = registerFuncs;
-	this.run = run;
+    this.registerFuncs = registerFuncs;
+    this.run = run;
 }
 
 function run(callback) {
 
-	// Check the presence of the MQTT server.
-	// If not, report an error and quit
+    // Check the presence of the MQTT server.
+    // If not, report an error and quit
 
-	checkMQTTServer(function(present) {
-		if (!present) {
-			console.log("ERROR! Cannot connect to MQTT server. Exiting.");
-			process.exit(1);
-		}
+    checkMQTTServer(function(present) {
+        if (!present) {
+            console.log("ERROR! Cannot connect to MQTT server. Exiting.");
+            process.exit(1);
+        }
 
         if (machType === globals.NodeType.CLOUD) {
             reggie.registerAndDiscover();
-			jnode.startService();
-			jnode.startRunner();
-		} else {
-			jnode.doRegister();
-			jnode.startService();
-			jnode.startRunner();
+            jnode.startService();
+            jnode.startRunner();
+        } else {
+            jnode.doRegister();
+            jnode.startService();
+            jnode.startRunner();
 
-		}
+        }
 
-		if (callback !== undefined)
-			callback();
-	});
+        if (callback !== undefined)
+            callback();
+    });
 
 }
 
 function registerFuncs(machbox) {
 
-	// Register all callbacks in the machbox.
-	// These are functions registered by the application
-	fkeys = Object.keys(machbox.functions);
-	for (i in fkeys) {
-		tkey = fkeys[i];
-		jnode.registerCallback(tkey, machbox.functions[tkey], machbox.signatures[tkey]);
-	}
+    // Register all callbacks in the machbox.
+    // These are functions registered by the application
+    fkeys = Object.keys(machbox.functions);
+    for (i in fkeys) {
+        tkey = fkeys[i];
+        jnode.registerCallback(tkey, machbox.functions[tkey], machbox.signatures[tkey]);
+    }
 }
 
 // Check whether the MQTT server is up and running..
 function checkMQTTServer(callback) {
-	var tserv = mqtt.connect("mqtt://localhost:" + cmdopts.port );
-	tserv.on('connect', function() {
-		tserv.end();
-		callback(true);
-	});
-	tserv.on('offline', function() {
+    var tserv = mqtt.connect("mqtt://localhost:" + cmdopts.port );
+    tserv.on('connect', function() {
+        tserv.end();
+        callback(true);
+    });
+    tserv.on('offline', function() {
         tserv.end();
-		callback(false);
-	});
+        callback(false);
+    });
 }
 
 
diff --git a/lib/jamserver/jamsys.js b/lib/jamserver/jamsys.js
index e97b87f1..77f4283a 100644
--- a/lib/jamserver/jamsys.js
+++ b/lib/jamserver/jamsys.js
@@ -96,7 +96,7 @@ module.exports = new function() {
             this.setupAdvertisement(aname);
         }
 
-        reggie.addAttributes(attr);
+        reggie.setAttributes(attr);
     }
 
     // Advertise aname off
diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index b9869492..bfede580 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -211,6 +211,11 @@ RegistrarTail.prototype.removeAttributes = function(attrs) {
     this._modifyAttributes("removeAttributes", attrs, this._getSeqVal());
 }
 RegistrarTail.prototype._modifyAttributes = function(fun, attrs, seqval) {
+    if (attrs instanceof Array) {
+        attrs = attrs.reduce((acc, p) => { acc[p] = null; return acc; }, {});
+    } else if (!(attrs instanceof Object)) {
+        attrs = { attrs : null };
+    }
     [this.mqttRegistry, this.mdnsRegistry].map(
         x => {if(x) x[fun](attrs, seqval);}
     );
@@ -264,6 +269,15 @@ RegistrarTail.prototype._formatDattributes = function(dattrs) {
     )
     if(dattrs.all)
         delete dattrs.all;
+    ['device', 'fog', 'cloud'].map(
+        x => {
+            if (dattrs[x] instanceof Array) {
+                dattrs[x] = dattrs[x].reduce((acc, p) => { acc[p] = null; return acc; }, {});
+            } else if (!(dattrs[x] instanceof Object)) {
+                dattrs[x] = { dattrs[x] : null };
+            }
+        }
+    );
     return dattrs;
 }
 RegistrarTail.prototype._modifyDattributes = function(fun, dattrs) {

From 95b3b19b44a6e602b1d758bc9cee820e2ec3c6cd Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 27 Jun 2018 12:29:47 -0400
Subject: [PATCH 42/62] JDISC: Fixed array arg marshalling in jreg

---
 lib/jdiscovery/jregistrar-tail.js | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index bfede580..07a30612 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -213,8 +213,6 @@ RegistrarTail.prototype.removeAttributes = function(attrs) {
 RegistrarTail.prototype._modifyAttributes = function(fun, attrs, seqval) {
     if (attrs instanceof Array) {
         attrs = attrs.reduce((acc, p) => { acc[p] = null; return acc; }, {});
-    } else if (!(attrs instanceof Object)) {
-        attrs = { attrs : null };
     }
     [this.mqttRegistry, this.mdnsRegistry].map(
         x => {if(x) x[fun](attrs, seqval);}
@@ -257,7 +255,17 @@ RegistrarTail.prototype.stopDiscoveringAttributes = function(dattrs) {
  * { 'device' : {...}, 'fog' : {...}, 'cloud' : {...} }
  */
 RegistrarTail.prototype._formatDattributes = function(dattrs) {
-    if(!(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud))
+    // array mashalling
+    if (dattrs instanceof Array) {
+        dattrs = dattrs.reduce((acc, p) => { acc[p] = null; return acc; }, {});
+    } else {
+        ['device', 'fog', 'cloud'].filter(x => dattrs[x] instanceof Array).map(
+            x => {
+                dattrs[x] = dattrs[x].reduce((acc, p) => { acc[p] = null; return acc; }, {});
+            }
+        );
+    }
+    if (!(dattrs.all || dattrs.device || dattrs.fog || dattrs.cloud))
         dattrs = { 'all' : dattrs };
     ['device', 'fog', 'cloud'].map(
         x => {
@@ -269,15 +277,6 @@ RegistrarTail.prototype._formatDattributes = function(dattrs) {
     )
     if(dattrs.all)
         delete dattrs.all;
-    ['device', 'fog', 'cloud'].map(
-        x => {
-            if (dattrs[x] instanceof Array) {
-                dattrs[x] = dattrs[x].reduce((acc, p) => { acc[p] = null; return acc; }, {});
-            } else if (!(dattrs[x] instanceof Object)) {
-                dattrs[x] = { dattrs[x] : null };
-            }
-        }
-    );
     return dattrs;
 }
 RegistrarTail.prototype._modifyDattributes = function(fun, dattrs) {

From ac31c7be54bae1f99661b63fb8593710ce167e65 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 27 Jun 2018 13:27:36 -0400
Subject: [PATCH 43/62] JDISC: Updated README (in progress)

---
 lib/jdiscovery/README.md | 287 +++++++++++----------------------------
 1 file changed, 78 insertions(+), 209 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 34eefd15..ca0ebf54 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -1,39 +1,20 @@
-# JAMScript Registration and Discovery
-
-## To Run
-If on Ubuntu, or other debianesque systems, you'll need to install `libavahi-compat-libdnssd-dev` for the mdns module to work:
-`sudo apt install libavahi-compat-libdnssd-dev`
-You'll also need to link the mdns module, regardless of whether you're on a debianesque system:
-- macOS: `cd mdns && npm link`
-- Ubuntu: `cd mdns && sudo npm link --unsafe-perm` (because Ubuntu sucks)
-If you want to use MQTT, start an MQTT broker: `mosquitto`
-
-### To run app.js
-Try the following:
-- `npm start device` - starts a device node
-- `npm start device iPhone 1234567` - starts a device with attribute `iPhone` and phone number `1234567`
-- `npm start device Android 1234567` - starts a device with attribute `Android` and phone number `1234567`
-- `npm start fog` - starts a fog node
-- `npm start cloud` - starts a cloud node
-See `app.js` to figure out who discovers whom :)
-
-### If the mdns module stops working...
-You should update it! Do the following:
-- Run `npm pack mdns` to download the latest mdns source
-- Unzip the downloaded source
-- Replace the contents of the `mdns` folder in this directory with the contents of the unzipped source
-- Open up `mdns/lib/service_type.js` and comment out the sections of the code that limit the length of the advertisement's name. Currently (mdns version 2.3.4), the code blocks that do this are:
-```
-    if (str.length > 15) {
-        throw new Error('type ' + str + ' has more than 15 characters');
-    }
-```
-```
-    if (str.length > 16) { // 16 is correct because we have a leading underscore
-        throw new Error('type ' + _uu(str) + ' has more than 15 characters');
-    }
-```
-- Follow the steps in the `To Run` section above to make sure the module is properly linked
+# JDISCOVERY: A Discovery Service for JAMScript
+
+JAMScript nodes discover each other using jdiscovery and its attribute system.
+
+## Quick Setup and Demo
+
+### Dependencies
+The following are automatically installed by the JAMScript setup scripts.
+- `node.js & npm`
+- `mosquitto` - MQTT Broker
+- npm packages: `bonjour, mqtt`
+
+### Demo
+Open two terminals to the jdiscovery directory and run the following commands (one in each).
+- `node apps/tester.js node-0 device 42420`
+- `node apps/tester.js node-1 fog 42421`
+
 
 ## Registrar
 The `Registrar` is the object you'll be interfacing with. It is responsible for finding other nodes running the same application as this one, and making this node visible so that other nodes may find it too. You create a Registrar as follows:
@@ -116,7 +97,7 @@ In addition to giving each node the status attribute, the system is configured s
 We see that devices discover fogs statuses and fogs discover clouds statuses by default, but what if we're a cloud node and we want to discover fogs? No problem, we can use the `discoverAttributes` API as follows:
 
 ```
-    var reggie = new Registrar(app, machType, id, port);
+    var reggie = new Registrar(app, type, id, port);
 
     reggie.discoverAttributes({
         fog: {
@@ -188,21 +169,7 @@ In order to give a node an attribute, you use the `addAttributes` API. For examp
     // ...
 ```
 
-Or, if you want to wait to provide the temperature until the exact moment that the Registrar announces the attribute on the network, you can provide a function that returns the temperature:
-
-```
-    // ...
-
-    reggie.addAttributes({
-        thermostat: function() {
-            return theTemperature;
-        }
-    });
-
-    // ...
-```
-
-Lastly, if you want to keep the rest of the network up to date on your temperature, you can simply set an interval:
+If you want to keep the rest of the network up to date on your temperature, you can simply set an interval:
 
 ```
     // ...
@@ -294,171 +261,73 @@ In addition to `fog-up`, `fog-down`, `cloud-up`, and `cloud-down` events, the Re
     });
 ```
 
-## Example
-Finally, an example, putting everything together, and assuming that the type of the node (device, fog, or cloud) is indeterminate until runtime:
-
-**app.js**
-```
-    var Registrar = require('./jregistrar'),
-        errLog = require('../jerrlog'),
-        globals = require('../constants').globals,
-        events = require('events'),
-        Random = require('random-js');
-
-    var random = new Random(Random.engines.mt19937().autoSeed());
-
-    var machType = process.argv[2],
-        phoneType = process.argv[3],
-        phoneNumber = process.argv[4],
-        app = 'keithTest',
-        port = 1337,
-        id = random.uuid4();
+## External API
 
-    // don't forget to initialize the logger!
-    errLog.init(app, false);
+const Registrar = require('jdiscovery');
+const reggie = new Registrar(app, type, id, port, config);
 
-    console.log('_______________________________________________');
-    console.log(machType + ' id: ' + id);
-    console.log('-----------------------------------------------');
-    console.log();
+Register a node on the network, and discover other nodes.
+`options` is an optional parameter
+`options` include:
+attrsToSet: key/value pair as in this.setAttributes
+attrsToDiscover: as in this.discoverAttributes
 
-    var reggie = new Registrar(app, machType, id, port);
-
-    //------------------------------------------------------------------------------
-    // Default discoveries
-    //------------------------------------------------------------------------------
+reggie.registerAndDiscover();
 
-    if (machType === 'device') {
-        reggie.on('fog-up', function(fogId, connInfo) {
-            console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
-        });
 
-        reggie.on('fog-down', function(fogId) {
-            console.log('FOG DOWN: id: ' + fogId);
-        });
-    } else if (machType === 'fog') {
-        reggie.on('cloud-up', function(cloudId, connInfo) {
-            console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+Add custom, discoverable attributes to this node
+attrs is an object of key value pairs
 
-        });
+reggie.setAttributes(attrs);
+reggie.removeAttributes(attrs);
 
-        reggie.on('cloud-down', function(cloudId) {
-            console.log('CLOUD DOWN: id: ' + cloudId);
-        });
+Specify attributes to be discovered.
+dattrs can have one of the following forms:
+(a)
+    {
+        all: {attr: event, ...}, // discover these attributes for all nodes
+        device: {attr: event, ...}, // discover these attributes just for devices
+        fog: {attr: event, ...}, // discover these attributes just for fogs
+        cloud: {attr: event, ...} // discover these attributes just for clouds
     }
-
-    // on rare occasions, you might get an error
-    reggie.on('error', function(err) {
-        switch(err.name) {
-            case 'permissions_err':
-                console.log(err.message);
-                console.log('Subscriptions: ' + err.value);
-                break;
-            default:
-                console.log('unknown error');
-                break;
+(b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
+    For the status attribute, the format is:
+        status: {
+            online: 'fog-up',
+            offline: 'fog-down'
         }
-    });
-
-    //------------------------------------------------------------------------------
-    // Custom attributes/discoveries
-    //------------------------------------------------------------------------------
-
-    if (machType === 'device') {
-        // we'll have devices announce if they are phones (iphone or android)
-        // we'll say all devices are thermostats too...I know it doesn't make sense but it's just meant
-        // to be demonstrative :P
-        if (phoneType === 'iPhone') {
-            reggie.addAttributes({
-                iPhone: phoneNumber
-            });
-        } else if (phoneType === 'Android') {
-            reggie.addAttributes({
-                android: 'psych, get an iPhone!'
-            });
-
-            // in 10 seconds, turn this android into an iphone
-            setTimeout(function() {
-                reggie.removeAttributes('android');
-                reggie.addAttributes({
-                    iPhone: phoneNumber
-                });
-            }, 5000);
+    Whereas for custom attributes, the format is:
+        is_a_phone: {
+            onAdd: 'phone-found'
+            onRemove: 'phone-lost'
         }
-        reggie.addAttributes({
-            thermostat: function() {
-                // returns some random number, which we'll treat as the temperature
-                return 'Temperature: ' + Math.random() * 100;
-            }
-        });
-    } else if (machType === 'fog') {
-        // since we'll have clouds discover fogs, we don't need fogs to discover clouds
-        reggie.stopDiscoveringAttributes({
-            cloud: ['status']
-        });
-
-        reggie.discoverAttributes({
-            device: {
-                thermostat: {
-                    onAdd: 'thermo-added',
-                    onRemove: 'thermo-removed'
-                }
-            }
-        });
-
-        reggie.on('thermo-added', function(id, temp) {
-            console.log('DEVICE ' + id + ' is a thermostat with temperature ' + temp);
-        });
-
-        reggie.on('thermo-removed', function(id, temp) {
-            console.log('DEVICE ' + id + ' is no longer a thermostat');
-        });
-    } else {
-        // maybe clouds want to discover fogs, and iphone devices
-        reggie.discoverAttributes({
-            device: {
-                iPhone: {
-                    onAdd: 'iPhone-added',
-                    onRemove: 'iPhone-removed'
-                },
-                android: {
-                    onAdd: 'android-added',
-                    onRemove: 'android-removed'
-                }
-            },
-            fog: {
-                status: {
-                    online: 'fog-up',
-                    offline: 'fog-down'
-                }
-            }
-        });
-
-        reggie.on('fog-up', function(fogId, connInfo) {
-            console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
-        });
-
-        reggie.on('fog-down', function(fogId) {
-            console.log('FOG DOWN: id: ' + fogId);
-        });
-
-        reggie.on('iPhone-added', function(deviceId, phoneNumber) {
-            console.log('DEVICE ' + deviceId + ' is an iPhone with number ' + phoneNumber);
-        });
-
-        reggie.on('iPhone-removed', function(deviceId) {
-            console.log('DEVICE ' + deviceId + ' is no longer an iPhone');
-        });
 
-        reggie.on('android-added', function(deviceId, phoneNumber) {
-            console.log('DEVICE ' + deviceId + ' is an Android with number: ' + phoneNumber);
-        });
-
-        reggie.on('android-removed', function(deviceId) {
-            console.log('DEVICE ' + deviceId + ' is no longer an Android');
-        });
-    }
-
-    reggie.registerAndDiscover();
-
-```
+reggie.discoverAttributes(dattrs);
+reggie.stopDiscoveringAttributes(dattrs);
+reggie.quit();
+
+## Developer Notes
+
+DISCOVERY TABLE (dt)
+Notes:
+ --> We don't keep an entry for our own node
+And an example...
+{
+     node_id : {
+         'attrs' : {
+             'status' : {
+                 'seqval' : seqval,
+                 'data' : data
+             },
+             other_attr : {
+                 'seqval' : seqval,
+                 'data' : data
+             }
+         }
+         'network' : 'LAN' || 'WAN',
+         'other_useful_info' : ...
+     },
+     other_node_id : {
+         ...
+     }
+}

From b394eac50e49911227edf9be7de155ab831cecb1 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 10:33:49 -0400
Subject: [PATCH 44/62] JDISC: Updating docs (in progress)

---
 lib/jdiscovery/README.md | 127 ++++++++++++---------------------------
 1 file changed, 37 insertions(+), 90 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index ca0ebf54..5f7c666b 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -1,6 +1,6 @@
 # JDISCOVERY: A Discovery Service for JAMScript
 
-JAMScript nodes discover each other using jdiscovery and its attribute system.
+JAMScript nodes discover each other using jdiscovery and its attribute system. Below, you will find a quick start guide as well as a description of its external and developer APIs.
 
 ## Quick Setup and Demo
 
@@ -15,8 +15,9 @@ Open two terminals to the jdiscovery directory and run the following commands (o
 - `node apps/tester.js node-0 device 42420`
 - `node apps/tester.js node-1 fog 42421`
 
+## System Description
 
-## Registrar
+### Registrar
 The `Registrar` is the object you'll be interfacing with. It is responsible for finding other nodes running the same application as this one, and making this node visible so that other nodes may find it too. You create a Registrar as follows:
 ```
     var reggie = new Registrar(app, machType, id, port, config);
@@ -28,7 +29,6 @@ where `app` is the name of the application, `machType` (machine type) is one of
         protocols: {
             mqtt: false,
             mdns: true,
-            localStorage: false
         }
     }
 ```
@@ -41,17 +41,16 @@ Alternatively
     }
 ```
 will suffice.
-- `eliminateDuplicates`: Set to `true` if you want the `Registrar` to automatically eliminate duplicate discoveries, so that only one event will be returned to the app per discovery. Set this to `false` if you want to be notified separately if/when a discovery is made by each of local storage, mDNS, and MQTT. The default is `true`.
 
 Now, before we get into how to use our new Registrar object, we'll need to understand what **attributes** are.
 
-## Attributes
+### Attributes
 The system revolves around attributes. An attribute is some aspect of a node with an associated value (or not - an attribute need not have a value). Nodes have attributes that are discoverable by other nodes. If you can follow that, then the rest should be easy - that's really all there is to it. But, to solidify the idea, here are a couple of examples:
 - `thermostat` - You might give this attribute to a device that is a thermostat. The value of this attribute could be the temperature measured by the device.
 - `dimmable` - Maybe you have some smart light bulbs that can be dimmed. If so, perhaps you'd want to give them this attribute. The associated value could be the percentage of full brightness that the bulb is currently set to.
 - `cellPhone` - Perhaps you want a fog node to discover all nearby devices that are cell phones, for whatever reason. You could give such devices this attribute. The value of the attribute could be null if you don't need to know anything other than the fact that the device is a cell phone. Remember: you don't need to have a value associated with an attribute.
 
-## Discovering attributes
+### Discovering attributes
 This module is all about registration and discovery, so obviously we'll want to discover some attributes of the nodes out there. But what is out there to begin with?
 
 ### Built-in attributes/discoveries
@@ -157,12 +156,12 @@ When an attribute is removed from a node, or the node goes down (status becomes
 Now back to attributes. If status is the only attribute that any node has by default, then how can we give nodes other attributes? This is done by announcing attributes.
 
 ## Announcing attributes
-In order to give a node an attribute, you use the `addAttributes` API. For example, to add the thermostat attribute, you could use:
+In order to give a node an attribute, you use the `setAttributes` method of the Registrar. For example, to add the thermostat attribute, you could use:
 
 ```
     // ...
 
-    reggie.addAttributes({
+    reggie.setAttributes({
         thermostat: 20
     });
 
@@ -175,38 +174,32 @@ If you want to keep the rest of the network up to date on your temperature, you
     // ...
 
     // update the temperature every five seconds
-    setInterval(reggie.addAttributes, 5000, { thermostat: theTemperature });
+    setInterval(reggie.setAttributes, 5000, { thermostat: theTemperature });
 
     // ...
 ```
 
-## API
+## External API
 
-### reggie.addAttributes(attrs)
-Adds the specified attributes to the node.
+### Import and Constructor
+```
+const Registrar = require('jdiscovery');
+const reggie = new Registrar(app, type, id, port, config);
+```
 
-`attrs` is an object of `<attributeName, attributeValue>` pairs. The `attributeName` is limited to valid JavaScript object keys. The attribute value can be any basic data type, `null`, a JavaScript object, an array, or a function that returns a basic data type, `null`, or a JavaScript object or array.
+### reggie.registerAndDiscover([options]);
+Kick-starts registration (announcing of attributes) and discovery.
 
-If a function is passed, then the function will be executed to retrieve the value of the attribute just before the Registrar announces the attribute on the network. If you need to pass parameters to the function, you can use bind:
-```
-    var x = 10;
-    var y = 12;
+`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.addAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
+- `attrsToAdd`: an object of the same form as that accepted by `reggie.addAttributes`
+- `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
 
-    reggie.addAttributes({
-        someAttribute: function(a, b) {
-            return a + b;
-        }.bind(null, x, y)
-    });
-```
+### reggie.setAttributes(attrs)
+Sets the specified attributes to the node.
 
-Both the attribute name and the attribute value **should be kept brief**. The Registrar uses MQTT and mDNS under the hood, which are both lightweight messaging protocols. You may run into some trouble if you try to send too much information! **Remember: This module is should be used for basic discovery only. If nodes need to exchange larger chunks of information, then a separate connection should be made**.
+`attrs` is an object of `<attributeName, attributeValue>` pairs. The `attributeName` is limited to valid JavaScript object keys. The attribute value can be any basic data type, `null`, a JavaScript object, an array, or a function that returns a basic data type, `null`, or a JavaScript object or array.
 
-#### Reserved attributes
-As a general rule, you can add any attributes that you'd like. However, the following names are reserved by the system, and cannot be reused:
-- `status`
-- `lastCheckIn`
-- `createdAt`
-- `updatedAt`
+Both the attribute name and the attribute value **should be kept brief**. The Registrar uses MQTT and mDNS under the hood, which are both lightweight messaging protocols. You may run into some trouble if you try to send too much information! **Remember: This module is should be used for basic discovery only. If nodes need to exchange larger chunks of information, then a separate connection should be made**.
 
 ### reggie.removeAttributes(attrs)
 Removes the specified attributes from the node.
@@ -226,63 +219,7 @@ The value of each of these keys should be an object of `<attributeName, eventNam
 
 If you wish to just pass attributes to be discovered on all other nodes, regardless of device, fog, or cloud, then `dattrs` can simply be an object of `<attributeName, eventName>` pairs.
 
-### reggie.stopDiscoveringAttributes(dattrs)
-Tells the Registrar to stop discovering the given attributes of other nodes.
-
-`dattrs` is an object with one or more of the following keys:
--  `all`: an `Array` of attributes of **all** other nodes that the node should stop discovering
-- `device`: an `Array` of attributes of devices that the node should stop discovering
-- `fog`: an `Array` of attributes of fogs that the node should stop discovering
-- `cloud`: an `Array` of attributes of clouds that the node should stop discovering
-
-Alternatively, if you want to stop discovering attributes on all other nodes, regardless of type, `dattrs` cam simply be an `Array` of the attribute names.
-
-### reggie.registerAndDiscover([options])
-Kick-starts registration (announcing of attributes) and discovery.
-
-`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.addAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
-- `attrsToAdd`: an object of the same form as that accepted by `reggie.addAttributes`
-- `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
-
-## Other events
-In addition to `fog-up`, `fog-down`, `cloud-up`, and `cloud-down` events, the Regisrar also emits an `error` event. However, currently this event is only emitted if `MQTT` denies you from discovering certain attributes due to a permissions issue, which would be weird (at least in the current state of JAMScript) and should be flagged. The idea is that this `error` event can be extended to represent other issues that should be noted by you, the programmer.
-
-```
-    reggie.on('error', function(err) {
-        switch(err.name) {
-            case 'permissions_err':
-                console.log(err.message);
-                console.log('The following MQTT subscriptions were denied: ' + err.value);
-                break;
-            default:
-                console.log('unknown error');
-                break;
-        }
-    });
-```
-
-## External API
-
-const Registrar = require('jdiscovery');
-const reggie = new Registrar(app, type, id, port, config);
-
-Register a node on the network, and discover other nodes.
-`options` is an optional parameter
-`options` include:
-attrsToSet: key/value pair as in this.setAttributes
-attrsToDiscover: as in this.discoverAttributes
-
-reggie.registerAndDiscover();
-
-
-Add custom, discoverable attributes to this node
-attrs is an object of key value pairs
-
-reggie.setAttributes(attrs);
-reggie.removeAttributes(attrs);
-
-Specify attributes to be discovered.
-dattrs can have one of the following forms:
+In other words, dattrs can have one of the following forms:
 (a)
     {
         all: {attr: event, ...}, // discover these attributes for all nodes
@@ -302,9 +239,19 @@ dattrs can have one of the following forms:
             onRemove: 'phone-lost'
         }
 
-reggie.discoverAttributes(dattrs);
-reggie.stopDiscoveringAttributes(dattrs);
-reggie.quit();
+### reggie.stopDiscoveringAttributes(dattrs)
+Tells the Registrar to stop discovering the given attributes of other nodes.
+
+`dattrs` is an object with one or more of the following keys:
+-  `all`: an `Array` of attributes of **all** other nodes that the node should stop discovering
+- `device`: an `Array` of attributes of devices that the node should stop discovering
+- `fog`: an `Array` of attributes of fogs that the node should stop discovering
+- `cloud`: an `Array` of attributes of clouds that the node should stop discovering
+
+Alternatively, if you want to stop discovering attributes on all other nodes, regardless of type, `dattrs` cam simply be an `Array` of the attribute names.
+
+### reggie.quit();
+Perform a clean exit for the current node on the network.
 
 ## Developer Notes
 

From 17d46789445cc7f26c436ce031fc6d318779b778 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 10:43:15 -0400
Subject: [PATCH 45/62] JDISC: Updating docs...

---
 lib/jdiscovery/README.md | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 5f7c666b..e5964be5 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -6,7 +6,7 @@ JAMScript nodes discover each other using jdiscovery and its attribute system. B
 
 ### Dependencies
 The following are automatically installed by the JAMScript setup scripts.
-- `node.js & npm`
+- `node.js` & `npm`
 - `mosquitto` - MQTT Broker
 - npm packages: `bonjour, mqtt`
 
@@ -20,9 +20,9 @@ Open two terminals to the jdiscovery directory and run the following commands (o
 ### Registrar
 The `Registrar` is the object you'll be interfacing with. It is responsible for finding other nodes running the same application as this one, and making this node visible so that other nodes may find it too. You create a Registrar as follows:
 ```
-    var reggie = new Registrar(app, machType, id, port, config);
+    var reggie = new Registrar(app, type, id, port, config);
 ```
-where `app` is the name of the application, `machType` (machine type) is one of 'device', 'fog', or 'cloud', `id` is the id of the node, `port` is the port it is running on, and `config` is an optional parameter with the following options:
+where `app` is the name of the application, `type` (machine type) is one of 'device', 'fog', or 'cloud', `id` is the id of the node, `port` is the port it is running on, and `config` is an optional parameter with the following options:
 - `protocols`: An object in which you specify what protocols you want to use. If this option is not present, all protocols will be used. e.g. To use just mDNS, you might pass
 ```
     {
@@ -53,7 +53,7 @@ The system revolves around attributes. An attribute is some aspect of a node wit
 ### Discovering attributes
 This module is all about registration and discovery, so obviously we'll want to discover some attributes of the nodes out there. But what is out there to begin with?
 
-### Built-in attributes/discoveries
+#### Built-in attributes/discoveries
 By default, each device, fog, and cloud on the network has only a single attribute: *status*. The *status* attribute is used to discover which nodes are online and which are offline. If a node is online, then its status attribute will have a value of form:
 ```
     {
@@ -92,7 +92,7 @@ In addition to giving each node the status attribute, the system is configured s
     reggie.registerAndDiscover();
 ```
 
-### Custom discoveries
+#### Custom discoveries
 We see that devices discover fogs statuses and fogs discover clouds statuses by default, but what if we're a cloud node and we want to discover fogs? No problem, we can use the `discoverAttributes` API as follows:
 
 ```
@@ -155,7 +155,7 @@ When an attribute is removed from a node, or the node goes down (status becomes
 
 Now back to attributes. If status is the only attribute that any node has by default, then how can we give nodes other attributes? This is done by announcing attributes.
 
-## Announcing attributes
+### Announcing attributes
 In order to give a node an attribute, you use the `setAttributes` method of the Registrar. For example, to add the thermostat attribute, you could use:
 
 ```

From d46798086424ffac3f80500734a409127d10c031 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 10:52:14 -0400
Subject: [PATCH 46/62] JDISC: Updating docs...

---
 lib/jdiscovery/README.md | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index e5964be5..27980f9d 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -221,6 +221,7 @@ If you wish to just pass attributes to be discovered on all other nodes, regardl
 
 In other words, dattrs can have one of the following forms:
 (a)
+```
     {
         all: {attr: event, ...}, // discover these attributes for all nodes
         device: {attr: event, ...}, // discover these attributes just for devices
@@ -229,15 +230,19 @@ In other words, dattrs can have one of the following forms:
     }
 (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
     For the status attribute, the format is:
+```
         status: {
             online: 'fog-up',
             offline: 'fog-down'
         }
+```
     Whereas for custom attributes, the format is:
+```
         is_a_phone: {
             onAdd: 'phone-found'
             onRemove: 'phone-lost'
         }
+```
 
 ### reggie.stopDiscoveringAttributes(dattrs)
 Tells the Registrar to stop discovering the given attributes of other nodes.
@@ -255,10 +260,11 @@ Perform a clean exit for the current node on the network.
 
 ## Developer Notes
 
-DISCOVERY TABLE (dt)
+### Discovery Table
 Notes:
  --> We don't keep an entry for our own node
 And an example...
+```
 {
      node_id : {
          'attrs' : {
@@ -278,3 +284,4 @@ And an example...
          ...
      }
 }
+```

From 806fe7216de581d4d14945ea5e311cc826bfad9d Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 10:55:52 -0400
Subject: [PATCH 47/62] JDISC: Updating docs...

---
 lib/jdiscovery/README.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 27980f9d..41b4d048 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -190,8 +190,8 @@ const reggie = new Registrar(app, type, id, port, config);
 ### reggie.registerAndDiscover([options]);
 Kick-starts registration (announcing of attributes) and discovery.
 
-`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.addAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
-- `attrsToAdd`: an object of the same form as that accepted by `reggie.addAttributes`
+`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.setAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
+- `attrsToAdd`: an object of the same form as that accepted by `reggie.setAttributes`
 - `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
 
 ### reggie.setAttributes(attrs)
@@ -228,6 +228,7 @@ In other words, dattrs can have one of the following forms:
         fog: {attr: event, ...}, // discover these attributes just for fogs
         cloud: {attr: event, ...} // discover these attributes just for clouds
     }
+```
 (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
     For the status attribute, the format is:
 ```

From ad2344103d2bb539878e80f4d9db6bf83cae8cac Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 11:02:06 -0400
Subject: [PATCH 48/62] JDISC: Updating docs...

---
 lib/jdiscovery/README.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 41b4d048..e0c44b37 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -204,7 +204,7 @@ Both the attribute name and the attribute value **should be kept brief**. The Re
 ### reggie.removeAttributes(attrs)
 Removes the specified attributes from the node.
 
-`attrs` can be a `String` or an `Array` of `String`s.
+`attrs` is an `Array` of `String`s (**not** just a `String`).
 
 ### reggie.discoverAttributes(dattrs)
 Causes the node to begin discovering the given attributes of other nodes.
@@ -219,8 +219,8 @@ The value of each of these keys should be an object of `<attributeName, eventNam
 
 If you wish to just pass attributes to be discovered on all other nodes, regardless of device, fog, or cloud, then `dattrs` can simply be an object of `<attributeName, eventName>` pairs.
 
-In other words, dattrs can have one of the following forms:
-(a)
+In other words, dattrs can have one of the following forms:\
+- (a)
 ```
     {
         all: {attr: event, ...}, // discover these attributes for all nodes
@@ -229,7 +229,7 @@ In other words, dattrs can have one of the following forms:
         cloud: {attr: event, ...} // discover these attributes just for clouds
     }
 ```
-(b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs
+- (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs.\
     For the status attribute, the format is:
 ```
         status: {

From 58a36acc8fcd5ed00c142b028df66236c2ed8390 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 11:11:24 -0400
Subject: [PATCH 49/62] JDISC: Updating docs...

---
 lib/jamserver/jamlib.js  |  1 -
 lib/jdiscovery/README.md | 24 ++++--------------------
 2 files changed, 4 insertions(+), 21 deletions(-)

diff --git a/lib/jamserver/jamlib.js b/lib/jamserver/jamlib.js
index 87c1fe9a..ef89530f 100644
--- a/lib/jamserver/jamlib.js
+++ b/lib/jamserver/jamlib.js
@@ -27,7 +27,6 @@ deviceParams.setItem('machType', machType);
 
 var reggie = new Registrar(cmdopts.app, machType, deviceParams.getItem('deviceId'),
                             cmdopts.port, { protocols: { mqtt: true, mdns: true } });
-//                          cmdopts.port, {long: cmdopts.long, lat: cmdopts.lat}, { protocols: { mqtt: true, mdns: true } });
 
 var jamsys = require('./jamsys');
 jamsys.init(reggie, machType, cmdopts.tags, cmdopts.iflow, cmdopts.oflow, deviceParams.getItem('deviceId'),
diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index e0c44b37..8cfa6fe9 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -219,8 +219,8 @@ The value of each of these keys should be an object of `<attributeName, eventNam
 
 If you wish to just pass attributes to be discovered on all other nodes, regardless of device, fog, or cloud, then `dattrs` can simply be an object of `<attributeName, eventName>` pairs.
 
-In other words, dattrs can have one of the following forms:\
-- (a)
+In other words, dattrs can have one of the following forms:
+
 ```
     {
         all: {attr: event, ...}, // discover these attributes for all nodes
@@ -229,21 +229,7 @@ In other words, dattrs can have one of the following forms:\
         cloud: {attr: event, ...} // discover these attributes just for clouds
     }
 ```
-- (b) As a shortcut for _all_, one can simply pass an object of <attr, event> pairs.\
-    For the status attribute, the format is:
-```
-        status: {
-            online: 'fog-up',
-            offline: 'fog-down'
-        }
-```
-    Whereas for custom attributes, the format is:
-```
-        is_a_phone: {
-            onAdd: 'phone-found'
-            onRemove: 'phone-lost'
-        }
-```
+**OR** As a shortcut for _all_, one can simply pass an object of <attr, event> pairs.
 
 ### reggie.stopDiscoveringAttributes(dattrs)
 Tells the Registrar to stop discovering the given attributes of other nodes.
@@ -262,9 +248,7 @@ Perform a clean exit for the current node on the network.
 ## Developer Notes
 
 ### Discovery Table
-Notes:
- --> We don't keep an entry for our own node
-And an example...
+The discovery table kept in the jregistrar-tail holds a map of the nodes on the network currently. An example discovery table can be found below. Refer to the in-code comments for further details.
 ```
 {
      node_id : {

From afd97545937c2964d02168ff74a9d6353ddac25e Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 11:16:24 -0400
Subject: [PATCH 50/62] JDISC: Updated docs!

---
 lib/jdiscovery/README.md | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 8cfa6fe9..bfc8fb7e 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -23,7 +23,7 @@ The `Registrar` is the object you'll be interfacing with. It is responsible for
     var reggie = new Registrar(app, type, id, port, config);
 ```
 where `app` is the name of the application, `type` (machine type) is one of 'device', 'fog', or 'cloud', `id` is the id of the node, `port` is the port it is running on, and `config` is an optional parameter with the following options:
-- `protocols`: An object in which you specify what protocols you want to use. If this option is not present, all protocols will be used. e.g. To use just mDNS, you might pass
+- `protocols`: An object in which you specify what protocols you want to use. If this option is not present, all protocols will be used. e.g. To use just mDNS, you would pass:
 ```
     {
         protocols: {
@@ -32,16 +32,6 @@ where `app` is the name of the application, `type` (machine type) is one of 'dev
         }
     }
 ```
-Alternatively
-```
-    {
-        protocols: {
-            mdns: true
-        }
-    }
-```
-will suffice.
-
 Now, before we get into how to use our new Registrar object, we'll need to understand what **attributes** are.
 
 ### Attributes

From 80a2a5d0ef149973d08e9bf3cd2eb3462702746a Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 29 Jun 2018 12:43:25 -0400
Subject: [PATCH 51/62] JDISC: Removed reserved attr list

---
 lib/jdiscovery/jregistrar-tail.js | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index 07a30612..c0c5b3b1 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -56,12 +56,6 @@ function RegistrarTail(app, type, id, port, config) {
     this.dt = {};
     this.seqval = 0;
 
-    /**
-     * Reserved attributes.
-     * These are attribute names that cannot be used by third parties.
-     */
-    this.reservedAttrs = ['status', 'lastCheckIn', 'createdAt', 'updatedAt'];
-
     /**
      * config-specific set-up
      */

From 3887c97494f96573d149f909160f92e16b18830d Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Tue, 3 Jul 2018 16:25:16 -0400
Subject: [PATCH 52/62] JDISC: Added getDiscoveryTable(handler) for dt pull,
 tested

---
 lib/jdiscovery/README.md             | 6 +++---
 lib/jdiscovery/apps/tester.js        | 2 +-
 lib/jdiscovery/jregistrar-abdomen.js | 3 +++
 lib/jdiscovery/jregistrar-head.js    | 8 ++++++++
 lib/jdiscovery/jregistrar-tail.js    | 6 ++++++
 5 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index bfc8fb7e..79f19a3e 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -235,10 +235,10 @@ Alternatively, if you want to stop discovering attributes on all other nodes, re
 ### reggie.quit();
 Perform a clean exit for the current node on the network.
 
-## Developer Notes
+### reggie.getDiscoveryTable(handler);
+Allows access to the discovery table (which is, essentially, a map of the current locally known network state). A call to getDiscoveryTable(...) does **NOT** return a copy of the discovery table. It is through the `handler` callback that the received discovery table can be processed.
 
-### Discovery Table
-The discovery table kept in the jregistrar-tail holds a map of the nodes on the network currently. An example discovery table can be found below. Refer to the in-code comments for further details.
+An example discovery table can be found below. Refer to the in-code comments for further details.
 ```
 {
      node_id : {
diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/tester.js
index d66762f2..fb09be09 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/tester.js
@@ -55,9 +55,9 @@ if (type === 'device') {
     f();
 } else if (type === 'fog') {
     
-
     reggie.on('new-secret', function(id, secret) {
         console.log('NEW-SECRET: id: ' + id + ' secret: ' + secret);
+//        reggie.getDiscoveryTable((x) => console.log(JSON.stringify(x)));
     });
     reggie.on('no-more-secret', function(id) {
         console.log('NO-MORE-SECRET: id: ' + id);
diff --git a/lib/jdiscovery/jregistrar-abdomen.js b/lib/jdiscovery/jregistrar-abdomen.js
index b109c30c..74283705 100644
--- a/lib/jdiscovery/jregistrar-abdomen.js
+++ b/lib/jdiscovery/jregistrar-abdomen.js
@@ -9,6 +9,9 @@ this.registrarTail.on('appNotifLess', (event, id, protocol) => {
 this.registrarTail.on('appNotifMore', (event, id, data, protocol) => {
     process.send({ appNotifMore: {event: event, id: id, data: data, protocol: protocol }});
 });
+this.registrarTail.on('discoveryTable', (dt) => {
+    process.send({ discoveryTable: dt });
+});
 
 process.on('message', (m) => {
     // XXX
diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
index a67db4a0..599f8545 100644
--- a/lib/jdiscovery/jregistrar-head.js
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -16,6 +16,9 @@ function RegistrarHead(app, type, id, port, config) {
         } else if(m.hasOwnProperty('appNotifMore')) {
             e = m['appNotifMore'];
             self.emit(e.event, e.id, e.data, e.protocol);
+        } else if(m.hasOwnProperty('discoveryTable')) {
+            if((typeof self.discoveryTableHandler) === 'function')
+                self.discoveryTableHandler(JSON.parse(m['discoveryTable']));
         }
     });
 }
@@ -51,5 +54,10 @@ RegistrarHead.prototype.quit = function() {
     this.registrarAbdomen.send({ quit : null });
 }
 
+RegistrarHead.prototype.getDiscoveryTable = function(handler) {
+    this.discoveryTableHandler = handler;
+    this.registrarAbdomen.send({ getDiscoveryTable : null });
+}
+
 /* exports */
 module.exports = RegistrarHead;
diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index c0c5b3b1..2fc57a04 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -289,6 +289,12 @@ RegistrarTail.prototype.quit = function() {
         }
     );
 }
+/**
+ * Respond to discovery table pull request
+ */
+RegistrarTail.prototype.getDiscoveryTable = function() {
+    this.emit('discoveryTable', JSON.stringify(this.dt));
+}
 
 /**
  * _PRIVATE HELPERS

From 6d6c017a6e24d9cf70aa330fe041bdf709cd51f4 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 4 Jul 2018 13:56:33 -0400
Subject: [PATCH 53/62] Updated aux repos & JDISC: README.md minor changes

---
 lib/jdiscovery/README.md | 4 ++--
 samples                  | 2 +-
 tools                    | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 79f19a3e..47808022 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -232,10 +232,10 @@ Tells the Registrar to stop discovering the given attributes of other nodes.
 
 Alternatively, if you want to stop discovering attributes on all other nodes, regardless of type, `dattrs` cam simply be an `Array` of the attribute names.
 
-### reggie.quit();
+### reggie.quit()
 Perform a clean exit for the current node on the network.
 
-### reggie.getDiscoveryTable(handler);
+### reggie.getDiscoveryTable(handler)
 Allows access to the discovery table (which is, essentially, a map of the current locally known network state). A call to getDiscoveryTable(...) does **NOT** return a copy of the discovery table. It is through the `handler` callback that the received discovery table can be processed.
 
 An example discovery table can be found below. Refer to the in-code comments for further details.
diff --git a/samples b/samples
index c751429b..69f6d869 160000
--- a/samples
+++ b/samples
@@ -1 +1 @@
-Subproject commit c751429b14373de757a1bcc44007f207ee37e1ea
+Subproject commit 69f6d869e07cd48af53c732b0c4932846cacd723
diff --git a/tools b/tools
index a7c201e1..44c53172 160000
--- a/tools
+++ b/tools
@@ -1 +1 @@
-Subproject commit a7c201e17d430a9a0a034775cfcd7a1af117ed4c
+Subproject commit 44c53172052d4125deda5346f1dc2f4fbfa80ba3

From 6b39320a7ad464b344a972ddc87c177bd557e680 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Wed, 4 Jul 2018 16:57:12 -0400
Subject: [PATCH 54/62] JDISC: Implemented mDNSreg heartbeat feature

---
 lib/jamserver/constants.js     |  7 +--
 lib/jdiscovery/mdnsregistry.js | 94 +++++++++++++++++++++++++++++-----
 2 files changed, 84 insertions(+), 17 deletions(-)

diff --git a/lib/jamserver/constants.js b/lib/jamserver/constants.js
index 49a3ec78..affa3ec7 100644
--- a/lib/jamserver/constants.js
+++ b/lib/jamserver/constants.js
@@ -49,9 +49,10 @@ module.exports = Object.freeze({
     },
 
     mdns: {
-        retries: 5,
-        retryInterval: 2000, // 2 seconds
-        longRetryInterval: 60000 // 1 minute
+        retryInterval: 2000,
+        heartBeatPort: 5454,
+        heartBeatMulticastAddress: '239.0.0.251',
+        heartBeatTTL: 1000
     },
 
     localStorage: {
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index dc5603d0..54c6620f 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -1,6 +1,7 @@
 var bonjour = require('bonjour')(),
     constants = require('../jamserver/constants'),
-    Registry = require('./registry');
+    Registry = require('./registry'),
+    dgram = require('dgram');
 
 function MDNSRegistry(app, type, id, port) {
 
@@ -24,6 +25,15 @@ function MDNSRegistry(app, type, id, port) {
     this.attrRemovalAd = null;
     this.attrRemovalBrowsers = {};
 
+    /**
+     * HeartBeat
+     */
+    this.hbPort = constants.mdns.heartBeatPort;
+    this.hbMulticastAddress = constants.mdns.heartBeatMulticastAddress;
+    this.hbTTL = constants.mdns.heartBeatTTL;
+    this.hbSocket = null;
+    this.upTable = {};
+
     this.started = false;
 }
 
@@ -42,8 +52,48 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
 
     if(this.started)
         return;
-    
+
     var self = this;
+
+    // Setup socket to send own hb and receive hb from others
+    this.hbSocket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
+    // ...  handle incoming heartbeats: if entry in upTable, renew entry TTL
+    this.hbSocket.on('message', function (message, remote) {
+        // message is just the node id
+        let id = message.toString();
+        // remote has properties: address, port
+        if(self.upTable.hasOwnProperty(id)) {
+            clearTimeout(self.upTable[id].timer);
+            self.upTable[id].timer = setTimeout(
+                () => {
+                    // remove node entry from upTable
+                    let type = self.upTable[id].type;
+                    delete self.upTable[id];
+                    // propagate node down to jreg
+                    self.emit('discovery', 'status',
+                        self.attrsToBrowse[type]['status'].offline,
+                        id, 'offline', undefined);
+                },
+                self.hbTTL
+            );
+        }
+    });
+    // ... bind and finalize socket setup
+    this.hbSocket.bind(this.hbPort, function() {
+        self.hbSocket.setBroadcast(true);
+        self.hbSocket.setMulticastTTL(128);
+        self.hbSocket.addMembership(self.hbMulticastAddress);
+    });
+    // ... start periodic hb emission for local node
+    setInterval(
+        () => {
+            let m = new Buffer.from(self.id);
+            self.hbSocket.send(m, 0, m.length, self.hbPort, self.hbMulticastAddress);
+        },
+        Math.floor(self.hbTTL/3)
+    );
+
+    // Setup attribute removal browsers
     ['device', 'fog', 'cloud'].map(
         (x) => {
             this.attrRemovalBrowsers[x] =
@@ -72,6 +122,11 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
 
                         // propagate advertised attribute removals
                         if(attr === 'status') {
+                            // clear upTable entry to avoid extra notif
+                            if(self.upTable.hasOwnProperty(service.name)) {
+                                clearTimeout(self.upTable[service.name].timer);
+                                delete self.upTable[service.name];
+                            }
                             self.emit('discovery', attr,
                                 self.attrsToBrowse[x][attr].offline,
                                 service.name, 'offline', undefined);
@@ -270,6 +325,28 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
         if (service.name === self.id) {
             return;
         }
+
+        // initiate heartbeat upon discovering a new node
+        if(attr === 'status') {
+            if(self.upTable.hasOwnProperty(service.name)) {
+                clearTimeout(self.upTable[service.name].timer);
+            }
+            self.upTable[service.name] = {
+                type    :   type,
+                timer   :   setTimeout(
+                                () => {
+                                    // remove node entry from upTable
+                                    delete self.upTable[service.name];
+                                    // propagate node down to jreg
+                                    self.emit('discovery', 'status',
+                                        self.attrsToBrowse[type]['status'].offline,
+                                        service.name, 'offline', undefined);
+                                },
+                                self.hbTTL
+                            )
+            }
+        }
+
         // emit a discovery event!
         self.emit('discovery', attr,
             (attr === 'status')
@@ -281,18 +358,7 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
         );
     });
     browser.on('down', function(service) {
-        // DEBUG
-        console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service down");
-        // ignore our own services
-        if (service.name === self.id) {
-            return;
-        }
-        // do nothing unless it s a status offline notification
-        if(attr === 'status') {
-            self.emit('discovery', attr,
-                self.attrsToBrowse[type][attr].offline,
-                service.name, 'offline', undefined);
-        }
+        // do nothing!
     });
     browser.start();
 }

From 149914577c2188a5ce27e97d774d60c42a1a6634 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 5 Jul 2018 11:59:40 -0400
Subject: [PATCH 55/62] JDISC: Updated docs, updated apps/demo.js

---
 lib/jdiscovery/README.md                   | 268 ++++++++-------------
 lib/jdiscovery/apps/{tester.js => demo.js} |  58 ++---
 2 files changed, 128 insertions(+), 198 deletions(-)
 rename lib/jdiscovery/apps/{tester.js => demo.js} (57%)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 47808022..61c74e47 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -11,109 +11,91 @@ The following are automatically installed by the JAMScript setup scripts.
 - npm packages: `bonjour, mqtt`
 
 ### Demo
+```
+/* Source in apps/demo.js */
+
+
+
+
+```
+
 Open two terminals to the jdiscovery directory and run the following commands (one in each).
 - `node apps/tester.js node-0 device 42420`
 - `node apps/tester.js node-1 fog 42421`
 
-## System Description
+## API
 
-### Registrar
-The `Registrar` is the object you'll be interfacing with. It is responsible for finding other nodes running the same application as this one, and making this node visible so that other nodes may find it too. You create a Registrar as follows:
+### Import and Constructor
 ```
-    var reggie = new Registrar(app, type, id, port, config);
+const Registrar = require('jdiscovery');
+const reggie = new Registrar(app, type, id, port, config);
 ```
-where `app` is the name of the application, `type` (machine type) is one of 'device', 'fog', or 'cloud', `id` is the id of the node, `port` is the port it is running on, and `config` is an optional parameter with the following options:
-- `protocols`: An object in which you specify what protocols you want to use. If this option is not present, all protocols will be used. e.g. To use just mDNS, you would pass:
+-`app`: Application name
+-`type`: Node type ('device', 'fog', 'cloud')
+-`id`: Node identifier (must be unique w.r.t. application name)
+-`port`: Port number where the application can be reached
+-`config`: Object with property `protocols` for regirstrar setup (as is for flexibility)
 ```
+    const config =
     {
         protocols: {
             mqtt: false,
-            mdns: true,
+            mdns: true
         }
     }
 ```
-Now, before we get into how to use our new Registrar object, we'll need to understand what **attributes** are.
 
-### Attributes
-The system revolves around attributes. An attribute is some aspect of a node with an associated value (or not - an attribute need not have a value). Nodes have attributes that are discoverable by other nodes. If you can follow that, then the rest should be easy - that's really all there is to it. But, to solidify the idea, here are a couple of examples:
-- `thermostat` - You might give this attribute to a device that is a thermostat. The value of this attribute could be the temperature measured by the device.
-- `dimmable` - Maybe you have some smart light bulbs that can be dimmed. If so, perhaps you'd want to give them this attribute. The associated value could be the percentage of full brightness that the bulb is currently set to.
-- `cellPhone` - Perhaps you want a fog node to discover all nearby devices that are cell phones, for whatever reason. You could give such devices this attribute. The value of the attribute could be null if you don't need to know anything other than the fact that the device is a cell phone. Remember: you don't need to have a value associated with an attribute.
+### reggie.registerAndDiscover([options]);
+Kick-starts registration (announcing of attributes) and discovery. **This method must be called for the registrar to start functioning; it is best to call it once all interests have been registered and base attributes set.** See the demo code above for an example.
 
-### Discovering attributes
-This module is all about registration and discovery, so obviously we'll want to discover some attributes of the nodes out there. But what is out there to begin with?
+`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.setAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
+- `attrsToAdd`: an object of the same form as that accepted by `reggie.setAttributes`
+- `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
 
-#### Built-in attributes/discoveries
-By default, each device, fog, and cloud on the network has only a single attribute: *status*. The *status* attribute is used to discover which nodes are online and which are offline. If a node is online, then its status attribute will have a value of form:
-```
-    {
-        ip: ip_addr_of_node,
-        port: port_of_node
-    }
-```
-In other words, the status attribute gives you the information you need to connect to a node. If, however, the node is offline, then the value of the status attribute will be 'offline'.
+### reggie.setAttributes(attrs);
+Sets the specified attributes to the node. Calling this method multiple times updates the attribute value being shared with the network.
 
-In addition to giving each node the status attribute, the system is configured such that devices discover fog statuses and fogs discover cloud statuses, i.e. devices will be made aware of when fogs go online or offline, and fogs will be made aware of when clouds go online or offline. But how will you, as the programmer, be informed of these status updates? Status updates, and, in fact, all attribute discoveries, are emitted to the application by the Registrar. To wrap up our discussion of built-in attributes, we'll take a look at the built-in events the Registrar object emits: `fog-up`, `fog-down`, `cloud-up`, and `cloud-down`. An example is usually helpful.
+`attrs` is an object of `<attr, value>` pairs. The `attr` is the name of the attribute and is limited to valid JavaScript object keys. The attribute `value` can be any basic data type, `null`, or a JavaScript object.
 
+Both attribute names and values **should be kept brief**. The Registrar uses MQTT and mDNS under the hood, which are both lightweight messaging protocols. You may run into some trouble if you try to send too much information! **Remember: This module is should be used for basic discovery only. If nodes need to exchange larger chunks of information, then a separate connection should be made**.
 ```
-    var reggie = new Registrar(app, machType, id, port);
-
-    // devices will receive these events by default
-    reggie.on('fog-up', function(id, connInfo, protocol) {
-        console.log('Fog ' + id + ' is online, with ip ' + connInfo.ip + ' and port ' + connInfo.port);
-    });
-
-    reggie.on('fog-down', function(id, protocol) {
-        console.log('Fog ' + id + ' has gone offline');
-    });
-
-    // fogs will receive these events by default
-    reggie.on('cloud-up', function(id, connInfo, protocol) {
-        console.log('Cloud ' + id + ' is online, with ip ' + connInfo.ip + ' and port ' + connInfo.port);
-    });
-
-    reggie.on('cloud-down', function(id, protocol) {
-        console.log('Cloud ' + id + ' has gone offline');
+    // Setting an attribute once
+    reggie.setAttributes({
+        thermostat: 20
     });
-
-    // clouds will not receive any such events (by default)
-
-    // kick-start registration and discovery
-    reggie.registerAndDiscover();
+    // If you want to keep the rest of the network up to date on your temperature, you can simply set an interval
+    // ... update the temperature every five seconds
+    setInterval(reggie.setAttributes, 
+                5000, 
+                { thermostat: _getThermostatTemp() }
+    );
 ```
 
-#### Custom discoveries
-We see that devices discover fogs statuses and fogs discover clouds statuses by default, but what if we're a cloud node and we want to discover fogs? No problem, we can use the `discoverAttributes` API as follows:
+### reggie.removeAttributes(attrs);
+Stop sharing the given attrs with the network.
 
-```
-    var reggie = new Registrar(app, type, id, port);
-
-    reggie.discoverAttributes({
-        fog: {
-            status: {
-                online: 'fog-up',
-                offline: 'fog-down'
-            }
-        }
-    });
-
-    // now this node (a cloud) will receive fog-up and fog-down events
-    reggie.on('fog-up', function(id, connInfo, protocol) {
-        console.log('Fog ' + id + ' is online, with ip ' + connInfo.ip + ' and port ' + connInfo.port);
-    });
+`attrs` is an `Array` of `String`s (**not** just a `String`).
 
-    reggie.on('fog-down', function(id, protocol) {
-        console.log('Fog ' + id + ' has gone offline');
-    });
+### reggie.discoverAttributes(dattrs);
+Causes the node to begin discovering the given attributes of other nodes.
 
-    reggie.registerAndDiscover();
+`dattrs` is an object with one or more of the following keys:
+-  `all`: attributes of **all** other nodes that the node should discover
+- `device`: attributes of devices that the node should discover
+- `fog`: attributes of fogs that the node should discover
+- `cloud`: attributes of clouds that the node should discover
 ```
-
-By this point, you're probably sick of the status attribute. Surely there are other things you'll want to discover too, for example devices that are thermostats! (exciting, right?) You're in luck! You can discover them as follows:
-
+    {
+        all: {attr: event, ...}, // discover these attributes for all nodes
+        device: {attr: event, ...}, // discover these attributes just for devices
+        fog: {attr: event, ...}, // discover these attributes just for fogs
+        cloud: {attr: event, ...} // discover these attributes just for clouds
+    }
 ```
-    // ...
+**OR** As a shortcut for _all_, one can simply pass an object of <attr, event> pairs.
 
+The value of each of these keys should be an object of `<attr, event>` pairs, where `attr` is the name of an attribute to be discovered, and `event` is an object with keys 'onAdd' & 'onRemove' mapped to event names which will be emitted by the Registrar on discovery (`onAdd`) or on loss (`onRemove`). 
+```
     reggie.discoverAttributes({
         device: {
             thermostat: {
@@ -122,106 +104,34 @@ By this point, you're probably sick of the status attribute. Surely there are ot
             }
         }
     });
-
+    // register interest in these events
     reggie.on('i-found-a-thermostat', function(id, value, protocol) {
         console.log('Node ' + id + ' is apparently a thermostat with temperature ' + value);
     });
-
     reggie.on('this-node-is-no-longer-a-thermostat', function(id, protocol) {
         console.log('Node ' + id + ' is apparently no longer a thermostat');
     });
-
-    // ...
 ```
-
-Notice the parameters to the functions called upon a discovery. When a discovery is made, the following arguments are available:
-- `nodeId`: the ID of the node for which the discovery was made
-- `attributeValue`: the value of the attribute discovered
-- `protocol`: the protocol which made the discovery
-    - The protocol will be one of the values defined in `../jamserver/constants.js#globals.Protocol`
-When an attribute is removed from a node, or the node goes down (status becomes offline), then there are just two parameters to the function:
-- `nodeId`: the ID of the node which removed the attribute, or went down
-- `protocol`: the protocol which made the discovery
-
-Now back to attributes. If status is the only attribute that any node has by default, then how can we give nodes other attributes? This is done by announcing attributes.
-
-### Announcing attributes
-In order to give a node an attribute, you use the `setAttributes` method of the Registrar. For example, to add the thermostat attribute, you could use:
-
+The only exception to this is when discovering the `status` attribute, in which case `event` must be an object specifying two events: one to be emitted when the remote node comes up (with `online`) and another to be emitted when it goes down (with `offline`).
 ```
-    // ...
-
-    reggie.setAttributes({
-        thermostat: 20
+    reggie.discoverAttributes({
+        fog: {
+            status: {
+                online: 'fog-up',
+                offline: 'fog-down'
+            }
+        }
+    });
+    // register interest in these events
+    reggie.on('fog-up', function(id, connInfo, protocol) {
+        console.log('Fog ' + id + ' is online, with ip ' + connInfo.ip + ' and port ' + connInfo.port);
+    });
+    reggie.on('fog-down', function(id, protocol) {
+        console.log('Fog ' + id + ' has gone offline');
     });
-
-    // ...
-```
-
-If you want to keep the rest of the network up to date on your temperature, you can simply set an interval:
-
-```
-    // ...
-
-    // update the temperature every five seconds
-    setInterval(reggie.setAttributes, 5000, { thermostat: theTemperature });
-
-    // ...
-```
-
-## External API
-
-### Import and Constructor
-```
-const Registrar = require('jdiscovery');
-const reggie = new Registrar(app, type, id, port, config);
-```
-
-### reggie.registerAndDiscover([options]);
-Kick-starts registration (announcing of attributes) and discovery.
-
-`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.setAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
-- `attrsToAdd`: an object of the same form as that accepted by `reggie.setAttributes`
-- `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
-
-### reggie.setAttributes(attrs)
-Sets the specified attributes to the node.
-
-`attrs` is an object of `<attributeName, attributeValue>` pairs. The `attributeName` is limited to valid JavaScript object keys. The attribute value can be any basic data type, `null`, a JavaScript object, an array, or a function that returns a basic data type, `null`, or a JavaScript object or array.
-
-Both the attribute name and the attribute value **should be kept brief**. The Registrar uses MQTT and mDNS under the hood, which are both lightweight messaging protocols. You may run into some trouble if you try to send too much information! **Remember: This module is should be used for basic discovery only. If nodes need to exchange larger chunks of information, then a separate connection should be made**.
-
-### reggie.removeAttributes(attrs)
-Removes the specified attributes from the node.
-
-`attrs` is an `Array` of `String`s (**not** just a `String`).
-
-### reggie.discoverAttributes(dattrs)
-Causes the node to begin discovering the given attributes of other nodes.
-
-`dattrs` is an object with one or more of the following keys:
--  `all`: attributes of **all** other nodes that the node should discover
-- `device`: attributes of devices that the node should discover
-- `fog`: attributes of fogs that the node should discover
-- `cloud`: attributes of clouds that the node should discover
-
-The value of each of these keys should be an object of `<attributeName, eventName>` pairs, where `attributeName` is the name of an attribute to be discovered, and `eventName` is the name of the event to have the Registrar emit when it makes a discovery. The only exception to this is when discovering the `status` attribute, in which case `eventName` must be an object specifying two events: one to be emitted when the remote node comes up (with key `online`) and another to be emitted when it goes down (with key `offline`), e.g. `{ online: 'aNodeWentUp', offline: 'ohNoItWentDown' }`.
-
-If you wish to just pass attributes to be discovered on all other nodes, regardless of device, fog, or cloud, then `dattrs` can simply be an object of `<attributeName, eventName>` pairs.
-
-In other words, dattrs can have one of the following forms:
-
-```
-    {
-        all: {attr: event, ...}, // discover these attributes for all nodes
-        device: {attr: event, ...}, // discover these attributes just for devices
-        fog: {attr: event, ...}, // discover these attributes just for fogs
-        cloud: {attr: event, ...} // discover these attributes just for clouds
-    }
 ```
-**OR** As a shortcut for _all_, one can simply pass an object of <attr, event> pairs.
 
-### reggie.stopDiscoveringAttributes(dattrs)
+### reggie.stopDiscoveringAttributes(dattrs);
 Tells the Registrar to stop discovering the given attributes of other nodes.
 
 `dattrs` is an object with one or more of the following keys:
@@ -230,16 +140,12 @@ Tells the Registrar to stop discovering the given attributes of other nodes.
 - `fog`: an `Array` of attributes of fogs that the node should stop discovering
 - `cloud`: an `Array` of attributes of clouds that the node should stop discovering
 
-Alternatively, if you want to stop discovering attributes on all other nodes, regardless of type, `dattrs` cam simply be an `Array` of the attribute names.
+Alternatively, if you want to stop discovering attributes on all nodes, regardless of type, `dattrs` cam simply be an `Array` of the attribute names.
 
-### reggie.quit()
-Perform a clean exit for the current node on the network.
-
-### reggie.getDiscoveryTable(handler)
-Allows access to the discovery table (which is, essentially, a map of the current locally known network state). A call to getDiscoveryTable(...) does **NOT** return a copy of the discovery table. It is through the `handler` callback that the received discovery table can be processed.
-
-An example discovery table can be found below. Refer to the in-code comments for further details.
+### reggie.getDiscoveryTable(handler);
+Gives access to the discovery table (which is, essentially, a map of the current locally known network state). A call to getDiscoveryTable(...) does **NOT** return a copy of the discovery table. It is through the `handler` callback that the received discovery table can be processed. An example discovery table can be found below.
 ```
+const dt = 
 {
      node_id : {
          'attrs' : {
@@ -260,3 +166,23 @@ An example discovery table can be found below. Refer to the in-code comments for
      }
 }
 ```
+
+### reggie.quit();
+Perform a clean exit for the current node on the network. This will lead to jdiscovery telling other nodes that the node going down is not because of a mobility-oriented disconnection.
+
+## Developer Notes
+
+### Registry Policies
+
+#### MQTT
+
+Connections between a client and a broker always use the following configuration:
+- `cleanSession : true`
+- `lastWill : { data : 'offline' }`
+Furthermore, all messages are sent with `retain : true` which leads to broker keeping all the most recent messages on all topics to reflect the current network state at any time.
+It may be noted that having `cleanSession : true` also requires a node to resubscribe to its topics of interest after any temporary disconnection.
+Regarding the last will of a node, it will be published to all nodes interested in the status attribute of the publisher in the case where the connection between the publisher and the broker is interrupted.
+
+#### MDNS
+
+Nodes and services are discovered using the standard MDNS service discovery methodology. For node and service downs, however, we rely on a new reserved service coming up, which all nodes are configured to listen to by default. This `(app)-(type)-attrrem` service holds a list of services going down in its text record. For crash and mobility-oriented connection detection, a simple heartbeat mechanism is used. Do note that for the heartbeat mechanism to function properly, port `5454`must be free on all hosts and multicast address `239.0.0.251` should be unused by other applications on the LAN. 
diff --git a/lib/jdiscovery/apps/tester.js b/lib/jdiscovery/apps/demo.js
similarity index 57%
rename from lib/jdiscovery/apps/tester.js
rename to lib/jdiscovery/apps/demo.js
index fb09be09..0471a856 100644
--- a/lib/jdiscovery/apps/tester.js
+++ b/lib/jdiscovery/apps/demo.js
@@ -1,22 +1,23 @@
-var Registrar = require('../jregistrar-head.js'),
-globals = require('../../jamserver/constants').globals,
-events = require('events');
+const   Registrar = require('../jregistrar-head.js'),
+        globals = require('../../jamserver/constants').globals,
+        events = require('events');
 
-var id = process.argv[2],
-type = process.argv[3],
-port = process.argv[4],
-app = 'tester';
+const   id = process.argv[2],
+        type = process.argv[3],
+        port = process.argv[4],
+        app = 'tester';
 
 console.log('_______________________________________________');
 console.log(' id: ' + id + ' type: ' + type + ' port: ' + port);
 console.log('-----------------------------------------------');
 console.log();
 
-var reggie = new Registrar(app, type, id, port,
+// Construct registrar and start it
+const   reggie = new Registrar(app, type, id, port,
                            { protocols: { mqtt: false, mdns: true } });
 reggie.registerAndDiscover();
 
-// Default discoveries
+// Setup default discoveries
 if (type === 'device') {
 
     reggie.on('fog-up', function(fogId, connInfo) {
@@ -34,33 +35,36 @@ if (type === 'device') {
         console.log('CLOUD DOWN: id: ' + cloudId);
     });
 }
-// on rare occasions, you might get an error
-reggie.on('error', function(err) {
-    console.log("TESTER: Registrar threw an error...");    
-});
 
-// Custom attributes/discoveries
+// Setup custom attributes/discoveries
 if (type === 'device') {
-    let i = 0;
-    let f = (x) => {
-        reggie.setAttributes({ secret : Math.random().toString(16) });
-        ++i;
-        if(i < 5) {
-            setTimeout(f, 10000);
-        } else {
-            reggie.removeAttributes({secret : null});
+    const ticker = setInterval(
+                reggie.setAttributes,
+                5000,
+                { secret : Math.random().toString(16) }
+    );
+    setTimeout(
+        (attrs) => {
+            clearInterval(ticker);
+            reggie.removeAttributes(['secret']);
             reggie.quit();
-        }
-    }
-    f();
+        },
+        22000
+    );
 } else if (type === 'fog') {
     
     reggie.on('new-secret', function(id, secret) {
         console.log('NEW-SECRET: id: ' + id + ' secret: ' + secret);
-//        reggie.getDiscoveryTable((x) => console.log(JSON.stringify(x)));
     });
     reggie.on('no-more-secret', function(id) {
         console.log('NO-MORE-SECRET: id: ' + id);
     });
-    reggie.discoverAttributes({ device: { secret: { onAdd : 'new-secret', onRemove : 'no-more-secret' }}});
+    reggie.discoverAttributes({ 
+        device: {
+            secret: { 
+                onAdd : 'new-secret', 
+                onRemove : 'no-more-secret' 
+            }
+        }
+    });
 }    

From 93f6520a63f15774914474210f5de71ee2aab508 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 5 Jul 2018 12:50:09 -0400
Subject: [PATCH 56/62] JDISC: Fixed 'this' bind issue in apps/demo.js

---
 lib/jdiscovery/README.md    | 82 +++++++++++++++++++++++++++++++++++--
 lib/jdiscovery/apps/demo.js |  9 +++-
 2 files changed, 86 insertions(+), 5 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 61c74e47..af364d16 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -14,14 +14,88 @@ The following are automatically installed by the JAMScript setup scripts.
 ```
 /* Source in apps/demo.js */
 
+const   Registrar = require('../jregistrar-head.js'),
+        globals = require('../../jamserver/constants').globals,
+        events = require('events');
+
+const   id = process.argv[2],
+        type = process.argv[3],
+        port = process.argv[4],
+        app = 'tester';
+
+console.log('_______________________________________________');
+console.log(' id: ' + id + ' type: ' + type + ' port: ' + port);
+console.log('-----------------------------------------------');
+console.log();
+
+// Construct registrar and start it
+const   reggie = new Registrar(app, type, id, port,
+                           { protocols: { mqtt: false, mdns: true } });
+reggie.registerAndDiscover();
+
+// Setup default discoveries
+if (type === 'device') {
+
+    reggie.on('fog-up', function(fogId, connInfo) {
+        console.log('FOG UP: id: ' + fogId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+    });
+    reggie.on('fog-down', function(fogId) {
+        console.log('FOG DOWN: id: ' + fogId);
+    });
+} else if (type === 'fog') {
 
+    reggie.on('cloud-up', function(cloudId, connInfo) {
+        console.log('CLOUD UP: id: ' + cloudId + ', ip: ' + connInfo.ip + ', port: ' + connInfo.port);
+    });
+    reggie.on('cloud-down', function(cloudId) {
+        console.log('CLOUD DOWN: id: ' + cloudId);
+    });
+}
 
-
+// Setup custom attributes/discoveries
+if (type === 'device') {
+    const ticker = setInterval(
+                /**
+                 * XXX N.B. XXX
+                 * The call must be bound to this scope
+                 * using either a lambda expression,
+                 * or, a call to bind()
+                 * It will NOT work otherwise!
+                 */
+                (o) => { reggie.setAttributes(o); },
+                5000,
+                { secret : Math.random().toString(16) }
+    );
+    setTimeout(
+        (attrs) => {
+            clearInterval(ticker);
+            reggie.removeAttributes(['secret']);
+            reggie.quit();
+        },
+        22000
+    );
+} else if (type === 'fog') {
+    
+    reggie.on('new-secret', function(id, secret) {
+        console.log('NEW-SECRET: id: ' + id + ' secret: ' + secret);
+    });
+    reggie.on('no-more-secret', function(id) {
+        console.log('NO-MORE-SECRET: id: ' + id);
+    });
+    reggie.discoverAttributes({ 
+        device: {
+            secret: { 
+                onAdd : 'new-secret', 
+                onRemove : 'no-more-secret' 
+            }
+        }
+    });
+}    
 ```
 
 Open two terminals to the jdiscovery directory and run the following commands (one in each).
-- `node apps/tester.js node-0 device 42420`
-- `node apps/tester.js node-1 fog 42421`
+- `node apps/demo.js node-0 device 42420`
+- `node apps/demo.js node-1 fog 42421`
 
 ## API
 
@@ -46,7 +120,7 @@ const reggie = new Registrar(app, type, id, port, config);
 ```
 
 ### reggie.registerAndDiscover([options]);
-Kick-starts registration (announcing of attributes) and discovery. **This method must be called for the registrar to start functioning; it is best to call it once all interests have been registered and base attributes set.** See the demo code above for an example.
+Kick-starts registration (announcing of attributes) and discovery. **This method must be called for the registrar to start functioning; it is best to call it right after calling the constructor.** See the demo code above for an example.
 
 `options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.setAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
 - `attrsToAdd`: an object of the same form as that accepted by `reggie.setAttributes`
diff --git a/lib/jdiscovery/apps/demo.js b/lib/jdiscovery/apps/demo.js
index 0471a856..25a69c20 100644
--- a/lib/jdiscovery/apps/demo.js
+++ b/lib/jdiscovery/apps/demo.js
@@ -39,7 +39,14 @@ if (type === 'device') {
 // Setup custom attributes/discoveries
 if (type === 'device') {
     const ticker = setInterval(
-                reggie.setAttributes,
+                /**
+                 * XXX N.B. XXX
+                 * The call must be bound to this scope
+                 * using either a lambda expression,
+                 * or, a call to bind()
+                 * It will NOT work otherwise!
+                 */
+                (o) => { reggie.setAttributes(o); },
                 5000,
                 { secret : Math.random().toString(16) }
     );

From 00ffa2b2b0a3a0bf97172441f38862de3fa9bc00 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 5 Jul 2018 12:57:09 -0400
Subject: [PATCH 57/62] JDISC: Enabled mqtt in apps/demo.js, fixed docs

---
 lib/jdiscovery/README.md    | 16 ++++++++++------
 lib/jdiscovery/apps/demo.js |  6 +++++-
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index af364d16..814424e3 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -30,7 +30,11 @@ console.log();
 
 // Construct registrar and start it
 const   reggie = new Registrar(app, type, id, port,
-                           { protocols: { mqtt: false, mdns: true } });
+                           { protocols: { mqtt: true, mdns: true } });
+// ... if the mqtt discovery option is enabled, you need to have a broker
+// ... running at the address specified in JAMScript-beta/lib/jamserver/constants.js
+// ... at constants.mqtt.brokerUrl
+
 reggie.registerAndDiscover();
 
 // Setup default discoveries
@@ -104,11 +108,11 @@ Open two terminals to the jdiscovery directory and run the following commands (o
 const Registrar = require('jdiscovery');
 const reggie = new Registrar(app, type, id, port, config);
 ```
--`app`: Application name
--`type`: Node type ('device', 'fog', 'cloud')
--`id`: Node identifier (must be unique w.r.t. application name)
--`port`: Port number where the application can be reached
--`config`: Object with property `protocols` for regirstrar setup (as is for flexibility)
+- `app`: Application name
+- `type`: Node type ('device', 'fog', 'cloud')
+- `id`: Node identifier (must be unique w.r.t. application name)
+- `port`: Port number where the application can be reached
+- `config`: Object with property `protocols` for regirstrar setup (as is for flexibility)
 ```
     const config =
     {
diff --git a/lib/jdiscovery/apps/demo.js b/lib/jdiscovery/apps/demo.js
index 25a69c20..3b801f74 100644
--- a/lib/jdiscovery/apps/demo.js
+++ b/lib/jdiscovery/apps/demo.js
@@ -14,7 +14,11 @@ console.log();
 
 // Construct registrar and start it
 const   reggie = new Registrar(app, type, id, port,
-                           { protocols: { mqtt: false, mdns: true } });
+                           { protocols: { mqtt: true, mdns: true } });
+// ... if the mqtt discovery option is enabled, you need to have a broker
+// ... running at the address specified in JAMScript-beta/lib/jamserver/constants.js
+// ... at constants.mqtt.brokerUrl
+
 reggie.registerAndDiscover();
 
 // Setup default discoveries

From 5794aa3e156b427cfd65cbe194a4ca3099da4a57 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 5 Jul 2018 13:28:22 -0400
Subject: [PATCH 58/62] JDISC: Added child kill check, commented out debug
 loggers

---
 lib/jdiscovery/jregistrar-abdomen.js |  6 +++---
 lib/jdiscovery/jregistrar-head.js    |  5 +++++
 lib/jdiscovery/jregistrar-tail.js    |  4 ++--
 lib/jdiscovery/mdnsregistry.js       | 16 ++++++++--------
 4 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/lib/jdiscovery/jregistrar-abdomen.js b/lib/jdiscovery/jregistrar-abdomen.js
index 74283705..9d9c5463 100644
--- a/lib/jdiscovery/jregistrar-abdomen.js
+++ b/lib/jdiscovery/jregistrar-abdomen.js
@@ -14,9 +14,9 @@ this.registrarTail.on('discoveryTable', (dt) => {
 });
 
 process.on('message', (m) => {
-    // XXX
-    console.log('Received call to: ');
-    console.log(m);
+    // DEBUG
+    // console.log('DEBUG: jregistrar-abdomen received call: ', m);
+
     for(f in m)
         if(m.hasOwnProperty(f))
             this.registrarTail[f](m[f]);
diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
index 599f8545..f9642a5d 100644
--- a/lib/jdiscovery/jregistrar-head.js
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -52,6 +52,11 @@ RegistrarHead.prototype.stopDiscoveringAttributes = function(dattrs) {
 
 RegistrarHead.prototype.quit = function() {
     this.registrarAbdomen.send({ quit : null });
+    setTimeout(
+        (s) => { this.registrarAbdomen.kill(s); },
+        3000,
+        'SIGTERM'
+    );
 }
 
 RegistrarHead.prototype.getDiscoveryTable = function(handler) {
diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index 2fc57a04..6bbd1630 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -305,7 +305,7 @@ RegistrarTail.prototype.getDiscoveryTable = function() {
 RegistrarTail.prototype._respondToDiscoveryEvent = function(attr, event, id, data, seqval, protocol) {
 
     // DUBUGGING: Print shit
-    console.log("DEBUG: Registrar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
+    // console.log("DEBUG: Registrar._respondToDiscoveryEvent: ", event, id, data, seqval, protocol);
 
     // Update discovery table
     if (!this.dt.hasOwnProperty(id)) {
@@ -366,7 +366,7 @@ RegistrarTail.prototype._respondToDiscoveryEvent = function(attr, event, id, dat
 RegistrarTail.prototype._respondToRemovalEvent = function(attr, event, id, seqval, protocol) {
 
     // DUBUGGING: Print shit
-    console.log("DEBUG: Registar._respondToRemovalEvent: ", event, id, seqval, protocol);
+    // console.log("DEBUG: Registar._respondToRemovalEvent: ", event, id, seqval, protocol);
 
     // check dt first
     if (  !this.dt.hasOwnProperty(id)
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 54c6620f..9b0f6cbb 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -100,9 +100,9 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                 bonjour.find({type : this.app + '-' + (x) + '-attrrem'});
             this.attrRemovalBrowsers[x].on('up', function(service) {
                 // DEBUG
-                console.log('DEBUG: MDNSRegistry.attrRemovalBrowser[' + x + ']');
-                console.log(JSON.stringify(service));
-                console.log(JSON.stringify(self.attrsToBrowse));
+                // console.log('DEBUG: MDNSRegistry.attrRemovalBrowser[' + x + ']');
+                // console.log(JSON.stringify(service));
+                // console.log(JSON.stringify(self.attrsToBrowse));
 
                 // ignore our own services
                 if (service.name === self.id) {
@@ -118,7 +118,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
                 ).map(
                     (attr) => {
                         // DEBUG
-                        console.log('TYPE: ' + x + ' ATTR: ' + attr);
+                        // console.log('TYPE: ' + x + ' ATTR: ' + attr);
 
                         // propagate advertised attribute removals
                         if(attr === 'status') {
@@ -154,7 +154,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
  */
 MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
     // DEBUG
-    console.log('DEBUG: MDNSRegistry.setAttributes');
+    // console.log('DEBUG: MDNSRegistry.setAttributes');
 
     if (this.started) {
         this._createAdvertisements(attrs, seqval);
@@ -170,7 +170,7 @@ MDNSRegistry.prototype.setAttributes = function(attrs, seqval) {
  */
 MDNSRegistry.prototype.removeAttributes = function(attrs, seqval) {
     // DEBUG
-    console.log('DEBUG: MDNSRegistry.removeAttributes');
+    // console.log('DEBUG: MDNSRegistry.removeAttributes');
 
     // clean & create attrrem advertisement
     if (this.attrRemovalAd) {
@@ -282,7 +282,7 @@ MDNSRegistry.prototype.quit = function(seqval) {
 MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
 
     // DEBUG
-    console.log('DEBUG: MDNSRegistry._createAdvertisements');
+    // console.log('DEBUG: MDNSRegistry._createAdvertisements');
 
     // stop old advertisements and create new ones for updated attrs
     for (var attr in attrs) {
@@ -319,7 +319,7 @@ MDNSRegistry.prototype._createBrowser = function(attr, type) {
     var self = this;
     browser.on('up', function(service) {
         // DEBUG
-        console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service up");
+        // console.log("DEBUG: MDNSRegistry: |" + type + "/" + attr + "| browser: service up");
 
         // ignore our own services
         if (service.name === self.id) {

From e9d9bb342c2bd2aeaf364f39fef8becf61627506 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Thu, 5 Jul 2018 13:35:58 -0400
Subject: [PATCH 59/62] JDISC: Fixed inconsistency in README.md

---
 lib/jdiscovery/README.md | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index 814424e3..b5cef1aa 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -143,7 +143,15 @@ Both attribute names and values **should be kept brief**. The Registrar uses MQT
     });
     // If you want to keep the rest of the network up to date on your temperature, you can simply set an interval
     // ... update the temperature every five seconds
-    setInterval(reggie.setAttributes, 
+    setInterval(
+                /**
+                 * XXX N.B. XXX
+                 * The call must be bound to this scope
+                 * using either a lambda expression,
+                 * or, a call to bind()
+                 * It will NOT work otherwise!
+                 */
+                (o) => { reggie.setAttributes(o); }, 
                 5000, 
                 { thermostat: _getThermostatTemp() }
     );

From 6e1b9d52fab13339f492d3551e241a5b1cde230a Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 6 Jul 2018 11:28:52 -0400
Subject: [PATCH 60/62] JDISC: Fixed mdns name already exists timing error,
 added easier value updates for setAttr

---
 lib/jdiscovery/README.md          |  8 ++++----
 lib/jdiscovery/apps/demo.js       |  4 ++--
 lib/jdiscovery/jregistrar-head.js | 15 ++++++++++++++-
 lib/jdiscovery/mdnsregistry.js    | 11 +++++++++++
 4 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index b5cef1aa..aea16f71 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -68,10 +68,10 @@ if (type === 'device') {
                  */
                 (o) => { reggie.setAttributes(o); },
                 5000,
-                { secret : Math.random().toString(16) }
+                { secret : () => Math.random().toString(16) }
     );
     setTimeout(
-        (attrs) => {
+        () => {
             clearInterval(ticker);
             reggie.removeAttributes(['secret']);
             reggie.quit();
@@ -133,7 +133,7 @@ Kick-starts registration (announcing of attributes) and discovery. **This method
 ### reggie.setAttributes(attrs);
 Sets the specified attributes to the node. Calling this method multiple times updates the attribute value being shared with the network.
 
-`attrs` is an object of `<attr, value>` pairs. The `attr` is the name of the attribute and is limited to valid JavaScript object keys. The attribute `value` can be any basic data type, `null`, or a JavaScript object.
+`attrs` is an object of `<attr, value>` pairs. The `attr` is the name of the attribute and is limited to valid JavaScript object keys. The attribute `value` can be any basic data type, `null`, a JavaScript object, or a function returning one of the previous. Note that functions should be wrapped in a lambda expression as in the example below to avoid binding issues with `this`.
 
 Both attribute names and values **should be kept brief**. The Registrar uses MQTT and mDNS under the hood, which are both lightweight messaging protocols. You may run into some trouble if you try to send too much information! **Remember: This module is should be used for basic discovery only. If nodes need to exchange larger chunks of information, then a separate connection should be made**.
 ```
@@ -153,7 +153,7 @@ Both attribute names and values **should be kept brief**. The Registrar uses MQT
                  */
                 (o) => { reggie.setAttributes(o); }, 
                 5000, 
-                { thermostat: _getThermostatTemp() }
+                { thermostat: () => _getThermostatTemp() }
     );
 ```
 
diff --git a/lib/jdiscovery/apps/demo.js b/lib/jdiscovery/apps/demo.js
index 3b801f74..3b892572 100644
--- a/lib/jdiscovery/apps/demo.js
+++ b/lib/jdiscovery/apps/demo.js
@@ -52,10 +52,10 @@ if (type === 'device') {
                  */
                 (o) => { reggie.setAttributes(o); },
                 5000,
-                { secret : Math.random().toString(16) }
+                { secret : () => Math.random().toString(16) }
     );
     setTimeout(
-        (attrs) => {
+        () => {
             clearInterval(ticker);
             reggie.removeAttributes(['secret']);
             reggie.quit();
diff --git a/lib/jdiscovery/jregistrar-head.js b/lib/jdiscovery/jregistrar-head.js
index f9642a5d..46637efb 100644
--- a/lib/jdiscovery/jregistrar-head.js
+++ b/lib/jdiscovery/jregistrar-head.js
@@ -37,7 +37,20 @@ RegistrarHead.prototype.registerAndDiscover = function(options) {
 }
 
 RegistrarHead.prototype.setAttributes = function(attrs) {
-    this.registrarAbdomen.send({ setAttributes : attrs });
+    // N.B. Gotta clone attrs to avoid some crazy JS shit...
+    // Evaluate attr function to get value 
+    // ... right before sending over to other process
+    let clone = {};
+    for(const key in attrs) {
+        if(attrs.hasOwnProperty(key)) {
+            if(typeof attrs[key] === 'function') {
+                clone[key] = attrs[key]();
+            } else {
+                clone[key] = attrs[key];
+            }
+        }
+    }
+    this.registrarAbdomen.send({ setAttributes : clone });
 }
 RegistrarHead.prototype.removeAttributes = function(attrs) {
     this.registrarAbdomen.send({ removeAttributes : attrs });
diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 9b0f6cbb..23036ebc 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -304,6 +304,17 @@ MDNSRegistry.prototype._createAdvertisements = function(attrs, seqval) {
                                 }
                 }
             );
+            // if we get a service already on network error, wait a little, then retry
+            // .. it seems like it takes a little time for the mdns server to clear its entry
+            this.ads[attr].on('error',
+                () => {
+                    setTimeout(
+                        () => { this.ads[attr].start(); },
+                        2000
+                    );
+                }
+            );
+            // attempt start :)
             this.ads[attr].start();
         }
     }

From 08a3120d8a01b0a1bbde5c55f31bf8409fc0b743 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 6 Jul 2018 11:39:58 -0400
Subject: [PATCH 61/62] JDISC: Added multi-app support on single LAN

---
 lib/jdiscovery/mdnsregistry.js | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/lib/jdiscovery/mdnsregistry.js b/lib/jdiscovery/mdnsregistry.js
index 23036ebc..c882d350 100644
--- a/lib/jdiscovery/mdnsregistry.js
+++ b/lib/jdiscovery/mdnsregistry.js
@@ -59,8 +59,11 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
     this.hbSocket = dgram.createSocket({ type: 'udp4', reuseAddr: true });
     // ...  handle incoming heartbeats: if entry in upTable, renew entry TTL
     this.hbSocket.on('message', function (message, remote) {
-        // message is just the node id
-        let id = message.toString();
+        let o = JSON.parse(message.toString());
+        // check if the message is for the same app
+        if(o.app !== self.app)
+            return;
+        let id = o.id;
         // remote has properties: address, port
         if(self.upTable.hasOwnProperty(id)) {
             clearTimeout(self.upTable[id].timer);
@@ -87,7 +90,7 @@ MDNSRegistry.prototype.registerAndDiscover = function() {
     // ... start periodic hb emission for local node
     setInterval(
         () => {
-            let m = new Buffer.from(self.id);
+            let m = new Buffer.from(JSON.stringify({app: self.app, id: self.id}));
             self.hbSocket.send(m, 0, m.length, self.hbPort, self.hbMulticastAddress);
         },
         Math.floor(self.hbTTL/3)

From 327398ed5f0439fe38c251a0c4664bc475abf854 Mon Sep 17 00:00:00 2001
From: Michael Saraga <mic.saraga@gmail.com>
Date: Fri, 6 Jul 2018 11:46:17 -0400
Subject: [PATCH 62/62] JDISC: Removed [options] arg from registerAndDiscover
 (redundant and unused)

---
 lib/jdiscovery/README.md          |  6 +-----
 lib/jdiscovery/jregistrar-tail.js | 21 +--------------------
 2 files changed, 2 insertions(+), 25 deletions(-)

diff --git a/lib/jdiscovery/README.md b/lib/jdiscovery/README.md
index aea16f71..39f639ca 100644
--- a/lib/jdiscovery/README.md
+++ b/lib/jdiscovery/README.md
@@ -123,13 +123,9 @@ const reggie = new Registrar(app, type, id, port, config);
     }
 ```
 
-### reggie.registerAndDiscover([options]);
+### reggie.registerAndDiscover();
 Kick-starts registration (announcing of attributes) and discovery. **This method must be called for the registrar to start functioning; it is best to call it right after calling the constructor.** See the demo code above for an example.
 
-`options` is an optional way to specify attributes of the node and those it should discover, rather than using `reggie.setAttributes` and `reggie.discoverAttributes`. It is an object that accepts the following `<key, value>` pairs:
-- `attrsToAdd`: an object of the same form as that accepted by `reggie.setAttributes`
-- `attrsToDiscover`: an object of the same form as that accepted by `reggie.discoverAttributes`
-
 ### reggie.setAttributes(attrs);
 Sets the specified attributes to the node. Calling this method multiple times updates the attribute value being shared with the network.
 
diff --git a/lib/jdiscovery/jregistrar-tail.js b/lib/jdiscovery/jregistrar-tail.js
index 6bbd1630..f4bee5ef 100644
--- a/lib/jdiscovery/jregistrar-tail.js
+++ b/lib/jdiscovery/jregistrar-tail.js
@@ -98,7 +98,7 @@ RegistrarTail.prototype.constructor = RegistrarTail;
  *   attrsToSet: key/value pair as in this.setAttributes
  *   attrsToDiscover: as in this.discoverAttributes
  */
-RegistrarTail.prototype.registerAndDiscover = function(options) {
+RegistrarTail.prototype.registerAndDiscover = function() {
 
     if (this.started)
         return;
@@ -169,25 +169,6 @@ RegistrarTail.prototype.registerAndDiscover = function(options) {
         });
     });
 
-    /**
-     * Handle passed `options`
-     * `options` include:
-     *   attrsToSet: key/value pair as in this.setAttributes
-     *   attrsToDiscover: as in this.discoverAttributes
-     */
-    if (options) {
-        if (typeof options !== 'object') {
-            throw new Error('options must be an object - see the docs');
-        }
-
-        if (options.attrsToSet) {
-            this.setAttributes(options.attrsToAdd);
-        }
-        if (options.attrsToDiscover) {
-            this.discoverAttributes(options.attrsToDiscover);
-        }
-    }
-
     [this.mqttRegistry, this.mdnsRegistry].map(
         x => {if(x) x.registerAndDiscover();}
     );